From e7fe537626fc74553605395a052b2c9d4adc1cad Mon Sep 17 00:00:00 2001 From: Sarath Lakshman Date: Thu, 7 Apr 2011 14:02:57 +0530 Subject: [PATCH 0001/2237] Add `create_tree` method for Index class Invoke tree = index.create_tree() to create a tree from index file and return Tree object. --- pygit2.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pygit2.c b/pygit2.c index 00fdd024e..7ca9b02bc 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1435,6 +1435,34 @@ Index_setitem(Index *self, PyObject *key, PyObject *value) { return 0; } +static PyObject * +Index_create_tree(Index *self) { + git_oid oid; + int err; + Tree *py_tree; + git_tree *tree; + + err = git_tree_create_fromindex(&oid, self->index); + if (err < 0) { + return Error_set(err); + } + + err = git_tree_lookup(&tree, self->repo->repo, &oid); + if (err < 0) { + return Error_set(err); + } + + py_tree = PyObject_New(Tree, &TreeType); + if (!py_tree) + return NULL; + + Py_INCREF(self->repo); + py_tree->repo = self->repo; + py_tree->tree = (git_tree*)tree; + + return (PyObject*)py_tree; +} + static PyMethodDef Index_methods[] = { {"add", (PyCFunction)Index_add, METH_VARARGS, "Add or update an index entry from a file in disk."}, @@ -1449,6 +1477,8 @@ static PyMethodDef Index_methods[] = { {"write", (PyCFunction)Index_write, METH_NOARGS, "Write an existing index object from memory back to disk using an" " atomic file lock."}, + {"create_tree", (PyCFunction)Index_create_tree, METH_NOARGS, + "Create a tree from the index entries"}, {NULL} }; From 014388c995434d386292ffba012aa121e315c976 Mon Sep 17 00:00:00 2001 From: Sarath Lakshman Date: Thu, 7 Apr 2011 14:07:48 +0530 Subject: [PATCH 0002/2237] Add unit test for method create_tree() from Index class --- test/test_index.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/test_index.py b/test/test_index.py index 38c8b172a..b2db4e125 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -88,6 +88,13 @@ def test_write(self): index.read() self.assertTrue('bye.txt' in index) + def test_create_tree(self): + sha = 'fd937514cb799514d4b81bb24c5fcfeb6472b245' + index = self.repo.index + index.read() + tree = index.create_tree() + self.assertEqual(sha, tree.sha) + if __name__ == '__main__': unittest.main() From 8491568dd006f9355527f4a1f3ae1d44a70931e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 7 Apr 2011 16:31:21 +0200 Subject: [PATCH 0003/2237] Fix compilation warnings --- pygit2.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pygit2.c b/pygit2.c index 00fdd024e..42e0dde28 100644 --- a/pygit2.c +++ b/pygit2.c @@ -59,7 +59,7 @@ typedef struct { typedef struct { PyObject_HEAD - git_tree_entry *entry; + const git_tree_entry *entry; Tree *tree; } TreeEntry; @@ -903,7 +903,7 @@ Tree_contains(Tree *self, PyObject *py_name) { } static TreeEntry * -wrap_tree_entry(git_tree_entry *entry, Tree *tree) { +wrap_tree_entry(const git_tree_entry *entry, Tree *tree) { TreeEntry *py_entry = NULL; py_entry = (TreeEntry*)TreeEntryType.tp_alloc(&TreeEntryType, 0); if (!py_entry) @@ -918,7 +918,8 @@ wrap_tree_entry(git_tree_entry *entry, Tree *tree) { static TreeEntry * Tree_getitem_by_name(Tree *self, PyObject *py_name) { char *name; - git_tree_entry *entry; + const git_tree_entry *entry; + name = PyString_AS_STRING(py_name); entry = git_tree_entry_byname(self->tree, name); if (!entry) { @@ -958,7 +959,7 @@ Tree_fix_index(Tree *self, PyObject *py_index) { static TreeEntry * Tree_getitem_by_index(Tree *self, PyObject *py_index) { int index; - git_tree_entry *entry; + const git_tree_entry *entry; index = Tree_fix_index(self, py_index); if (PyErr_Occurred()) From 7947c5396cf0298106d988a3ea761e05286cc246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 8 Apr 2011 16:35:20 +0200 Subject: [PATCH 0004/2237] tests: remove unneeded calls to Index.read Since libgit2's commit 3bdc0d4c8c3a5 the index file is automatically read when opened. --- test/test_index.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/test_index.py b/test/test_index.py index b2db4e125..99c025fe7 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -61,7 +61,6 @@ def test_read(self): def test_add(self): index = self.repo.index - index.read() sha = '0907563af06c7464d62a70cdd135a6ba7d2b41d8' self.assertFalse('bye.txt' in index) @@ -72,14 +71,12 @@ def test_add(self): def test_clear(self): index = self.repo.index - index.read() self.assertEqual(len(index), 2) index.clear() self.assertEqual(len(index), 0) def test_write(self): index = self.repo.index - index.read() index.add('bye.txt', 0) index.write() @@ -91,7 +88,6 @@ def test_write(self): def test_create_tree(self): sha = 'fd937514cb799514d4b81bb24c5fcfeb6472b245' index = self.repo.index - index.read() tree = index.create_tree() self.assertEqual(sha, tree.sha) From 43ea66b738962c3faecd1e61bf43c480dc8840b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 8 Apr 2011 17:03:41 +0200 Subject: [PATCH 0005/2237] Add support for the time offset of the signature --- pygit2.c | 9 ++++----- test/test_commit.py | 11 ++++++----- test/test_tag.py | 7 ++++--- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/pygit2.c b/pygit2.c index 8ad9d99be..7c3f49e38 100644 --- a/pygit2.c +++ b/pygit2.c @@ -556,21 +556,20 @@ static PyTypeObject ObjectType = { 0, /* tp_new */ }; -/* TODO Add support for the time offset */ static PyObject * build_person(const git_signature *signature) { - return Py_BuildValue("(ssL)", signature->name, signature->email, - signature->when.time); + return Py_BuildValue("(ssLi)", signature->name, signature->email, + signature->when.time, signature->when.offset); } static int signature_converter(PyObject *value, git_signature **out) { char *name, *email; long long time; - int offset = 0; + int offset; git_signature *signature; - if (!PyArg_ParseTuple(value, "ssL", &name, &email, &time)) + if (!PyArg_ParseTuple(value, "ssLi", &name, &email, &time, &offset)) return 0; signature = git_signature_new(name, email, time, offset); diff --git a/test/test_commit.py b/test/test_commit.py index 74a4220cc..6aea2a67e 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -53,17 +53,18 @@ def test_read_commit(self): commit_time = 1288481576 self.assertEqual(commit_time, commit.commit_time) self.assertEqual( - ('Dave Borowitz', 'dborowitz@google.com', commit_time), + ('Dave Borowitz', 'dborowitz@google.com', commit_time, -420), commit.committer) - self.assertEqual(('Dave Borowitz', 'dborowitz@google.com', 1288477363), - commit.author) + self.assertEqual( + ('Dave Borowitz', 'dborowitz@google.com', 1288477363, -420), + commit.author) self.assertEqual( '967fce8df97cc71722d3c2a5930ef3e6f1d27b12', commit.tree.sha) def test_new_commit(self): message = 'New commit.\n\nMessage.\n' - committer = ('John Doe', 'jdoe@example.com', 12346) - author = ('Jane Doe', 'jdoe2@example.com', 12345) + committer = ('John Doe', 'jdoe@example.com', 12346, 0) + author = ('Jane Doe', 'jdoe2@example.com', 12345, 0) tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' parents = [COMMIT_SHA] diff --git a/test/test_tag.py b/test/test_tag.py index c3afdb7a0..f802b05c3 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -45,8 +45,9 @@ def test_read_tag(self): self.assertEqual(pygit2.GIT_OBJ_TAG, tag.type) self.assertEqual(pygit2.GIT_OBJ_COMMIT, tag.target.type) self.assertEqual('root', tag.name) - self.assertEqual(('Dave Borowitz', 'dborowitz@google.com', 1288724692), - tag.tagger) + self.assertEqual( + ('Dave Borowitz', 'dborowitz@google.com', 1288724692, -420), + tag.tagger) self.assertEqual('Tagged root commit.\n', tag.message) commit = tag.target @@ -57,7 +58,7 @@ def test_new_tag(self): name = 'thetag' target = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' message = 'Tag a blob.\n' - tagger = ('John Doe', 'jdoe@example.com', 12347) + tagger = ('John Doe', 'jdoe@example.com', 12347, 0) tag = pygit2.Tag(self.repo, name, target, pygit2.GIT_OBJ_BLOB, tagger, message) From 32acbc0f713ff80412edd644174f5777e9cb2a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 8 Apr 2011 18:37:32 +0200 Subject: [PATCH 0006/2237] Set Python error when Commit_init fails --- pygit2.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pygit2.c b/pygit2.c index 7c3f49e38..51739e8d2 100644 --- a/pygit2.c +++ b/pygit2.c @@ -638,12 +638,16 @@ Commit_init(Commit *py_commit, PyObject *args, PyObject *kwds) { err = git_commit_create(&oid, repo->repo, NULL, author, committer, message, &tree_oid, parent_count, (const git_oid**)parents); - if (err < 0) + if (err < 0) { + Error_set(err); return free_parents(parents, parent_count); + } err = git_commit_lookup(&commit, repo->repo, &oid); - if (err < 0) + if (err < 0) { + Error_set(err); return free_parents(parents, parent_count); + } Py_INCREF(repo); py_commit->repo = repo; From 6f2b864619fc62e95a78df87d82135a189826f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 11 Apr 2011 18:44:49 +0200 Subject: [PATCH 0007/2237] Change API to create Git objects Do not allow to create commits, trees, blobs and tags directly from the constructor. For instance, now calling "Commit(...)" raises an error. Instead use the create methods of the repository object: Before Now ----------------------------- ----------------------------- commit = Commit(repo, ...) sha = repo.create_commit(...) tag = Tag(repo, ...) sha = repo.create_tag(...) Most often you won't need to get the object just created, but if you do just call "repo[sha]" afterwards. (Methods to create blobs and trees are still missing, just like before.) Similarly the method that creates a tree object from the index file does not return the tree object anymore, but just its SHA: Before Now ----------------------------- ----------------------------- tree = index.create_tree() sha = index.create_tree() --- pygit2.c | 293 +++++++++++++++++++------------------------- test/test_commit.py | 6 +- test/test_index.py | 6 +- test/test_tag.py | 5 +- 4 files changed, 135 insertions(+), 175 deletions(-) diff --git a/pygit2.c b/pygit2.c index 51739e8d2..6efea74b9 100644 --- a/pygit2.c +++ b/pygit2.c @@ -377,7 +377,119 @@ Repository_walk(Repository *self, PyObject *args) return (PyObject*)py_walker; } +static PyObject * +build_person(const git_signature *signature) { + return Py_BuildValue("(ssLi)", signature->name, signature->email, + signature->when.time, signature->when.offset); +} + +static int +signature_converter(PyObject *value, git_signature **out) { + char *name, *email; + long long time; + int offset; + git_signature *signature; + + if (!PyArg_ParseTuple(value, "ssLi", &name, &email, &time, &offset)) + return 0; + + signature = git_signature_new(name, email, time, offset); + if (signature == NULL) { + PyErr_SetNone(PyExc_MemoryError); + return 0; + } + + *out = signature; + return 1; +} + +static PyObject * +free_parents(git_oid **parents, int n) { + int i; + + for (i = 0; i < n; i++) + free(parents[i]); + free(parents); + return NULL; +} + +static PyObject * +Repository_create_commit(Repository *self, PyObject *args) { + git_signature *author, *committer; + char *message; + git_oid tree_oid, oid; + PyObject *py_parents, *py_parent; + int parent_count; + git_oid **parents; + int err, i; + char hex[GIT_OID_HEXSZ]; + + if (!PyArg_ParseTuple(args, "O&O&sO&O!", + signature_converter, &author, + signature_converter, &committer, + &message, + py_str_to_git_oid, &tree_oid, + &PyList_Type, &py_parents)) + return NULL; + + parent_count = (int)PyList_Size(py_parents); + parents = malloc(parent_count * sizeof(git_oid*)); + if (parents == NULL) { + PyErr_SetNone(PyExc_MemoryError); + return NULL; + } + for (i = 0; i < parent_count; i++) { + parents[i] = malloc(sizeof(git_oid)); + if (parents[i] == NULL) { + PyErr_SetNone(PyExc_MemoryError); + return free_parents(parents, i); + } + py_parent = PyList_GET_ITEM(py_parents, i); + if (!py_str_to_git_oid(py_parent, parents[i])) + return free_parents(parents, i); + } + + err = git_commit_create(&oid, self->repo, NULL, + author, committer, message, &tree_oid, + parent_count, (const git_oid**)parents); + free_parents(parents, parent_count); + if (err < 0) + return Error_set(err); + + git_oid_fmt(hex, &oid); + return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); +} + +static PyObject * +Repository_create_tag(Repository *self, PyObject *args) { + char *tag_name, *message; + git_signature *tagger; + git_oid target, oid; + int err, target_type; + char hex[GIT_OID_HEXSZ]; + + if (!PyArg_ParseTuple(args, "sO&iO&s", + &tag_name, + py_str_to_git_oid, &target, + &target_type, + signature_converter, &tagger, + &message)) + return NULL; + + err = git_tag_create(&oid, self->repo, + tag_name, &target, target_type, tagger, message); + if (err < 0) + return NULL; + + git_oid_fmt(hex, &oid); + return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); +} + static PyMethodDef Repository_methods[] = { + {"create_commit", (PyCFunction)Repository_create_commit, METH_VARARGS, + "Create a new commit object, return its SHA."}, + {"create_tag", (PyCFunction)Repository_create_tag, METH_VARARGS, + "Create a new tag object, return its SHA."}, {"walk", (PyCFunction)Repository_walk, METH_VARARGS, "Generator that traverses the history starting from the given commit."}, {"read", (PyCFunction)Repository_read, METH_O, @@ -556,106 +668,6 @@ static PyTypeObject ObjectType = { 0, /* tp_new */ }; -static PyObject * -build_person(const git_signature *signature) { - return Py_BuildValue("(ssLi)", signature->name, signature->email, - signature->when.time, signature->when.offset); -} - -static int -signature_converter(PyObject *value, git_signature **out) { - char *name, *email; - long long time; - int offset; - git_signature *signature; - - if (!PyArg_ParseTuple(value, "ssLi", &name, &email, &time, &offset)) - return 0; - - signature = git_signature_new(name, email, time, offset); - if (signature == NULL) { - PyErr_SetNone(PyExc_MemoryError); - return 0; - } - - *out = signature; - return 1; -} - -static int -free_parents(git_oid **parents, int n) { - int i; - - for (i = 0; i < n; i++) - free(parents[i]); - free(parents); - return -1; -} - -static int -Commit_init(Commit *py_commit, PyObject *args, PyObject *kwds) { - Repository *repo = NULL; - git_signature *author, *committer; - char *message; - git_oid tree_oid, oid; - PyObject *py_parents, *py_parent; - int parent_count; - git_oid **parents; - git_commit *commit; - int err, i; - - if (kwds) { - PyErr_Format(PyExc_TypeError, "%s takes no keyword arugments", - py_commit->ob_type->tp_name); - return -1; - } - - if (!PyArg_ParseTuple(args, "O!O&O&sO&O!", &RepositoryType, &repo, - signature_converter, &author, - signature_converter, &committer, - &message, - py_str_to_git_oid, &tree_oid, - &PyList_Type, &py_parents)) - return -1; - - parent_count = (int)PyList_Size(py_parents); - parents = malloc(parent_count * sizeof(git_oid*)); - if (parents == NULL) { - PyErr_SetNone(PyExc_MemoryError); - return -1; - } - for (i = 0; i < parent_count; i++) { - parents[i] = malloc(sizeof(git_oid)); - if (parents[i] == NULL) { - PyErr_SetNone(PyExc_MemoryError); - return free_parents(parents, i); - } - py_parent = PyList_GET_ITEM(py_parents, i); - if (!py_str_to_git_oid(py_parent, parents[i])) - return free_parents(parents, i); - } - - err = git_commit_create(&oid, repo->repo, NULL, - author, committer, message, &tree_oid, - parent_count, (const git_oid**)parents); - if (err < 0) { - Error_set(err); - return free_parents(parents, parent_count); - } - - err = git_commit_lookup(&commit, repo->repo, &oid); - if (err < 0) { - Error_set(err); - return free_parents(parents, parent_count); - } - - Py_INCREF(repo); - py_commit->repo = repo; - py_commit->commit = commit; - free_parents(parents, parent_count); - return 0; -} - static PyObject * Commit_get_message_short(Commit *commit) { return PyString_FromString(git_commit_message_short(commit->commit)); @@ -790,7 +802,7 @@ static PyTypeObject CommitType = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)Commit_init, /* tp_init */ + 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; @@ -1096,44 +1108,6 @@ static PyTypeObject BlobType = { 0, /* tp_new */ }; -static int -Tag_init(Tag *py_tag, PyObject *args, PyObject *kwds) { - Repository *repo = NULL; - char *tag_name, *message; - git_signature *tagger; - git_oid target, oid; - git_tag *tag; - int err, target_type; - - if (kwds) { - PyErr_Format(PyExc_TypeError, "%s takes no keyword arugments", - py_tag->ob_type->tp_name); - return -1; - } - - if (!PyArg_ParseTuple(args, "O!sO&iO&s", &RepositoryType, &repo, - &tag_name, - py_str_to_git_oid, &target, - &target_type, - signature_converter, &tagger, - &message)) - return -1; - - err = git_tag_create(&oid, repo->repo, - tag_name, &target, target_type, tagger, message); - if (err < 0) - return -1; - - err = git_tag_lookup(&tag, repo->repo, &oid); - if (err < 0) - return -1; - - Py_INCREF(repo); - py_tag->repo = repo; - py_tag->tag = tag; - return 0; -} - static void Tag_dealloc(Tag *self) { Py_XDECREF(self->target); @@ -1234,7 +1208,7 @@ static PyTypeObject TagType = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)Tag_init, /* tp_init */ + 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; @@ -1443,28 +1417,14 @@ static PyObject * Index_create_tree(Index *self) { git_oid oid; int err; - Tree *py_tree; - git_tree *tree; + char hex[GIT_OID_HEXSZ]; err = git_tree_create_fromindex(&oid, self->index); - if (err < 0) { - return Error_set(err); - } - - err = git_tree_lookup(&tree, self->repo->repo, &oid); - if (err < 0) { + if (err < 0) return Error_set(err); - } - - py_tree = PyObject_New(Tree, &TreeType); - if (!py_tree) - return NULL; - - Py_INCREF(self->repo); - py_tree->repo = self->repo; - py_tree->tree = (git_tree*)tree; - return (PyObject*)py_tree; + git_oid_fmt(hex, &oid); + return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); } static PyMethodDef Index_methods[] = { @@ -1482,7 +1442,7 @@ static PyMethodDef Index_methods[] = { "Write an existing index object from memory back to disk using an" " atomic file lock."}, {"create_tree", (PyCFunction)Index_create_tree, METH_NOARGS, - "Create a tree from the index entries"}, + "Create a tree from the index file, return its SHA."}, {NULL} }; @@ -1796,28 +1756,27 @@ initpygit2(void) RepositoryType.tp_new = PyType_GenericNew; if (PyType_Ready(&RepositoryType) < 0) return; - /* Do not set ObjectType.tp_new, to prevent creating Objects directly. */ + + /* Do not set 'tp_new' for Git objects. To create Git objects use the + * Repository.create_XXX methods */ if (PyType_Ready(&ObjectType) < 0) return; CommitType.tp_base = &ObjectType; - CommitType.tp_new = PyType_GenericNew; if (PyType_Ready(&CommitType) < 0) return; - TreeEntryType.tp_new = PyType_GenericNew; - if (PyType_Ready(&TreeEntryType) < 0) - return; TreeType.tp_base = &ObjectType; - TreeType.tp_new = PyType_GenericNew; if (PyType_Ready(&TreeType) < 0) return; BlobType.tp_base = &ObjectType; - BlobType.tp_new = PyType_GenericNew; if (PyType_Ready(&BlobType) < 0) return; TagType.tp_base = &ObjectType; - TagType.tp_new = PyType_GenericNew; if (PyType_Ready(&TagType) < 0) return; + + TreeEntryType.tp_new = PyType_GenericNew; + if (PyType_Ready(&TreeEntryType) < 0) + return; IndexType.tp_new = PyType_GenericNew; if (PyType_Ready(&IndexType) < 0) return; diff --git a/test/test_commit.py b/test/test_commit.py index 6aea2a67e..81aff775a 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -31,7 +31,7 @@ import unittest -from pygit2 import Commit, GIT_OBJ_COMMIT +from pygit2 import GIT_OBJ_COMMIT import utils COMMIT_SHA = '5fe808e8953c12735680c257f56600cb0de44b10' @@ -62,13 +62,15 @@ def test_read_commit(self): '967fce8df97cc71722d3c2a5930ef3e6f1d27b12', commit.tree.sha) def test_new_commit(self): + repo = self.repo message = 'New commit.\n\nMessage.\n' committer = ('John Doe', 'jdoe@example.com', 12346, 0) author = ('Jane Doe', 'jdoe2@example.com', 12345, 0) tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' parents = [COMMIT_SHA] - commit = Commit(self.repo, author, committer, message, tree, parents) + sha = repo.create_commit(author, committer, message, tree, parents) + commit = repo[sha] self.assertEqual(GIT_OBJ_COMMIT, commit.type) self.assertEqual('30bb126a4959290987fc07ea49f92be276dce9d6', diff --git a/test/test_index.py b/test/test_index.py index 99c025fe7..1da755877 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -86,10 +86,8 @@ def test_write(self): self.assertTrue('bye.txt' in index) def test_create_tree(self): - sha = 'fd937514cb799514d4b81bb24c5fcfeb6472b245' - index = self.repo.index - tree = index.create_tree() - self.assertEqual(sha, tree.sha) + sha = self.repo.index.create_tree() + self.assertEqual(sha, 'fd937514cb799514d4b81bb24c5fcfeb6472b245') if __name__ == '__main__': diff --git a/test/test_tag.py b/test/test_tag.py index f802b05c3..a8b447b78 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -60,8 +60,9 @@ def test_new_tag(self): message = 'Tag a blob.\n' tagger = ('John Doe', 'jdoe@example.com', 12347, 0) - tag = pygit2.Tag(self.repo, name, target, pygit2.GIT_OBJ_BLOB, - tagger, message) + sha = self.repo.create_tag(name, target, pygit2.GIT_OBJ_BLOB, tagger, + message) + tag = self.repo[sha] self.assertEqual('3ee44658fd11660e828dfc96b9b5c5f38d5b49bb', tag.sha) self.assertEqual(name, tag.name) From 713b14d2ee1dfa5b8946f5c5fd855d8b7148df56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 11 Apr 2011 19:23:48 +0200 Subject: [PATCH 0008/2237] Support updating reference in create_commit --- pygit2.c | 10 +++++----- test/test_commit.py | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pygit2.c b/pygit2.c index 6efea74b9..ef7416079 100644 --- a/pygit2.c +++ b/pygit2.c @@ -416,7 +416,7 @@ free_parents(git_oid **parents, int n) { static PyObject * Repository_create_commit(Repository *self, PyObject *args) { git_signature *author, *committer; - char *message; + char *message, *update_ref; git_oid tree_oid, oid; PyObject *py_parents, *py_parent; int parent_count; @@ -424,7 +424,8 @@ Repository_create_commit(Repository *self, PyObject *args) { int err, i; char hex[GIT_OID_HEXSZ]; - if (!PyArg_ParseTuple(args, "O&O&sO&O!", + if (!PyArg_ParseTuple(args, "zO&O&sO&O!", + &update_ref, signature_converter, &author, signature_converter, &committer, &message, @@ -449,9 +450,8 @@ Repository_create_commit(Repository *self, PyObject *args) { return free_parents(parents, i); } - err = git_commit_create(&oid, self->repo, NULL, - author, committer, message, &tree_oid, - parent_count, (const git_oid**)parents); + err = git_commit_create(&oid, self->repo, update_ref, author, committer, + message, &tree_oid, parent_count, (const git_oid**)parents); free_parents(parents, parent_count); if (err < 0) return Error_set(err); diff --git a/test/test_commit.py b/test/test_commit.py index 81aff775a..deb7bd5af 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -69,7 +69,8 @@ def test_new_commit(self): tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' parents = [COMMIT_SHA] - sha = repo.create_commit(author, committer, message, tree, parents) + sha = repo.create_commit(None, author, committer, message, tree, + parents) commit = repo[sha] self.assertEqual(GIT_OBJ_COMMIT, commit.type) From aac4cf1b86dd2189ceef16eba9fb542c7f5744e9 Mon Sep 17 00:00:00 2001 From: David Versmisse Date: Tue, 12 Apr 2011 12:52:06 +0200 Subject: [PATCH 0009/2237] Implement the References (part I) --- pygit2.c | 304 +++++++++++++++++++++++++++++++++++++++++----- test/__init__.py | 3 +- test/test_refs.py | 97 +++++++++++++++ 3 files changed, 370 insertions(+), 34 deletions(-) create mode 100644 test/test_refs.py diff --git a/pygit2.c b/pygit2.c index ef7416079..ad79352e5 100644 --- a/pygit2.c +++ b/pygit2.c @@ -49,6 +49,7 @@ OBJECT_STRUCT(Object, git_object, obj) OBJECT_STRUCT(Commit, git_commit, commit) OBJECT_STRUCT(Tree, git_tree, tree) OBJECT_STRUCT(Blob, git_blob, blob) +OBJECT_STRUCT(Walker, git_revwalk, walk) typedef struct { PyObject_HEAD @@ -77,9 +78,8 @@ typedef struct { typedef struct { PyObject_HEAD - Repository *repo; - git_revwalk *walk; -} Walker; + git_reference *reference; +} Reference; static PyTypeObject RepositoryType; static PyTypeObject ObjectType; @@ -91,6 +91,7 @@ static PyTypeObject TagType; static PyTypeObject IndexType; static PyTypeObject IndexEntryType; static PyTypeObject WalkerType; +static PyTypeObject ReferenceType; static PyObject *GitError; @@ -163,6 +164,46 @@ Error_set_py_obj(int err, PyObject *py_obj) { return NULL; } +static Object * +wrap_object(git_object *obj, Repository *repo) { + Object *py_obj = NULL; + switch (git_object_type(obj)) { + case GIT_OBJ_COMMIT: + py_obj = (Object*)CommitType.tp_alloc(&CommitType, 0); + break; + case GIT_OBJ_TREE: + py_obj = (Object*)TreeType.tp_alloc(&TreeType, 0); + break; + case GIT_OBJ_BLOB: + py_obj = (Object*)BlobType.tp_alloc(&BlobType, 0); + break; + case GIT_OBJ_TAG: + py_obj = (Object*)TagType.tp_alloc(&TagType, 0); + break; + default: + assert(0); + } + if (!py_obj) + return (Object*)PyErr_NoMemory(); + + py_obj->obj = obj; + py_obj->repo = repo; + Py_INCREF(repo); + return py_obj; +} + +static PyObject * +wrap_reference(git_reference * c_reference) +{ + Reference *py_reference=NULL; + + py_reference = (Reference *)ReferenceType.tp_alloc(&ReferenceType, 0); + if (py_reference == NULL) + return NULL; + py_reference->reference = c_reference; + return (PyObject *)py_reference; +} + static int py_str_to_git_oid(PyObject *py_str, git_oid *oid) { char *hex; @@ -224,34 +265,6 @@ Repository_contains(Repository *self, PyObject *value) { return git_odb_exists(git_repository_database(self->repo), &oid); } -static Object * -wrap_object(git_object *obj, Repository *repo) { - Object *py_obj = NULL; - switch (git_object_type(obj)) { - case GIT_OBJ_COMMIT: - py_obj = (Object*)CommitType.tp_alloc(&CommitType, 0); - break; - case GIT_OBJ_TREE: - py_obj = (Object*)TreeType.tp_alloc(&TreeType, 0); - break; - case GIT_OBJ_BLOB: - py_obj = (Object*)BlobType.tp_alloc(&BlobType, 0); - break; - case GIT_OBJ_TAG: - py_obj = (Object*)TagType.tp_alloc(&TagType, 0); - break; - default: - assert(0); - } - if (!py_obj) - return (Object*)PyErr_NoMemory(); - - py_obj->obj = obj; - py_obj->repo = repo; - Py_INCREF(repo); - return py_obj; -} - static PyObject * Repository_getitem(Repository *self, PyObject *value) { git_oid oid; @@ -273,7 +286,8 @@ Repository_getitem(Repository *self, PyObject *value) { } static int -Repository_read_raw(git_odb_object **obj, git_repository *repo, const git_oid *oid) { +Repository_read_raw(git_odb_object **obj, git_repository *repo, + const git_oid *oid) { return git_odb_read(obj, git_repository_database(repo), oid); } @@ -485,6 +499,87 @@ Repository_create_tag(Repository *self, PyObject *args) { return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); } +static PyObject * +Repository_listall_references(Repository *self, PyObject *args) +{ + git_strarray c_result; + PyObject *py_result, *py_string; + unsigned index; + int err; + + /* 1- Get the C result */ + /* TODO We can choose an other option (instead of GIT_REF_LISTALL) */ + err = git_reference_listall (&c_result, self->repo, GIT_REF_LISTALL); + if (err < 0) + return Error_set(err); + + /* 2- Create a new PyTuple */ + if ( (py_result = PyTuple_New(c_result.count)) == NULL) { + git_strarray_free(&c_result); + return NULL; + } + + /* 3- Fill it */ + for (index=0; index < c_result.count; index++) { + if ((py_string = PyString_FromString( (c_result.strings)[index] )) + == NULL) { + Py_XDECREF(py_result); + git_strarray_free(&c_result); + return NULL; + } + PyTuple_SET_ITEM(py_result, index, py_string); + } + + /* 4- Destroy the c_result */ + git_strarray_free(&c_result); + + /* 5- And return the py_result */ + return py_result; +} + +static PyObject * +Repository_lookup_reference(Repository *self, PyObject *py_name) +{ + git_reference *c_reference; + char *c_name; + int err; + + /* 1- Get the C name */ + c_name = PyString_AsString(py_name); + if (c_name == NULL) + return NULL; + + /* 2- Lookup */ + err = git_reference_lookup(&c_reference, self->repo, c_name); + if (err < 0) + return Error_set(err); + + /* 3- Make an instance of Reference and return it */ + return wrap_reference(c_reference); +} + +static PyObject * +Repository_create_reference(Repository *self, PyObject *args) +{ + git_reference *c_reference; + char *c_name; + git_oid oid; + int err; + + /* 1- Get the C variables */ + if (!PyArg_ParseTuple(args, "sO&", &c_name, + py_str_to_git_oid, &oid)) + return NULL; + + /* 2- Create the reference */ + err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid); + if (err < 0) + return Error_set(err); + + /* 3- Make an instance of Reference and return it */ + return wrap_reference(c_reference); +} + static PyMethodDef Repository_methods[] = { {"create_commit", (PyCFunction)Repository_create_commit, METH_VARARGS, "Create a new commit object, return its SHA."}, @@ -494,6 +589,15 @@ static PyMethodDef Repository_methods[] = { "Generator that traverses the history starting from the given commit."}, {"read", (PyCFunction)Repository_read, METH_O, "Read raw object data from the repository."}, + {"listall_references", (PyCFunction)Repository_listall_references, + METH_NOARGS, + "Return a list with all the references that can be found in a " + "repository."}, + {"lookup_reference", (PyCFunction)Repository_lookup_reference, METH_O, + "Lookup a reference by its name in a repository."}, + {"create_reference", (PyCFunction)Repository_create_reference, METH_VARARGS, + "Create a new reference \"name\" that points to the object given by its " + "\"sha\"."}, {NULL} }; @@ -530,7 +634,7 @@ static PyTypeObject RepositoryType = { 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ - 0, /* tp_repr */ + 0, /* tp_repr */ 0, /* tp_as_number */ &Repository_as_sequence, /* tp_as_sequence */ &Repository_as_mapping, /* tp_as_mapping */ @@ -1712,6 +1816,131 @@ static PyTypeObject WalkerType = { 0, /* tp_new */ }; +static PyObject * +Reference_resolve(Reference *self, PyObject *args) +{ + git_reference *c_reference; + int err; + + /* 1- Resolve */ + err = git_reference_resolve(&c_reference, self->reference); + if (err < 0) + return Error_set(err); + + /* 2- Make an instance of Reference and return it */ + return wrap_reference(c_reference); +} + +static PyObject * +Reference_get_target(Reference *self, PyObject *args) +{ + const char * c_name; + + /* 1- Get the target */ + c_name = git_reference_target(self->reference); + if (c_name == NULL) { + PyErr_Format(PyExc_ValueError, "Not target available"); + return NULL; + } + + /* 2- Make a PyString and return it */ + return PyString_FromString(c_name); +} + +static PyObject * +Reference_get_name(Reference *self) { + const char *c_name; + + c_name = git_reference_name(self->reference); + return PyString_FromString(c_name); +} + +static PyObject * +Reference_get_sha(Reference *self) { + char hex[GIT_OID_HEXSZ]; + const git_oid *oid; + + /* 1- Get the oid (only for "direct" references) */ + oid = git_reference_oid(self->reference); + if (oid == NULL) + { + PyErr_Format(PyExc_ValueError, + "sha is only available if the reference is direct (i.e. not symbolic)"); + return NULL; + } + + /* 2- Convert and return it */ + git_oid_fmt(hex, oid); + return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); +} + +static PyObject * +Reference_get_type(Reference *self) { + git_rtype c_type; + + c_type = git_reference_type(self->reference); + return PyInt_FromLong(c_type); +} + +static PyMethodDef Reference_methods[] = { + {"resolve", (PyCFunction)Reference_resolve, METH_NOARGS, + "Resolve a symbolic reference and return a direct reference"}, + {"get_target", (PyCFunction)Reference_get_target, METH_NOARGS, + "Get full name to the reference pointed by this symbolic reference."}, + {NULL} +}; + +static PyGetSetDef Reference_getseters[] = { + {"name", (getter)Reference_get_name, NULL, + "The full name of a reference.", NULL}, + {"sha", (getter)Reference_get_sha, NULL, "hex SHA", NULL}, + {"type", (getter)Reference_get_type, NULL, + "type (GIT_REF_OID, GIT_REF_SYMBOLIC or GIT_REF_PACKED).", NULL}, + {NULL} +}; + +static PyTypeObject ReferenceType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "pygit2.Reference", /* tp_name */ + sizeof(Reference), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* 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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Reference", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Reference_methods, /* tp_methods */ + 0, /* tp_members */ + Reference_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 */ +}; + static PyObject * init_repository(PyObject *self, PyObject *args) { git_repository *repo; @@ -1786,6 +2015,9 @@ initpygit2(void) WalkerType.tp_new = PyType_GenericNew; if (PyType_Ready(&WalkerType) < 0) return; + ReferenceType.tp_new = PyType_GenericNew; + if (PyType_Ready(&ReferenceType) < 0) + return; m = Py_InitModule3("pygit2", module_methods, "Python bindings for libgit2."); @@ -1823,6 +2055,9 @@ initpygit2(void) Py_INCREF(&IndexEntryType); PyModule_AddObject(m, "IndexEntry", (PyObject *)&IndexEntryType); + Py_INCREF(&ReferenceType); + PyModule_AddObject(m, "Reference", (PyObject *)&ReferenceType); + 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); @@ -1832,4 +2067,7 @@ initpygit2(void) PyModule_AddIntConstant(m, "GIT_SORT_TOPOLOGICAL", GIT_SORT_TOPOLOGICAL); PyModule_AddIntConstant(m, "GIT_SORT_TIME", GIT_SORT_TIME); PyModule_AddIntConstant(m, "GIT_SORT_REVERSE", GIT_SORT_REVERSE); + PyModule_AddIntConstant(m,"GIT_REF_OID", GIT_REF_OID); + PyModule_AddIntConstant(m,"GIT_REF_SYMBOLIC", GIT_REF_SYMBOLIC); + PyModule_AddIntConstant(m,"GIT_REF_PACKED", GIT_REF_PACKED); } diff --git a/test/__init__.py b/test/__init__.py index 93e908ff5..cebf864d1 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -35,7 +35,8 @@ import unittest -names = ['blob', 'commit', 'index', 'repository', 'revwalk', 'tag', 'tree'] +names = ['blob', 'commit', 'index', 'refs', 'repository', 'revwalk', 'tag', + 'tree'] def test_suite(): modules = ['test.test_%s' % n for n in names] return unittest.defaultTestLoader.loadTestsFromNames(modules) diff --git a/test/test_refs.py b/test/test_refs.py new file mode 100644 index 000000000..52e615ebb --- /dev/null +++ b/test/test_refs.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2011 Itaapy +# +# 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. + +"""Tests for reference objects.""" + + +__author__ = 'david.versmisse@itaapy.com (David Versmisse)' + +import unittest +import utils +from pygit2 import GIT_REF_OID + + + +LAST_COMMIT = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' + + + +class ReferencesTest(utils.RepoTestCase): + + def test_list_all_references(self): + self.assertEqual(self.repo.listall_references(), + ('refs/heads/i18n', 'refs/heads/master')) + + + def test_lookup_reference(self): + repo = self.repo + + # Raise KeyError ? + self.assertRaises(KeyError, repo.lookup_reference, 'foo') + + # Test a lookup + reference = repo.lookup_reference('refs/heads/master') + self.assertEqual(reference.name, 'refs/heads/master') + + + def test_reference_get_sha(self): + reference = self.repo.lookup_reference('refs/heads/master') + self.assertEqual(reference.sha, LAST_COMMIT) + + + def test_reference_get_type(self): + reference = self.repo.lookup_reference('refs/heads/master') + self.assertEqual(reference.type, GIT_REF_OID) + + + def test_get_target(self): + # XXX We must have a symbolic reference to make this test + pass + + + def test_reference_resolve(self): + # XXX We must have a symbolic reference to make a better test + reference = self.repo.lookup_reference('refs/heads/master') + reference = reference.resolve() + self.assertEqual(reference.type, GIT_REF_OID) + self.assertEqual(reference.sha, LAST_COMMIT) + + + def test_create_reference(self): + # We add a tag as a new reference that points to "origin/master" + reference = self.repo.create_reference('refs/tags/version1', + LAST_COMMIT) + refs = self.repo.listall_references() + self.assertTrue('refs/tags/version1' in refs) + reference = self.repo.lookup_reference('refs/tags/version1') + self.assertEqual(reference.sha, LAST_COMMIT) + + + +if __name__ == '__main__': + unittest.main() From 858adab759a8852ef4db819c3f5e6fd3b170edb2 Mon Sep 17 00:00:00 2001 From: David Versmisse Date: Wed, 27 Apr 2011 09:31:04 +0200 Subject: [PATCH 0010/2237] Implement the References (part II) --- pygit2.c | 163 +++++++++++++++++++++++++++++++++++++++------- test/test_refs.py | 73 +++++++++++++++++++-- 2 files changed, 208 insertions(+), 28 deletions(-) diff --git a/pygit2.c b/pygit2.c index ad79352e5..192ad051f 100644 --- a/pygit2.c +++ b/pygit2.c @@ -500,26 +500,29 @@ Repository_create_tag(Repository *self, PyObject *args) { } static PyObject * -Repository_listall_references(Repository *self, PyObject *args) -{ +Repository_listall_references(Repository *self, PyObject *args) { + unsigned list_flags=GIT_REF_LISTALL; git_strarray c_result; PyObject *py_result, *py_string; unsigned index; int err; - /* 1- Get the C result */ - /* TODO We can choose an other option (instead of GIT_REF_LISTALL) */ - err = git_reference_listall (&c_result, self->repo, GIT_REF_LISTALL); + /* 1- Get list_flags */ + if (!PyArg_ParseTuple(args, "|I", &list_flags)) + return NULL; + + /* 2- Get the C result */ + err = git_reference_listall(&c_result, self->repo, list_flags); if (err < 0) return Error_set(err); - /* 2- Create a new PyTuple */ + /* 3- Create a new PyTuple */ if ( (py_result = PyTuple_New(c_result.count)) == NULL) { git_strarray_free(&c_result); return NULL; } - /* 3- Fill it */ + /* 4- Fill it */ for (index=0; index < c_result.count; index++) { if ((py_string = PyString_FromString( (c_result.strings)[index] )) == NULL) { @@ -530,16 +533,15 @@ Repository_listall_references(Repository *self, PyObject *args) PyTuple_SET_ITEM(py_result, index, py_string); } - /* 4- Destroy the c_result */ + /* 5- Destroy the c_result */ git_strarray_free(&c_result); - /* 5- And return the py_result */ + /* 6- And return the py_result */ return py_result; } static PyObject * -Repository_lookup_reference(Repository *self, PyObject *py_name) -{ +Repository_lookup_reference(Repository *self, PyObject *py_name) { git_reference *c_reference; char *c_name; int err; @@ -559,8 +561,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) } static PyObject * -Repository_create_reference(Repository *self, PyObject *args) -{ +Repository_create_reference(Repository *self, PyObject *args) { git_reference *c_reference; char *c_name; git_oid oid; @@ -580,6 +581,39 @@ Repository_create_reference(Repository *self, PyObject *args) return wrap_reference(c_reference); } +static PyObject * +Repository_create_symbolic_reference(Repository *self, PyObject *args) { + git_reference *c_reference; + char *c_name, *c_target; + int err; + + /* 1- Get the C variables */ + if (!PyArg_ParseTuple(args, "ss", &c_name, &c_target)) + return NULL; + + /* 2- Create the reference */ + err = git_reference_create_symbolic(&c_reference, self->repo, c_name, + c_target); + if (err < 0) + return Error_set(err); + + /* 3- Make an instance of Reference and return it */ + return wrap_reference(c_reference); +} + +static PyObject * +Repository_packall_references(Repository *self, PyObject *args) { + int err; + + /* 1- Pack */ + err = git_reference_packall(self->repo); + if (err < 0) + return Error_set(err); + + /* 2- Return None */ + Py_RETURN_NONE; +} + static PyMethodDef Repository_methods[] = { {"create_commit", (PyCFunction)Repository_create_commit, METH_VARARGS, "Create a new commit object, return its SHA."}, @@ -590,7 +624,7 @@ static PyMethodDef Repository_methods[] = { {"read", (PyCFunction)Repository_read, METH_O, "Read raw object data from the repository."}, {"listall_references", (PyCFunction)Repository_listall_references, - METH_NOARGS, + METH_VARARGS, "Return a list with all the references that can be found in a " "repository."}, {"lookup_reference", (PyCFunction)Repository_lookup_reference, METH_O, @@ -598,6 +632,12 @@ static PyMethodDef Repository_methods[] = { {"create_reference", (PyCFunction)Repository_create_reference, METH_VARARGS, "Create a new reference \"name\" that points to the object given by its " "\"sha\"."}, + {"create_symbolic_reference", + (PyCFunction)Repository_create_symbolic_reference, METH_VARARGS, + "Create a new symbolic reference \"name\" that points to the reference " + "\"target\"."}, + {"packall_references", (PyCFunction)Repository_packall_references, + METH_NOARGS, "Pack all the loose references in the repository."}, {NULL} }; @@ -1817,8 +1857,42 @@ static PyTypeObject WalkerType = { }; static PyObject * -Reference_resolve(Reference *self, PyObject *args) -{ +Reference_delete(Reference *self, PyObject *args) { + int err; + + /* 1- Delete the reference */ + err = git_reference_delete(self->reference); + if (err < 0) + return Error_set(err); + + /* 2- Invalidate the pointer */ + self->reference = NULL; + + /* 3- Return None */ + Py_RETURN_NONE; +} + +static PyObject * +Reference_rename(Reference *self, PyObject *py_name) { + char *c_name; + int err; + + /* 1- Get the C name */ + c_name = PyString_AsString(py_name); + if (c_name == NULL) + return NULL; + + /* 2- Rename */ + err = git_reference_rename(self->reference, c_name); + if (err < 0) + return Error_set(err); + + /* 3- Return None */ + Py_RETURN_NONE; +} + +static PyObject * +Reference_resolve(Reference *self, PyObject *args) { git_reference *c_reference; int err; @@ -1832,8 +1906,7 @@ Reference_resolve(Reference *self, PyObject *args) } static PyObject * -Reference_get_target(Reference *self, PyObject *args) -{ +Reference_get_target(Reference *self) { const char * c_name; /* 1- Get the target */ @@ -1847,6 +1920,27 @@ Reference_get_target(Reference *self, PyObject *args) return PyString_FromString(c_name); } +static int +Reference_set_target(Reference *self, PyObject *py_name) { + char *c_name; + int err; + + /* 1- Get the C name */ + c_name = PyString_AsString(py_name); + if (c_name == NULL) + return -1; + + /* 2- Set the new target */ + err = git_reference_set_target(self->reference, c_name); + if (err < 0) { + Error_set(err); + return -1; + } + + /* 3- All OK */ + return 0; +} + static PyObject * Reference_get_name(Reference *self) { const char *c_name; @@ -1874,6 +1968,26 @@ Reference_get_sha(Reference *self) { return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); } +static int +Reference_set_sha(Reference *self, PyObject *py_sha) { + git_oid oid; + int err; + + /* 1- Get the oid from the py_sha */ + if (!py_str_to_git_oid(py_sha, &oid)) + return -1; + + /* 2- Set the oid */ + err = git_reference_set_oid (self->reference, &oid); + if (err < 0) { + Error_set(err); + return -1; + } + + /* 3- All OK */ + return 0; +} + static PyObject * Reference_get_type(Reference *self) { git_rtype c_type; @@ -1883,17 +1997,22 @@ Reference_get_type(Reference *self) { } static PyMethodDef Reference_methods[] = { + {"delete", (PyCFunction)Reference_delete, METH_NOARGS, + "Delete this reference. It will no longer be valid!"}, + {"rename", (PyCFunction)Reference_rename, METH_O, + "Rename the reference."}, {"resolve", (PyCFunction)Reference_resolve, METH_NOARGS, - "Resolve a symbolic reference and return a direct reference"}, - {"get_target", (PyCFunction)Reference_get_target, METH_NOARGS, - "Get full name to the reference pointed by this symbolic reference."}, + "Resolve a symbolic reference and return a direct reference."}, {NULL} }; static PyGetSetDef Reference_getseters[] = { {"name", (getter)Reference_get_name, NULL, "The full name of a reference.", NULL}, - {"sha", (getter)Reference_get_sha, NULL, "hex SHA", NULL}, + {"sha", (getter)Reference_get_sha, (setter)Reference_set_sha, "hex SHA", + NULL}, + {"target", (getter)Reference_get_target, (setter)Reference_set_target, + "target", NULL}, {"type", (getter)Reference_get_type, NULL, "type (GIT_REF_OID, GIT_REF_SYMBOLIC or GIT_REF_PACKED).", NULL}, {NULL} diff --git a/test/test_refs.py b/test/test_refs.py index 52e615ebb..7c4d3280f 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -33,7 +33,7 @@ import unittest import utils -from pygit2 import GIT_REF_OID +from pygit2 import GIT_REF_OID, GIT_REF_SYMBOLIC @@ -44,9 +44,23 @@ class ReferencesTest(utils.RepoTestCase): def test_list_all_references(self): - self.assertEqual(self.repo.listall_references(), + repo = self.repo + + # Without argument + self.assertEqual(repo.listall_references(), ('refs/heads/i18n', 'refs/heads/master')) + # We add a symbolic reference + reference = repo.create_symbolic_reference('refs/tags/version1', + 'refs/heads/master') + self.assertEqual(repo.listall_references(), + ('refs/heads/i18n', 'refs/heads/master', + 'refs/tags/version1')) + + # Now we list only the symbolic references + self.assertEqual(repo.listall_references(GIT_REF_SYMBOLIC), + ('refs/tags/version1', )) + def test_lookup_reference(self): repo = self.repo @@ -64,19 +78,54 @@ def test_reference_get_sha(self): self.assertEqual(reference.sha, LAST_COMMIT) + def test_reference_set_sha(self): + NEW_COMMIT = '5ebeeebb320790caf276b9fc8b24546d63316533' + reference = self.repo.lookup_reference('refs/heads/master') + reference.sha = NEW_COMMIT + self.assertEqual(reference.sha, NEW_COMMIT) + + def test_reference_get_type(self): reference = self.repo.lookup_reference('refs/heads/master') self.assertEqual(reference.type, GIT_REF_OID) def test_get_target(self): - # XXX We must have a symbolic reference to make this test - pass + reference = self.repo.lookup_reference('HEAD') + self.assertEqual(reference.target, 'refs/heads/master') + + + def test_set_target(self): + reference = self.repo.lookup_reference('HEAD') + self.assertEqual(reference.target, 'refs/heads/master') + reference.target = 'refs/heads/i18n' + self.assertEqual(reference.target, 'refs/heads/i18n') + + + def test_delete(self): + repo = self.repo + + # We add a tag as a new reference that points to "origin/master" + reference = repo.create_reference('refs/tags/version1', LAST_COMMIT) + self.assertTrue('refs/tags/version1' in repo.listall_references()) + + # And we delete it + reference.delete() + self.assertFalse('refs/tags/version1' in repo.listall_references()) + + + def test_rename(self): + # We add a tag as a new reference that points to "origin/master" + reference = self.repo.create_reference('refs/tags/version1', + LAST_COMMIT) + self.assertEqual(reference.name, 'refs/tags/version1') + reference.rename('refs/tags/version2') + self.assertEqual(reference.name, 'refs/tags/version2') def test_reference_resolve(self): - # XXX We must have a symbolic reference to make a better test - reference = self.repo.lookup_reference('refs/heads/master') + reference = self.repo.lookup_reference('HEAD') + self.assertEqual(reference.type, GIT_REF_SYMBOLIC) reference = reference.resolve() self.assertEqual(reference.type, GIT_REF_OID) self.assertEqual(reference.sha, LAST_COMMIT) @@ -92,6 +141,18 @@ def test_create_reference(self): self.assertEqual(reference.sha, LAST_COMMIT) + def test_create_symbolic_reference(self): + # We add a tag as a new symbolic reference that always points to + # "refs/heads/master" + reference = self.repo.create_symbolic_reference('refs/tags/beta', + 'refs/heads/master') + self.assertEqual(reference.type, GIT_REF_SYMBOLIC) + self.assertEqual(reference.target, 'refs/heads/master') + + + def test_packall_references(self): + self.repo.packall_references() + if __name__ == '__main__': unittest.main() From bed8c13757d598110f9f65bef492873ade46563f Mon Sep 17 00:00:00 2001 From: David Versmisse Date: Thu, 28 Apr 2011 12:27:14 +0200 Subject: [PATCH 0011/2237] Add the "getters" Repository.path and Repository.workdir --- pygit2.c | 24 ++++++++++++++++++++++++ test/test_repository.py | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/pygit2.c b/pygit2.c index 192ad051f..92a00be7a 100644 --- a/pygit2.c +++ b/pygit2.c @@ -341,6 +341,25 @@ Repository_get_index(Repository *self, void *closure) { return self->index; } +static PyObject * +Repository_get_path(Repository *self, void *closure) { + const char *c_path; + + c_path = git_repository_path(self->repo); + return PyString_FromString(c_path); +} + +static PyObject * +Repository_get_workdir(Repository *self, void *closure) { + const char *c_path; + + c_path = git_repository_workdir(self->repo); + if (c_path == NULL) + Py_RETURN_NONE; + + return PyString_FromString(c_path); +} + static PyObject * Repository_walk(Repository *self, PyObject *args) { @@ -643,6 +662,11 @@ static PyMethodDef Repository_methods[] = { static PyGetSetDef Repository_getseters[] = { {"index", (getter)Repository_get_index, NULL, "index file. ", NULL}, + {"path", (getter)Repository_get_path, NULL, + "The normalized path to the git repository.", NULL}, + {"workdir", (getter)Repository_get_workdir, NULL, + "The normalized path to the working directory of the repository. " + "If the repository is bare, None will be returned.", NULL}, {NULL} }; diff --git a/test/test_repository.py b/test/test_repository.py index 58030cecb..b8573bc3d 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -31,6 +31,7 @@ import binascii import unittest +from os.path import join, abspath import pygit2 import utils @@ -75,6 +76,29 @@ def test_lookup_commit(self): 'This commit has some additional text.\n'), commit.message) + def test_get_path(self): + directory = abspath(self.repo.path) + expected = abspath(join(self._temp_dir, 'testrepo.git')) + self.assertEqual(directory, expected) + + def test_get_workdir(self): + self.assertEqual(self.repo.workdir, None) + + + +class RepositoryTest_II(utils.RepoTestCase): + + def test_get_path(self): + directory = abspath(self.repo.path) + expected = abspath(join(self._temp_dir, 'testrepo', '.git')) + self.assertEqual(directory, expected) + + def test_get_workdir(self): + directory = abspath(self.repo.workdir) + expected = abspath(join(self._temp_dir, 'testrepo')) + self.assertEqual(directory, expected) + + if __name__ == '__main__': unittest.main() From 51e6cdfd5d291bc0ace105f386c3701d9c146989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 5 May 2011 19:27:09 +0200 Subject: [PATCH 0012/2237] Replace internal function wrap_object by lookup_object The new function does a little more, saving some lines from the callers. This change will allow to implement a cache, now all object lookups are centralized in a single function. --- pygit2.c | 73 ++++++++++++++++++++++++-------------------------------- 1 file changed, 31 insertions(+), 42 deletions(-) diff --git a/pygit2.c b/pygit2.c index 92a00be7a..d40920749 100644 --- a/pygit2.c +++ b/pygit2.c @@ -164,9 +164,20 @@ Error_set_py_obj(int err, PyObject *py_obj) { return NULL; } -static Object * -wrap_object(git_object *obj, Repository *repo) { +static PyObject * +lookup_object(Repository *repo, const git_oid *oid, git_otype type) { + int err; + char hex[GIT_OID_HEXSZ + 1]; + git_object *obj; Object *py_obj = NULL; + + err = git_object_lookup(&obj, repo->repo, oid, type); + if (err < 0) { + git_oid_fmt(hex, oid); + hex[GIT_OID_HEXSZ] = '\0'; + return Error_set_str(err, hex); + } + switch (git_object_type(obj)) { case GIT_OBJ_COMMIT: py_obj = (Object*)CommitType.tp_alloc(&CommitType, 0); @@ -184,12 +195,12 @@ wrap_object(git_object *obj, Repository *repo) { assert(0); } if (!py_obj) - return (Object*)PyErr_NoMemory(); + return PyErr_NoMemory(); py_obj->obj = obj; py_obj->repo = repo; Py_INCREF(repo); - return py_obj; + return (PyObject*)py_obj; } static PyObject * @@ -268,21 +279,12 @@ Repository_contains(Repository *self, PyObject *value) { static PyObject * Repository_getitem(Repository *self, PyObject *value) { git_oid oid; - int err; git_object *obj; - Object *py_obj; if (!py_str_to_git_oid(value, &oid)) return NULL; - err = git_object_lookup(&obj, self->repo, &oid, GIT_OBJ_ANY); - if (err < 0) - return Error_set_py_obj(err, value); - - py_obj = wrap_object(obj, self); - if (!py_obj) - return NULL; - return (PyObject*)py_obj; + return lookup_object(self, &oid, GIT_OBJ_ANY); } static int @@ -890,8 +892,7 @@ static PyObject * Commit_get_parents(Commit *commit) { unsigned int i, parent_count; - int err; - git_commit *parent; + const git_oid *parent_oid; PyObject *obj; PyObject *list; @@ -901,13 +902,13 @@ Commit_get_parents(Commit *commit) return NULL; for (i=0; i < parent_count; i++) { - err = git_commit_parent(&parent, commit->commit, i); - if (err < 0) { + parent_oid = git_commit_parent_oid(commit->commit, i); + if (parent_oid == NULL) { Py_DECREF(list); - Error_set(err); + Error_set(GIT_ENOTFOUND); return NULL; } - obj = (PyObject*)wrap_object((git_object *)parent, commit->repo); + obj = lookup_object(commit->repo, parent_oid, GIT_OBJ_COMMIT); if (obj == NULL) { Py_DECREF(list); return NULL; @@ -1000,17 +1001,10 @@ TreeEntry_get_sha(TreeEntry *self) { static PyObject * TreeEntry_to_object(TreeEntry *self) { - git_object *obj; - int err; - char hex[GIT_OID_HEXSZ + 1]; + const git_oid *entry_oid; - err = git_tree_entry_2object(&obj, self->tree->repo->repo, self->entry); - if (err < 0) { - git_oid_fmt(hex, git_tree_entry_id(self->entry)); - hex[GIT_OID_HEXSZ] = '\0'; - return Error_set_str(err, hex); - } - return (PyObject*)wrap_object(obj, self->tree->repo); + entry_oid = git_tree_entry_id(self->entry); + return lookup_object(self->tree->repo, entry_oid, GIT_OBJ_ANY); } static PyGetSetDef TreeEntry_getseters[] = { @@ -1284,20 +1278,15 @@ Tag_dealloc(Tag *self) { static PyObject * Tag_get_target(Tag *self) { - git_object *target; - int err; + const git_oid *target_oid; + git_otype target_type; if (self->target == NULL) { - err = git_tag_target(&target, self->tag); - if (err == GIT_ENOTFOUND) { - /* This can only happen if we have a new tag with no target set - * yet. */ - Py_INCREF(Py_None); - self->target = Py_None; - } else if (err < 0) - return Error_set(err); - else - self->target = (PyObject*)wrap_object(target, self->repo); + target_oid = git_tag_target_oid(self->tag); + target_type = git_tag_type(self->tag); + self->target = lookup_object(self->repo, target_oid, target_type); + if (self->target == NULL) + return NULL; } Py_INCREF(self->target); From 8ad5e30eda0995ae77994da878dbb0bb7d9225ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 5 May 2011 19:55:04 +0200 Subject: [PATCH 0013/2237] Update maintainer in the setup file --- setup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index f49161e5d..85a67270a 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ -#!/usr/bin/env python +# -*- coding: UTF-8 -*- # # Copyright 2010 Google, Inc. +# Copyright 2011 Itaapy # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, @@ -72,8 +73,8 @@ def run(self): version='0.1', url='http://github.com/libgit2/pygit2', license='GPLv2', - author='David Borowitz', - author_email='dborowitz@google.com', + maintainer='J. David Ibáñez', + maintainer_email='jdavid@itaapy.com', long_description=""" Bindings for libgit2, a linkable C library for the Git version-control system. From dd86fbe048cc52ccf6d57fcb55c0926ecd0d1561 Mon Sep 17 00:00:00 2001 From: Zoran Zaric Date: Mon, 9 May 2011 13:43:09 +0200 Subject: [PATCH 0014/2237] Fix spelling mistake in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 56c3a28d2..d88b1cef7 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Iterate over all entries of the index: Index write: >>> index.add('path/to/file') # git add - >>> del index['path/to/file'] # gig rm + >>> del index['path/to/file'] # git rm >>> index.write() # don't forget to save the changes Revision walking: From 1a717451962279f0b69e92c12f57037969a46096 Mon Sep 17 00:00:00 2001 From: Zoran Zaric Date: Mon, 9 May 2011 13:43:38 +0200 Subject: [PATCH 0015/2237] Remove trailing spaces from README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d88b1cef7..0519880f5 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ pygit2 is a set of Python 2.5+ bindings to the libgit2 linkable C Git library. INSTALLING AND RUNNING ======================== -First you need to install the latest version of libgit2. +First you need to install the latest version of libgit2. You can find platform-specific instructions to build the library in the libgit2 website: @@ -20,7 +20,7 @@ Also, make sure you have Python 2.5+ installed together with the Python developm When those are installed, you can install pygit2: - $ git clone git://github.com/libgit2/pygit2.git + $ git clone git://github.com/libgit2/pygit2.git $ cd pygit2 $ python setup.py install $ python setup.py test @@ -73,7 +73,7 @@ Fork libgit2/pygit2 on GitHub, make it awesomer (preferably in a branch named for the topic), send a pull request. -AUTHORS +AUTHORS ============== * David Borowitz From 22b6d44749fd9abf8bcc462f7ff6f89430a158fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 10 May 2011 15:45:52 +0200 Subject: [PATCH 0016/2237] Update to libgit2's changes: strerror -> lasterror --- pygit2.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pygit2.c b/pygit2.c index d40920749..3dec261db 100644 --- a/pygit2.c +++ b/pygit2.c @@ -119,14 +119,14 @@ Error_set(int err) { if (err == GIT_ENOTFOUND) { /* KeyError expects the arg to be the missing key. If the caller called * this instead of Error_set_py_obj, it means we don't know the key, but - * nor should we use git_strerror. */ + * nor should we use git_lasterror. */ PyErr_SetNone(PyExc_KeyError); return NULL; } else if (err == GIT_EOSERR) { PyErr_SetFromErrno(GitError); return NULL; } - PyErr_SetString(Error_type(err), git_strerror(err)); + PyErr_SetString(Error_type(err), git_lasterror()); return NULL; } @@ -137,7 +137,7 @@ Error_set_str(int err, const char *str) { PyErr_Format(PyExc_KeyError, "%s", str); return NULL; } - PyErr_Format(Error_type(err), "%s: %s", str, git_strerror(err)); + PyErr_Format(Error_type(err), "%s: %s", str, git_lasterror()); return NULL; } @@ -159,7 +159,7 @@ Error_set_py_obj(int err, PyObject *py_obj) { } py_str = PyObject_Str(py_obj); str = py_str ? PyString_AS_STRING(py_str) : ""; - PyErr_Format(Error_type(err), "%s: %s", str, git_strerror(err)); + PyErr_Format(Error_type(err), "%s: %s", str, git_lasterror()); Py_XDECREF(py_str); return NULL; } From 9dfc40e3f3f329965a8ff29b3f5b7becdc2b675e Mon Sep 17 00:00:00 2001 From: Jared Flatow Date: Sun, 8 May 2011 11:09:26 -0700 Subject: [PATCH 0017/2237] added iterator to index objects --- pygit2.c | 83 +++++++++++++++++++++++++++++++++++++++++++++- test/test_index.py | 4 +++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index d40920749..0f01da2af 100644 --- a/pygit2.c +++ b/pygit2.c @@ -71,6 +71,12 @@ typedef struct { int own_obj:1; } Index; +typedef struct { + PyObject_HEAD + Index *owner; + Py_ssize_t i; +} IndexIter; + typedef struct { PyObject_HEAD git_index_entry *entry; @@ -89,12 +95,17 @@ static PyTypeObject TreeType; static PyTypeObject BlobType; static PyTypeObject TagType; static PyTypeObject IndexType; +static PyTypeObject IndexIterType; static PyTypeObject IndexEntryType; static PyTypeObject WalkerType; static PyTypeObject ReferenceType; static PyObject *GitError; +static PyObject * IndexIter_new(PyTypeObject *, Index *); +static void IndexIter_dealloc(IndexIter *); +static PyObject * IndexIter_iternext(IndexIter *); + static PyObject * Error_type(int err) { switch (err) { @@ -1515,6 +1526,11 @@ Index_contains(Index *self, PyObject *value) { return 1; } +static PyObject * +Index_iter(Index *self) { + return IndexIter_new(&IndexIterType, self); +} + static Py_ssize_t Index_len(Index *self) { return (Py_ssize_t)git_index_entrycount(self->index); @@ -1647,7 +1663,7 @@ static PyTypeObject IndexType = { 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ + (getiterfunc)Index_iter, /* tp_iter */ 0, /* tp_iternext */ Index_methods, /* tp_methods */ 0, /* tp_members */ @@ -1662,6 +1678,71 @@ static PyTypeObject IndexType = { 0, /* tp_new */ }; + +static PyObject * +IndexIter_new(PyTypeObject *type, Index *owner) { + IndexIter *self = PyObject_New(IndexIter, type); + if (self != NULL) { + Py_INCREF(owner); + self->owner = owner; + self->i = 0; + } + return (PyObject *)self; +} + +static void +IndexIter_dealloc(IndexIter *self) { + Py_CLEAR(self->owner); + PyObject_Del(self); +} + +static PyObject * +IndexIter_iternext(IndexIter *self) { + PyObject *value = NULL, *entry = NULL; + + if (self->i >= Index_len(self->owner)) + return NULL; + + value = PyInt_FromSsize_t(self->i++); + if (value == NULL) + return NULL; + + entry = Index_getitem(self->owner, value); + Py_CLEAR(value); + return entry; +} + +static PyTypeObject IndexIterType = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "pygit2.IndexIter", /* tp_name */ + sizeof(IndexIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)IndexIter_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)IndexIter_iternext, /* tp_iternext */ + }; + static void IndexEntry_dealloc(IndexEntry *self) { self->ob_type->tp_free((PyObject*)self); diff --git a/test/test_index.py b/test/test_index.py index 1da755877..2d88f4d0e 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -89,6 +89,10 @@ def test_create_tree(self): sha = self.repo.index.create_tree() self.assertEqual(sha, 'fd937514cb799514d4b81bb24c5fcfeb6472b245') + def test_iter(self): + index = self.repo.index + entries = [index[x] for x in xrange(len(index))] + self.assertNotEqual(list(index), entries) if __name__ == '__main__': unittest.main() From b7fe1f90d0d4c33240e12fd1bf67a34bb9aa935d Mon Sep 17 00:00:00 2001 From: Jared Flatow Date: Sun, 8 May 2011 11:13:19 -0700 Subject: [PATCH 0018/2237] added script to obviate nasty memory leak in Index_getitem --- test/test_leak.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test/test_leak.py diff --git a/test/test_leak.py b/test/test_leak.py new file mode 100644 index 000000000..35259b801 --- /dev/null +++ b/test/test_leak.py @@ -0,0 +1,17 @@ +import unittest + +import utils + +class TestLeak(utils.RepoTestCase): + def test_index_iter(self): + index = self.repo.index + while True: + list(index) + + def test_index_getitem(self): + index = self.repo.index + while True: + index[0] + +if __name__ == '__main__': + unittest.main() From f55b001db9a893f9c36c80e74da0de1ae63845a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 11 May 2011 17:18:23 +0200 Subject: [PATCH 0019/2237] Fix index iterator test --- test/test_index.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/test_index.py b/test/test_index.py index 2d88f4d0e..69504f420 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -91,8 +91,12 @@ def test_create_tree(self): def test_iter(self): index = self.repo.index - entries = [index[x] for x in xrange(len(index))] - self.assertNotEqual(list(index), entries) + n = len(index) + self.assertEqual(len(list(index)), n) + # FIXME This fails + #entries = [index[x] for x in xrange(n)] + #self.assertEqual(list(index), entries) + if __name__ == '__main__': unittest.main() From f8e61de9734b135f856a385bc20740a547a87ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 11 May 2011 18:03:50 +0200 Subject: [PATCH 0020/2237] Optimize index iterator, refactor code --- pygit2.c | 73 +++++++++++++++++++++++++++----------------------------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/pygit2.c b/pygit2.c index 60c1f555b..1841655db 100644 --- a/pygit2.c +++ b/pygit2.c @@ -74,7 +74,7 @@ typedef struct { typedef struct { PyObject_HEAD Index *owner; - Py_ssize_t i; + int i; } IndexIter; typedef struct { @@ -102,10 +102,6 @@ static PyTypeObject ReferenceType; static PyObject *GitError; -static PyObject * IndexIter_new(PyTypeObject *, Index *); -static void IndexIter_dealloc(IndexIter *); -static PyObject * IndexIter_iternext(IndexIter *); - static PyObject * Error_type(int err) { switch (err) { @@ -1528,7 +1524,16 @@ Index_contains(Index *self, PyObject *value) { static PyObject * Index_iter(Index *self) { - return IndexIter_new(&IndexIterType, self); + IndexIter *iter; + + iter = PyObject_New(IndexIter, &IndexIterType); + if (!iter) + return NULL; + + Py_INCREF(self); + iter->owner = self; + iter->i = 0; + return (PyObject*)iter; } static Py_ssize_t @@ -1536,11 +1541,24 @@ Index_len(Index *self) { return (Py_ssize_t)git_index_entrycount(self->index); } +static PyObject * +wrap_index_entry(git_index_entry *entry, Index *index) { + IndexEntry *py_entry; + + py_entry = (IndexEntry*)IndexEntryType.tp_alloc(&IndexEntryType, 0); + if (!py_entry) + return PyErr_NoMemory(); + + py_entry->entry = entry; + + Py_INCREF(py_entry); + return (PyObject*)py_entry; +} + static PyObject * Index_getitem(Index *self, PyObject *value) { int idx; git_index_entry *index_entry; - IndexEntry *py_index_entry; idx = Index_get_position(self, value); if (idx == -1) @@ -1552,14 +1570,7 @@ Index_getitem(Index *self, PyObject *value) { return NULL; } - py_index_entry = (IndexEntry*)IndexEntryType.tp_alloc(&IndexEntryType, 0); - if (!py_index_entry) - return PyErr_NoMemory(); - - py_index_entry->entry = index_entry; - - Py_INCREF(py_index_entry); - return (PyObject*)py_index_entry; + return wrap_index_entry(index_entry, self); } static int @@ -1679,37 +1690,23 @@ static PyTypeObject IndexType = { }; -static PyObject * -IndexIter_new(PyTypeObject *type, Index *owner) { - IndexIter *self = PyObject_New(IndexIter, type); - if (self != NULL) { - Py_INCREF(owner); - self->owner = owner; - self->i = 0; - } - return (PyObject *)self; -} - static void IndexIter_dealloc(IndexIter *self) { - Py_CLEAR(self->owner); - PyObject_Del(self); + Py_CLEAR(self->owner); + PyObject_Del(self); } static PyObject * IndexIter_iternext(IndexIter *self) { - PyObject *value = NULL, *entry = NULL; - - if (self->i >= Index_len(self->owner)) - return NULL; + git_index_entry *index_entry; + PyObject *entry = NULL; - value = PyInt_FromSsize_t(self->i++); - if (value == NULL) - return NULL; + index_entry = git_index_get(self->owner->index, self->i); + if (!index_entry) + return NULL; - entry = Index_getitem(self->owner, value); - Py_CLEAR(value); - return entry; + self->i += 1; + return wrap_index_entry(index_entry, self->owner); } static PyTypeObject IndexIterType = { From 64b20305e68dce744555c69e82abe29b8b5a7549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 16 Jun 2011 22:48:42 +0200 Subject: [PATCH 0021/2237] Use newer API --- pygit2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pygit2.c b/pygit2.c index 1841655db..9f0292080 100644 --- a/pygit2.c +++ b/pygit2.c @@ -354,7 +354,7 @@ static PyObject * Repository_get_path(Repository *self, void *closure) { const char *c_path; - c_path = git_repository_path(self->repo); + c_path = git_repository_path(self->repo, GIT_REPO_PATH); return PyString_FromString(c_path); } @@ -362,7 +362,7 @@ static PyObject * Repository_get_workdir(Repository *self, void *closure) { const char *c_path; - c_path = git_repository_workdir(self->repo); + c_path = git_repository_path(self->repo, GIT_REPO_PATH_WORKDIR); if (c_path == NULL) Py_RETURN_NONE; @@ -1391,7 +1391,7 @@ Index_init(Index *self, PyObject *args, PyObject *kwds) { if (!PyArg_ParseTuple(args, "s", &path)) return -1; - err = git_index_open_bare(&self->index, path); + err = git_index_open(&self->index, path); if (err < 0) { Error_set_str(err, path); return -1; From 7e8da5456942c02818056523a3b12a605f2c35ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 20 Jun 2011 16:04:52 +0200 Subject: [PATCH 0022/2237] Update to libgit2 v0.13.0 --- pygit2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index 9f0292080..851c663d3 100644 --- a/pygit2.c +++ b/pygit2.c @@ -233,7 +233,7 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) { return 0; } - err = git_oid_mkstr(oid, hex); + err = git_oid_fromstr(oid, hex); if (err < 0) { Error_set_py_obj(err, py_str); return 0; From 215893328cf22e94c69601c801ac2400cd01aafc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 25 Jun 2011 18:43:21 +0200 Subject: [PATCH 0023/2237] Fix issue #22, pygit2 compiles again with Python 2.5 --- pygit2.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pygit2.c b/pygit2.c index 851c663d3..3716caf23 100644 --- a/pygit2.c +++ b/pygit2.c @@ -29,6 +29,12 @@ #include #include +/* Define PyVarObject_HEAD_INIT for Python 2.5 */ +#ifndef PyVarObject_HEAD_INIT +#define PyVarObject_HEAD_INIT(type, size) \ + PyObject_HEAD_INIT(type) size, +#endif + typedef struct { PyObject_HEAD From eedd975bc832cd0baf46cf53e8c5e3eaf571e334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 25 Jun 2011 18:53:22 +0200 Subject: [PATCH 0024/2237] Fix compilation warnings, remove unused variables --- pygit2.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/pygit2.c b/pygit2.c index 3716caf23..e4761f727 100644 --- a/pygit2.c +++ b/pygit2.c @@ -292,7 +292,6 @@ Repository_contains(Repository *self, PyObject *value) { static PyObject * Repository_getitem(Repository *self, PyObject *value) { git_oid oid; - git_object *obj; if (!py_str_to_git_oid(value, &oid)) return NULL; @@ -1705,7 +1704,6 @@ IndexIter_dealloc(IndexIter *self) { static PyObject * IndexIter_iternext(IndexIter *self) { git_index_entry *index_entry; - PyObject *entry = NULL; index_entry = git_index_get(self->owner->index, self->i); if (!index_entry) From 84cb4616b7260d6e9956e7403b47ae17d8a5fd00 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Jul 2011 11:04:28 +0200 Subject: [PATCH 0025/2237] Fixed reference tests, they were dependent on the order of the file-system, which in fact is undetermined. Sorting them to assure order --- test/test_refs.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test_refs.py b/test/test_refs.py index 7c4d3280f..575560e85 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -47,15 +47,15 @@ def test_list_all_references(self): repo = self.repo # Without argument - self.assertEqual(repo.listall_references(), - ('refs/heads/i18n', 'refs/heads/master')) + self.assertEqual(sorted(repo.listall_references()), + ['refs/heads/i18n', 'refs/heads/master']) # We add a symbolic reference reference = repo.create_symbolic_reference('refs/tags/version1', 'refs/heads/master') - self.assertEqual(repo.listall_references(), - ('refs/heads/i18n', 'refs/heads/master', - 'refs/tags/version1')) + self.assertEqual(sorted(repo.listall_references()), + ['refs/heads/i18n', 'refs/heads/master', + 'refs/tags/version1']) # Now we list only the symbolic references self.assertEqual(repo.listall_references(GIT_REF_SYMBOLIC), From cf37c150593bdd6a57e4184ca10502aaf7fc9ba1 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Jul 2011 11:38:12 +0200 Subject: [PATCH 0026/2237] allow 20 byte sha strings Adjusted py_str_to_git_oid to allow 20 byte binary strings. This improves usability to other clients which already store their shas as binary strings, increasing overall performance as they won't have to convert them into hex beforehand, which would have to be converted back to binary form by libgit2 in turn. Adjusted tests to accept binary shas. --- pygit2.c | 16 +++++++++++----- test/test_repository.py | 7 ++++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/pygit2.c b/pygit2.c index e4761f727..0de7a58bc 100644 --- a/pygit2.c +++ b/pygit2.c @@ -230,16 +230,22 @@ wrap_reference(git_reference * c_reference) static int py_str_to_git_oid(PyObject *py_str, git_oid *oid) { - char *hex; + const char *hex_or_bin; int err; - hex = PyString_AsString(py_str); - if (hex == NULL) { + hex_or_bin = PyString_AsString(py_str); + if (hex_or_bin == NULL) { Error_set_py_obj(GIT_ENOTOID, py_str); return 0; } - - err = git_oid_fromstr(oid, hex); + + if (PyString_Size(py_str) == 20) { + git_oid_fromraw(oid, (const unsigned char*)hex_or_bin); + err = 0; + } else { + err = git_oid_fromstr(oid, hex_or_bin); + } + if (err < 0) { Error_set_py_obj(err, py_str); return 0; diff --git a/test/test_repository.py b/test/test_repository.py index b8573bc3d..d584e46bc 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -44,10 +44,11 @@ class RepositoryTest(utils.BareRepoTestCase): def test_read(self): self.assertRaises(TypeError, self.repo.read, 123) - self.assertRaises(ValueError, self.repo.read, A_BIN_SHA) self.assertRaisesWithArg(KeyError, '1' * 40, self.repo.read, '1' * 40) + ab = self.repo.read(A_BIN_SHA) a = self.repo.read(A_HEX_SHA) + self.assertEqual(ab, a) self.assertEqual((pygit2.GIT_OBJ_BLOB, 'a contents\n'), a) a2 = self.repo.read('7f129fd57e31e935c6d60a0c794efe4e6927664b') @@ -55,13 +56,13 @@ def test_read(self): def test_contains(self): self.assertRaises(TypeError, lambda: 123 in self.repo) - self.assertRaises(ValueError, lambda: A_BIN_SHA in self.repo) + self.assertTrue(A_BIN_SHA in self.repo) self.assertTrue(A_HEX_SHA in self.repo) self.assertFalse('a' * 40 in self.repo) def test_lookup_blob(self): self.assertRaises(TypeError, lambda: self.repo[123]) - self.assertRaises(ValueError, lambda: self.repo[A_BIN_SHA]) + self.assertEqual(self.repo[A_BIN_SHA].sha, A_HEX_SHA) a = self.repo[A_HEX_SHA] self.assertEqual('a contents\n', a.read_raw()) self.assertEqual(A_HEX_SHA, a.sha) From a0975f78d597730a0b40ed17a4642edb256e1a2b Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Jul 2011 11:54:36 +0200 Subject: [PATCH 0027/2237] Fixed memory leaks Repository_read would not free the odb object after using it. Object_read_raw didn't close the odb object after usage --- pygit2.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pygit2.c b/pygit2.c index 0de7a58bc..3b3c74523 100644 --- a/pygit2.c +++ b/pygit2.c @@ -238,14 +238,14 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) { Error_set_py_obj(GIT_ENOTOID, py_str); return 0; } - + if (PyString_Size(py_str) == 20) { git_oid_fromraw(oid, (const unsigned char*)hex_or_bin); err = 0; } else { err = git_oid_fromstr(oid, hex_or_bin); } - + if (err < 0) { Error_set_py_obj(err, py_str); return 0; @@ -324,11 +324,14 @@ Repository_read(Repository *self, PyObject *py_hex) { if (err < 0) return Error_set_py_obj(err, py_hex); - return Py_BuildValue( + PyObject* tuple = Py_BuildValue( "(ns#)", git_odb_object_type(obj), git_odb_object_data(obj), git_odb_object_size(obj)); + + git_odb_object_close(obj); + return tuple; } static PyObject * @@ -797,6 +800,8 @@ Object_read_raw(Object *self) { git_odb_object_data(obj), git_odb_object_size(obj)); + git_odb_object_close(obj); + cleanup: Py_XDECREF(py_sha); return result; From 0eb75de425dc3c8a229de5e32d8a420226277e7d Mon Sep 17 00:00:00 2001 From: Julien Miotte Date: Mon, 11 Jul 2011 22:07:29 +0200 Subject: [PATCH 0028/2237] Making the Tree object iterable. By copying IndexIter functions, this gives us the ability to iterate of TreeEntries of a Tree object. --- pygit2.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index e4761f727..3169dac8b 100644 --- a/pygit2.c +++ b/pygit2.c @@ -83,6 +83,12 @@ typedef struct { int i; } IndexIter; +typedef struct { + PyObject_HEAD + Tree *owner; + int i; +} TreeIter; + typedef struct { PyObject_HEAD git_index_entry *entry; @@ -101,6 +107,7 @@ static PyTypeObject TreeType; static PyTypeObject BlobType; static PyTypeObject TagType; static PyTypeObject IndexType; +static PyTypeObject TreeIterType; static PyTypeObject IndexIterType; static PyTypeObject IndexEntryType; static PyTypeObject WalkerType; @@ -1145,6 +1152,21 @@ Tree_fix_index(Tree *self, PyObject *py_index) { return (int)index; } +static PyObject * +Tree_iter(Tree *self) { + TreeIter *iter; + + iter = PyObject_New(TreeIter, &TreeIterType); + if (!iter) + return NULL; + + Py_INCREF(self); + iter->owner = self; + iter->i = 0; + + return (PyObject*)iter; +} + static TreeEntry * Tree_getitem_by_index(Tree *self, PyObject *py_index) { int index; @@ -1220,7 +1242,7 @@ static PyTypeObject TreeType = { 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ + (getiterfunc)Tree_iter, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ @@ -1235,6 +1257,55 @@ static PyTypeObject TreeType = { 0, /* tp_new */ }; +static void +TreeIter_dealloc(TreeIter *self) { + Py_CLEAR(self->owner); + PyObject_Del(self); +} + +static TreeEntry * +TreeIter_iternext(TreeIter *self) { + const git_tree_entry *tree_entry; + + tree_entry = git_tree_entry_byindex(self->owner->tree, self->i); + if (!tree_entry) + return NULL; + + self->i += 1; + return (TreeEntry*)wrap_tree_entry(tree_entry, self->owner); +} + +static PyTypeObject TreeIterType = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "pygit2.TreeIter", /* tp_name */ + sizeof(TreeIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)TreeIter_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)TreeIter_iternext, /* tp_iternext */ + }; + static PyGetSetDef Blob_getseters[] = { {"data", (getter)Object_read_raw, NULL, "raw data", NULL}, {NULL} From 30078270bb77554829210b5fae3faf717e67da7f Mon Sep 17 00:00:00 2001 From: Julien Miotte Date: Mon, 11 Jul 2011 22:23:01 +0200 Subject: [PATCH 0029/2237] Adding a test on Tree objects iteration. --- test/test_tree.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/test_tree.py b/test/test_tree.py index bcbac3162..b48308f82 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -108,6 +108,16 @@ def test_modify_tree(self): self.assertRaises(TypeError, operator.setitem, 'c', tree['a']) self.assertRaises(TypeError, operator.delitem, 'c') + def test_iterate_tree(self): + """ + Testing that we're able to iterate of a Tree object and that the + resulting sha strings are consitent with the sha strings we could + get with other Tree access methods. + """ + tree = self.repo[TREE_SHA] + for tree_entry in tree: + self.assertEqual(tree_entry.sha, tree[tree_entry.name].sha) + if __name__ == '__main__': unittest.main() From d484edb293035092fd746cdae395da73b0f2bdfd Mon Sep 17 00:00:00 2001 From: Julien Miotte Date: Tue, 12 Jul 2011 16:14:06 +0200 Subject: [PATCH 0030/2237] Adding a new 'mode' attribute to IndexEntry objects. This new attribute gives use read access on IndexEntry's mode. --- pygit2.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pygit2.c b/pygit2.c index b870e694d..dc605b4ea 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1831,6 +1831,11 @@ IndexEntry_dealloc(IndexEntry *self) { self->ob_type->tp_free((PyObject*)self); } +static PyObject * +IndexEntry_get_mode(IndexEntry *self) { + return PyInt_FromLong(self->entry->mode); +} + static PyObject * IndexEntry_get_path(IndexEntry *self) { return PyString_FromString(self->entry->path); @@ -1845,6 +1850,7 @@ IndexEntry_get_sha(IndexEntry *self) { } static PyGetSetDef IndexEntry_getseters[] = { + {"mode", (getter)IndexEntry_get_mode, NULL, "mode", NULL}, {"path", (getter)IndexEntry_get_path, NULL, "path", NULL}, {"sha", (getter)IndexEntry_get_sha, NULL, "hex SHA", NULL}, {NULL}, From 209328111090184fecf5e293794193a3d5e91ef2 Mon Sep 17 00:00:00 2001 From: Julien Miotte Date: Tue, 12 Jul 2011 16:15:35 +0200 Subject: [PATCH 0031/2237] Adding a test on IndexEntry 'mode' new attribute. --- test/test_index.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/test_index.py b/test/test_index.py index 69504f420..f2d82f73c 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -97,6 +97,15 @@ def test_iter(self): #entries = [index[x] for x in xrange(n)] #self.assertEqual(list(index), entries) + def test_mode(self): + """ + Testing that we can access an index entry mode. + """ + index = self.repo.index + + hello_mode = index['hello.txt'].mode + self.assertEqual(hello_mode, 33188) + if __name__ == '__main__': unittest.main() From 7b6c6a7a4ced77cba7d0f4bbcd4145e11fe0b2ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 14 Jul 2011 12:05:09 +0200 Subject: [PATCH 0032/2237] Update to libgit2 v0.14.0 --- pygit2.c | 57 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/pygit2.c b/pygit2.c index dc605b4ea..bd9bbc961 100644 --- a/pygit2.c +++ b/pygit2.c @@ -467,11 +467,11 @@ signature_converter(PyObject *value, git_signature **out) { } static PyObject * -free_parents(git_oid **parents, int n) { +free_parents(git_commit **parents, int n) { int i; for (i = 0; i < n; i++) - free(parents[i]); + git_commit_close(parents[i]); free(parents); return NULL; } @@ -480,10 +480,11 @@ static PyObject * Repository_create_commit(Repository *self, PyObject *args) { git_signature *author, *committer; char *message, *update_ref; - git_oid tree_oid, oid; + git_oid oid; + git_tree *tree; PyObject *py_parents, *py_parent; int parent_count; - git_oid **parents; + git_commit **parents; int err, i; char hex[GIT_OID_HEXSZ]; @@ -492,29 +493,36 @@ Repository_create_commit(Repository *self, PyObject *args) { signature_converter, &author, signature_converter, &committer, &message, - py_str_to_git_oid, &tree_oid, + py_str_to_git_oid, &oid, &PyList_Type, &py_parents)) return NULL; + err = git_tree_lookup(&tree, self->repo, &oid); + if (err < 0) + return Error_set(err); + parent_count = (int)PyList_Size(py_parents); - parents = malloc(parent_count * sizeof(git_oid*)); + parents = malloc(parent_count * sizeof(git_commit*)); if (parents == NULL) { + git_tree_close(tree); PyErr_SetNone(PyExc_MemoryError); return NULL; } for (i = 0; i < parent_count; i++) { - parents[i] = malloc(sizeof(git_oid)); - if (parents[i] == NULL) { - PyErr_SetNone(PyExc_MemoryError); + py_parent = PyList_GET_ITEM(py_parents, i); + if (!py_str_to_git_oid(py_parent, &oid)) { + git_tree_close(tree); return free_parents(parents, i); } - py_parent = PyList_GET_ITEM(py_parents, i); - if (!py_str_to_git_oid(py_parent, parents[i])) + if (git_commit_lookup(&parents[i], self->repo, &oid)) { + git_tree_close(tree); return free_parents(parents, i); + } } err = git_commit_create(&oid, self->repo, update_ref, author, committer, - message, &tree_oid, parent_count, (const git_oid**)parents); + message, tree, parent_count, (const git_commit**)parents); + git_tree_close(tree); free_parents(parents, parent_count); if (err < 0) return Error_set(err); @@ -527,20 +535,29 @@ static PyObject * Repository_create_tag(Repository *self, PyObject *args) { char *tag_name, *message; git_signature *tagger; - git_oid target, oid; + git_oid oid; + git_object *target; int err, target_type; - char hex[GIT_OID_HEXSZ]; + char hex[GIT_OID_HEXSZ + 1]; if (!PyArg_ParseTuple(args, "sO&iO&s", &tag_name, - py_str_to_git_oid, &target, + py_str_to_git_oid, &oid, &target_type, signature_converter, &tagger, &message)) return NULL; - err = git_tag_create(&oid, self->repo, - tag_name, &target, target_type, tagger, message); + err = git_object_lookup(&target, self->repo, &oid, target_type); + if (err < 0) { + git_oid_fmt(hex, &oid); + hex[GIT_OID_HEXSZ] = '\0'; + return Error_set_str(err, hex); + } + + err = git_tag_create(&oid, self->repo, tag_name, target, tagger, message, + 0); + git_object_close(target); if (err < 0) return NULL; @@ -622,7 +639,7 @@ Repository_create_reference(Repository *self, PyObject *args) { return NULL; /* 2- Create the reference */ - err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid); + err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid, 0); if (err < 0) return Error_set(err); @@ -642,7 +659,7 @@ Repository_create_symbolic_reference(Repository *self, PyObject *args) { /* 2- Create the reference */ err = git_reference_create_symbolic(&c_reference, self->repo, c_name, - c_target); + c_target, 0); if (err < 0) return Error_set(err); @@ -2066,7 +2083,7 @@ Reference_rename(Reference *self, PyObject *py_name) { return NULL; /* 2- Rename */ - err = git_reference_rename(self->reference, c_name); + err = git_reference_rename(self->reference, c_name, 0); if (err < 0) return Error_set(err); From a139335098d3265dc962b921723a9db18e652f5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 14 Jul 2011 22:01:40 +0200 Subject: [PATCH 0033/2237] Fix memory leak in 'wrap_index_entry' (issue #18) --- pygit2.c | 1 - test/test_leak.py | 17 ----------------- 2 files changed, 18 deletions(-) delete mode 100644 test/test_leak.py diff --git a/pygit2.c b/pygit2.c index bd9bbc961..4c767dec9 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1655,7 +1655,6 @@ wrap_index_entry(git_index_entry *entry, Index *index) { py_entry->entry = entry; - Py_INCREF(py_entry); return (PyObject*)py_entry; } diff --git a/test/test_leak.py b/test/test_leak.py deleted file mode 100644 index 35259b801..000000000 --- a/test/test_leak.py +++ /dev/null @@ -1,17 +0,0 @@ -import unittest - -import utils - -class TestLeak(utils.RepoTestCase): - def test_index_iter(self): - index = self.repo.index - while True: - list(index) - - def test_index_getitem(self): - index = self.repo.index - while True: - index[0] - -if __name__ == '__main__': - unittest.main() From d0bc77612120ce31edcad7de836a14a9570b62b4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Jul 2011 12:51:03 +0200 Subject: [PATCH 0034/2237] Repository.write implemented Implemented Repository write function, using odb write streams Added simple test --- pygit2.c | 59 ++++++++++++++++++++++++++++++++++++++++- test/test_repository.py | 11 ++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index 3b3c74523..9ca6c1526 100644 --- a/pygit2.c +++ b/pygit2.c @@ -162,7 +162,7 @@ Error_set_py_obj(int err, PyObject *py_obj) { assert(err < 0); if (err == GIT_ENOTOID && !PyString_Check(py_obj)) { - PyErr_Format(PyExc_TypeError, "Git object id must be str, not %.200s", + PyErr_Format(PyExc_TypeError, "Git object id must be 40 byte hexadecimal str, or 20 byte binary str: %.200s", py_obj->ob_type->tp_name); return NULL; } else if (err == GIT_ENOTFOUND) { @@ -216,6 +216,18 @@ lookup_object(Repository *repo, const git_oid *oid, git_otype type) { return (PyObject*)py_obj; } +static git_otype +int_to_loose_object_type(int type_id) +{ + switch((git_otype)type_id) { + case GIT_OBJ_COMMIT: return GIT_OBJ_COMMIT; + case GIT_OBJ_TREE: return GIT_OBJ_TREE; + case GIT_OBJ_BLOB: return GIT_OBJ_BLOB; + case GIT_OBJ_TAG: return GIT_OBJ_TAG; + default: return GIT_OBJ_BAD; + } +} + static PyObject * wrap_reference(git_reference * c_reference) { @@ -254,6 +266,16 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) { return 1; } +static PyObject* +git_oid_to_py_string(git_oid* oid) +{ + char buf[GIT_OID_HEXSZ+1]; + if (strlen(git_oid_to_string(buf, sizeof(buf), oid)) != GIT_OID_HEXSZ) + return NULL; + + return PyString_FromStringAndSize(buf, GIT_OID_HEXSZ); +} + static int Repository_init(Repository *self, PyObject *args, PyObject *kwds) { char *path; @@ -334,6 +356,37 @@ Repository_read(Repository *self, PyObject *py_hex) { return tuple; } +static PyObject * +Repository_write(Repository *self, PyObject *args) +{ + int err; + git_oid oid; + git_odb_stream* stream; + + int type_id; + const char* buffer; + Py_ssize_t buflen; + + if (!PyArg_ParseTuple(args, "Is#", &type_id, &buffer, &buflen)) + return NULL; + + git_otype type = int_to_loose_object_type(type_id); + if (type == GIT_OBJ_BAD) + return Error_set_str(-100, "Invalid object type"); + + git_odb* odb = git_repository_database(self->repo); + + if ((err = git_odb_open_wstream(&stream, odb, buflen, type)) == GIT_SUCCESS) { + stream->write(stream, buffer, buflen); + err = stream->finalize_write(&oid, stream); + stream->free(stream); + } + if (err < 0) + return Error_set_str(err, "failed to write data"); + + return git_oid_to_py_string(&oid); +} + static PyObject * Repository_get_index(Repository *self, void *closure) { int err; @@ -665,6 +718,10 @@ static PyMethodDef Repository_methods[] = { "Generator that traverses the history starting from the given commit."}, {"read", (PyCFunction)Repository_read, METH_O, "Read raw object data from the repository."}, + {"write", (PyCFunction)Repository_write, METH_VARARGS, + "Write raw object data into the repository. First arg is the object type number, \n\ + the second one a buffer with data.\n\ + Return the hexadecimal sha of the created object"}, {"listall_references", (PyCFunction)Repository_listall_references, METH_VARARGS, "Return a list with all the references that can be found in a " diff --git a/test/test_repository.py b/test/test_repository.py index d584e46bc..064667450 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -53,6 +53,17 @@ def test_read(self): a2 = self.repo.read('7f129fd57e31e935c6d60a0c794efe4e6927664b') self.assertEqual((pygit2.GIT_OBJ_BLOB, 'a contents 2\n'), a2) + + def test_write(self): + data = "hello world" + # invalid object type + self.assertRaises(pygit2.GitError, self.repo.write, pygit2.GIT_OBJ_ANY, data) + + hex_sha = self.repo.write(pygit2.GIT_OBJ_BLOB, data) + self.assertEqual(len(hex_sha), 40) + + # works as buffer as well + self.assertEqual(hex_sha, self.repo.write(pygit2.GIT_OBJ_BLOB, buffer(data))) def test_contains(self): self.assertRaises(TypeError, lambda: 123 in self.repo) From dafe4b11b28e540a8e042a3b269516c70dd97645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 20 Jul 2011 14:50:19 +0200 Subject: [PATCH 0035/2237] Fix coding style - do not use tabs - remove trailing white spaces - lines are 79 chars max. --- pygit2.c | 48 +++++++++++++++++++++-------------------- test/test_repository.py | 18 ++++++++-------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/pygit2.c b/pygit2.c index a0b5d62f4..6122a46be 100644 --- a/pygit2.c +++ b/pygit2.c @@ -137,9 +137,9 @@ static PyObject * Error_set(int err) { assert(err < 0); if (err == GIT_ENOTFOUND) { - /* KeyError expects the arg to be the missing key. If the caller called - * this instead of Error_set_py_obj, it means we don't know the key, but - * nor should we use git_lasterror. */ + /* KeyError expects the arg to be the missing key. If the caller + * called this instead of Error_set_py_obj, it means we don't know + * the key, but nor should we use git_lasterror. */ PyErr_SetNone(PyExc_KeyError); return NULL; } else if (err == GIT_EOSERR) { @@ -223,16 +223,16 @@ lookup_object(Repository *repo, const git_oid *oid, git_otype type) { return (PyObject*)py_obj; } -static git_otype +static git_otype int_to_loose_object_type(int type_id) { - switch((git_otype)type_id) { - case GIT_OBJ_COMMIT: return GIT_OBJ_COMMIT; - case GIT_OBJ_TREE: return GIT_OBJ_TREE; - case GIT_OBJ_BLOB: return GIT_OBJ_BLOB; - case GIT_OBJ_TAG: return GIT_OBJ_TAG; - default: return GIT_OBJ_BAD; - } + switch((git_otype)type_id) { + case GIT_OBJ_COMMIT: return GIT_OBJ_COMMIT; + case GIT_OBJ_TREE: return GIT_OBJ_TREE; + case GIT_OBJ_BLOB: return GIT_OBJ_BLOB; + case GIT_OBJ_TAG: return GIT_OBJ_TAG; + default: return GIT_OBJ_BAD; + } } static PyObject * @@ -279,7 +279,7 @@ git_oid_to_py_string(git_oid* oid) char buf[GIT_OID_HEXSZ+1]; if (strlen(git_oid_to_string(buf, sizeof(buf), oid)) != GIT_OID_HEXSZ) return NULL; - + return PyString_FromStringAndSize(buf, GIT_OID_HEXSZ); } @@ -383,7 +383,8 @@ Repository_write(Repository *self, PyObject *args) git_odb* odb = git_repository_database(self->repo); - if ((err = git_odb_open_wstream(&stream, odb, buflen, type)) == GIT_SUCCESS) { + err = git_odb_open_wstream(&stream, odb, buflen, type); + if (err == GIT_SUCCESS) { stream->write(stream, buffer, buflen); err = stream->finalize_write(&oid, stream); stream->free(stream); @@ -743,21 +744,21 @@ static PyMethodDef Repository_methods[] = { {"read", (PyCFunction)Repository_read, METH_O, "Read raw object data from the repository."}, {"write", (PyCFunction)Repository_write, METH_VARARGS, - "Write raw object data into the repository. First arg is the object type number, \n\ - the second one a buffer with data.\n\ - Return the hexadecimal sha of the created object"}, + "Write raw object data into the repository. First arg is the object\n" + "type, the second one a buffer with data. Return the hexadecimal sha\n" + "of the created object."}, {"listall_references", (PyCFunction)Repository_listall_references, METH_VARARGS, - "Return a list with all the references that can be found in a " - "repository."}, + "Return a list with all the references in the repository."}, {"lookup_reference", (PyCFunction)Repository_lookup_reference, METH_O, "Lookup a reference by its name in a repository."}, - {"create_reference", (PyCFunction)Repository_create_reference, METH_VARARGS, + {"create_reference", (PyCFunction)Repository_create_reference, + METH_VARARGS, "Create a new reference \"name\" that points to the object given by its " "\"sha\"."}, {"create_symbolic_reference", (PyCFunction)Repository_create_symbolic_reference, METH_VARARGS, - "Create a new symbolic reference \"name\" that points to the reference " + "Create a new symbolic reference \"name\" that points to the reference\n" "\"target\"."}, {"packall_references", (PyCFunction)Repository_packall_references, METH_NOARGS, "Pack all the loose references in the repository."}, @@ -1230,8 +1231,8 @@ Tree_fix_index(Tree *self, PyObject *py_index) { return -1; } - /* This function is called via mp_subscript, which doesn't do negative index - * rewriting, so we have to do it manually. */ + /* This function is called via mp_subscript, which doesn't do negative + * index rewriting, so we have to do it manually. */ if (index < 0) index = len + index; return (int)index; @@ -2215,7 +2216,8 @@ Reference_get_sha(Reference *self) { if (oid == NULL) { PyErr_Format(PyExc_ValueError, - "sha is only available if the reference is direct (i.e. not symbolic)"); + "sha is only available if the reference is direct " + "(i.e. not symbolic)"); return NULL; } diff --git a/test/test_repository.py b/test/test_repository.py index 064667450..445c2e616 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -33,7 +33,7 @@ import unittest from os.path import join, abspath -import pygit2 +from pygit2 import GitError, GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT import utils A_HEX_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' @@ -49,21 +49,21 @@ def test_read(self): ab = self.repo.read(A_BIN_SHA) a = self.repo.read(A_HEX_SHA) self.assertEqual(ab, a) - self.assertEqual((pygit2.GIT_OBJ_BLOB, 'a contents\n'), a) + self.assertEqual((GIT_OBJ_BLOB, 'a contents\n'), a) a2 = self.repo.read('7f129fd57e31e935c6d60a0c794efe4e6927664b') - self.assertEqual((pygit2.GIT_OBJ_BLOB, 'a contents 2\n'), a2) - + self.assertEqual((GIT_OBJ_BLOB, 'a contents 2\n'), a2) + def test_write(self): data = "hello world" # invalid object type - self.assertRaises(pygit2.GitError, self.repo.write, pygit2.GIT_OBJ_ANY, data) + self.assertRaises(GitError, self.repo.write, GIT_OBJ_ANY, data) - hex_sha = self.repo.write(pygit2.GIT_OBJ_BLOB, data) + hex_sha = self.repo.write(GIT_OBJ_BLOB, data) self.assertEqual(len(hex_sha), 40) # works as buffer as well - self.assertEqual(hex_sha, self.repo.write(pygit2.GIT_OBJ_BLOB, buffer(data))) + self.assertEqual(hex_sha, self.repo.write(GIT_OBJ_BLOB, buffer(data))) def test_contains(self): self.assertRaises(TypeError, lambda: 123 in self.repo) @@ -77,13 +77,13 @@ def test_lookup_blob(self): a = self.repo[A_HEX_SHA] self.assertEqual('a contents\n', a.read_raw()) self.assertEqual(A_HEX_SHA, a.sha) - self.assertEqual(pygit2.GIT_OBJ_BLOB, a.type) + self.assertEqual(GIT_OBJ_BLOB, a.type) def test_lookup_commit(self): commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10' commit = self.repo[commit_sha] self.assertEqual(commit_sha, commit.sha) - self.assertEqual(pygit2.GIT_OBJ_COMMIT, commit.type) + self.assertEqual(GIT_OBJ_COMMIT, commit.type) self.assertEqual(('Second test data commit.\n\n' 'This commit has some additional text.\n'), commit.message) From d55ea4c102fae7deec5716c04f251af38a23278c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 20 Jul 2011 15:29:58 +0200 Subject: [PATCH 0036/2237] Reuse the function 'git_oid_to_py_str' --- pygit2.c | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/pygit2.c b/pygit2.c index 6122a46be..8cde55ed0 100644 --- a/pygit2.c +++ b/pygit2.c @@ -274,13 +274,12 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) { } static PyObject* -git_oid_to_py_string(git_oid* oid) +git_oid_to_py_str(const git_oid *oid) { - char buf[GIT_OID_HEXSZ+1]; - if (strlen(git_oid_to_string(buf, sizeof(buf), oid)) != GIT_OID_HEXSZ) - return NULL; + char hex[GIT_OID_HEXSZ]; - return PyString_FromStringAndSize(buf, GIT_OID_HEXSZ); + git_oid_fmt(hex, oid); + return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); } static int @@ -392,7 +391,7 @@ Repository_write(Repository *self, PyObject *args) if (err < 0) return Error_set_str(err, "failed to write data"); - return git_oid_to_py_string(&oid); + return git_oid_to_py_str(&oid); } static PyObject * @@ -540,7 +539,6 @@ Repository_create_commit(Repository *self, PyObject *args) { int parent_count; git_commit **parents; int err, i; - char hex[GIT_OID_HEXSZ]; if (!PyArg_ParseTuple(args, "zO&O&sO&O!", &update_ref, @@ -581,8 +579,7 @@ Repository_create_commit(Repository *self, PyObject *args) { if (err < 0) return Error_set(err); - git_oid_fmt(hex, &oid); - return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); + return git_oid_to_py_str(&oid); } static PyObject * @@ -615,8 +612,7 @@ Repository_create_tag(Repository *self, PyObject *args) { if (err < 0) return NULL; - git_oid_fmt(hex, &oid); - return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); + return git_oid_to_py_str(&oid); } static PyObject * @@ -849,15 +845,13 @@ Object_get_type(Object *self) { static PyObject * Object_get_sha(Object *self) { - const git_oid *id; - char hex[GIT_OID_HEXSZ]; + const git_oid *oid; - id = git_object_id(self->obj); - if (!id) + oid = git_object_id(self->obj); + if (!oid) Py_RETURN_NONE; - git_oid_fmt(hex, id); - return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); + return git_oid_to_py_str(oid); } static PyObject * @@ -1099,9 +1093,7 @@ TreeEntry_get_name(TreeEntry *self) { static PyObject * TreeEntry_get_sha(TreeEntry *self) { - char hex[GIT_OID_HEXSZ]; - git_oid_fmt(hex, git_tree_entry_id(self->entry)); - return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); + return git_oid_to_py_str(git_tree_entry_id(self->entry)); } static PyObject * @@ -1762,14 +1754,12 @@ static PyObject * Index_create_tree(Index *self) { git_oid oid; int err; - char hex[GIT_OID_HEXSZ]; err = git_tree_create_fromindex(&oid, self->index); if (err < 0) return Error_set(err); - git_oid_fmt(hex, &oid); - return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); + return git_oid_to_py_str(&oid); } static PyMethodDef Index_methods[] = { @@ -1917,10 +1907,7 @@ IndexEntry_get_path(IndexEntry *self) { static PyObject * IndexEntry_get_sha(IndexEntry *self) { - char hex[GIT_OID_HEXSZ]; - - git_oid_fmt(hex, &self->entry->oid); - return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); + return git_oid_to_py_str(&self->entry->oid); } static PyGetSetDef IndexEntry_getseters[] = { @@ -2208,7 +2195,6 @@ Reference_get_name(Reference *self) { static PyObject * Reference_get_sha(Reference *self) { - char hex[GIT_OID_HEXSZ]; const git_oid *oid; /* 1- Get the oid (only for "direct" references) */ @@ -2222,8 +2208,7 @@ Reference_get_sha(Reference *self) { } /* 2- Convert and return it */ - git_oid_fmt(hex, oid); - return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); + return git_oid_to_py_str(oid); } static int From 802015246db6ad73e5024f3d2f557c3bc4046938 Mon Sep 17 00:00:00 2001 From: Julien Miotte Date: Tue, 12 Jul 2011 18:42:43 +0200 Subject: [PATCH 0037/2237] Small improvements of the documentation (commit and iterable trees) --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0519880f5..daa2824f0 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Index read: Iterate over all entries of the index: - >>> for i in range(0, len(index)): + >>> for i in xrange(len(index)): ... entry = index[i] ... print entry.path, entry.sha @@ -65,6 +65,18 @@ Revision walking: >>> for commit in repo.walk(sha, GIT_SORT_TIME): ... print commit.sha +Read commit information: + + >>> master_ref = repo.lookup_reference("refs/heads/master") # Getting the Reference object + >>> commit = repo[master_ref.sha] + >>> [name, email, timestamp, tz_offset] = commit.author # Read the commit authored infos + >>> tree = commit.tree # Access the tree of the commit + +Iterate over all entries of the tree: + + >>> for entry in tree: + ... print entry.name, entry.sha, entry.attributes + CONTRIBUTING ============== From b62fcc8877d5b613f9ac859f393b34e6c542927b Mon Sep 17 00:00:00 2001 From: Julien Miotte Date: Thu, 21 Jul 2011 12:09:19 +0200 Subject: [PATCH 0038/2237] Purely cosmetic, alining all PyTypeObjects comments. --- pygit2.c | 822 +++++++++++++++++++++++++++---------------------------- 1 file changed, 411 insertions(+), 411 deletions(-) diff --git a/pygit2.c b/pygit2.c index 8cde55ed0..d3c8e4f46 100644 --- a/pygit2.c +++ b/pygit2.c @@ -790,44 +790,44 @@ static PyMappingMethods Repository_as_mapping = { static PyTypeObject RepositoryType = { PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "pygit2.Repository", /* tp_name */ - sizeof(Repository), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Repository_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - &Repository_as_sequence, /* tp_as_sequence */ - &Repository_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Git repository", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ + 0, /* ob_size */ + "pygit2.Repository", /* tp_name */ + sizeof(Repository), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Repository_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &Repository_as_sequence, /* tp_as_sequence */ + &Repository_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Git repository", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Repository_methods, /* tp_methods */ - 0, /* tp_members */ - Repository_getseters, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Repository_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Repository_methods, /* tp_methods */ + 0, /* tp_members */ + Repository_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Repository_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ }; static void @@ -897,44 +897,44 @@ static PyMethodDef Object_methods[] = { static PyTypeObject ObjectType = { PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "pygit2.Object", /*tp_name*/ - sizeof(Object), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)Object_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 */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "Object objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ + 0, /* ob_size */ + "pygit2.Object", /* tp_name */ + sizeof(Object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Object_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Object objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Object_methods, /* tp_methods */ - 0, /* tp_members */ - Object_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 */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Object_methods, /* tp_methods */ + 0, /* tp_members */ + Object_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 */ }; static PyObject * @@ -1035,44 +1035,44 @@ static PyGetSetDef Commit_getseters[] = { static PyTypeObject CommitType = { PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "pygit2.Commit", /*tp_name*/ - sizeof(Commit), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - 0, /*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 */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "Commit objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ + 0, /* ob_size */ + "pygit2.Commit", /* tp_name */ + sizeof(Commit), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* 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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Commit objects", /* 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 */ - Commit_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 */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + Commit_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 */ }; static void @@ -1119,44 +1119,44 @@ static PyMethodDef TreeEntry_methods[] = { static PyTypeObject TreeEntryType = { PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "pygit2.TreeEntry", /*tp_name*/ - sizeof(TreeEntry), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)TreeEntry_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 */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "TreeEntry objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ + 0, /* ob_size */ + "pygit2.TreeEntry", /* tp_name */ + sizeof(TreeEntry), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)TreeEntry_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "TreeEntry objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - TreeEntry_methods, /* tp_methods */ - 0, /* tp_members */ - TreeEntry_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 */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + TreeEntry_methods, /* tp_methods */ + 0, /* tp_members */ + TreeEntry_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 */ }; static Py_ssize_t @@ -1295,44 +1295,44 @@ static PyMappingMethods Tree_as_mapping = { static PyTypeObject TreeType = { PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "pygit2.Tree", /*tp_name*/ - sizeof(Tree), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - 0, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - &Tree_as_sequence, /*tp_as_sequence*/ - &Tree_as_mapping, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "Tree objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ + 0, /* ob_size */ + "pygit2.Tree", /* tp_name */ + sizeof(Tree), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &Tree_as_sequence, /* tp_as_sequence */ + &Tree_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Tree objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - (getiterfunc)Tree_iter, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* 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 */ + (getiterfunc)Tree_iter, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* 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 */ }; static void @@ -1355,10 +1355,10 @@ TreeIter_iternext(TreeIter *self) { static PyTypeObject TreeIterType = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - "pygit2.TreeIter", /* tp_name */ - sizeof(TreeIter), /* tp_basicsize */ + "pygit2.TreeIter", /* tp_name */ + sizeof(TreeIter), /* tp_basicsize */ 0, /* tp_itemsize */ - (destructor)TreeIter_dealloc , /* tp_dealloc */ + (destructor)TreeIter_dealloc , /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -1381,7 +1381,7 @@ static PyTypeObject TreeIterType = { 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc)TreeIter_iternext, /* tp_iternext */ + (iternextfunc)TreeIter_iternext, /* tp_iternext */ }; static PyGetSetDef Blob_getseters[] = { @@ -1391,44 +1391,44 @@ static PyGetSetDef Blob_getseters[] = { static PyTypeObject BlobType = { PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "pygit2.Blob", /*tp_name*/ - sizeof(Blob), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - 0, /*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 */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "Blob objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ + 0, /* ob_size */ + "pygit2.Blob", /* tp_name */ + sizeof(Blob), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* 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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Blob objects", /* 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 */ - Blob_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 */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + Blob_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 */ }; static void @@ -1491,44 +1491,44 @@ static PyGetSetDef Tag_getseters[] = { static PyTypeObject TagType = { PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "pygit2.Tag", /*tp_name*/ - sizeof(Tag), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)Tag_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 */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "Tag objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ + 0, /* ob_size */ + "pygit2.Tag", /* tp_name */ + sizeof(Tag), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Tag_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Tag objects", /* 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 */ - Tag_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 */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + Tag_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 */ }; static int @@ -1800,44 +1800,44 @@ static PyMappingMethods Index_as_mapping = { static PyTypeObject IndexType = { PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "pygit2.Index", /* tp_name */ - sizeof(Index), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Index_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - &Index_as_sequence, /* tp_as_sequence */ - &Index_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Index file", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ + 0, /* ob_size */ + "pygit2.Index", /* tp_name */ + sizeof(Index), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Index_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &Index_as_sequence, /* tp_as_sequence */ + &Index_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Index file", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - (getiterfunc)Index_iter, /* tp_iter */ - 0, /* tp_iternext */ - Index_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Index_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ + (getiterfunc)Index_iter, /* tp_iter */ + 0, /* tp_iternext */ + Index_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Index_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ }; @@ -1919,44 +1919,44 @@ static PyGetSetDef IndexEntry_getseters[] = { static PyTypeObject IndexEntryType = { PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "pygit2.IndexEntry", /* tp_name */ - sizeof(IndexEntry), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)IndexEntry_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - "Index entry", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ + 0, /* ob_size */ + "pygit2.IndexEntry", /* tp_name */ + sizeof(IndexEntry), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)IndexEntry_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Index entry", /* 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 */ - IndexEntry_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 */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + IndexEntry_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 */ }; static void @@ -2060,44 +2060,44 @@ static PyMethodDef Walker_methods[] = { static PyTypeObject WalkerType = { PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "pygit2.Walker", /* tp_name */ - sizeof(Walker), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Walker_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /* tp_flags */ - "Revision walker", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ + 0, /* ob_size */ + "pygit2.Walker", /* tp_name */ + sizeof(Walker), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Walker_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /* tp_flags */ + "Revision walker", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - (getiterfunc)Walker_iter, /* tp_iter */ - (iternextfunc)Walker_iternext, /* tp_iternext */ - Walker_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* 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 */ + (getiterfunc)Walker_iter, /* tp_iter */ + (iternextfunc)Walker_iternext, /* tp_iternext */ + Walker_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* 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 */ }; static PyObject * @@ -2263,44 +2263,44 @@ static PyGetSetDef Reference_getseters[] = { static PyTypeObject ReferenceType = { PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "pygit2.Reference", /* tp_name */ - sizeof(Reference), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* 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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - "Reference", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ + 0, /* ob_size */ + "pygit2.Reference", /* tp_name */ + sizeof(Reference), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* 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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Reference", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Reference_methods, /* tp_methods */ - 0, /* tp_members */ - Reference_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 */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Reference_methods, /* tp_methods */ + 0, /* tp_members */ + Reference_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 */ }; static PyObject * From d6a30142084a431cdb1b51ba6f5d3df6ae5ac809 Mon Sep 17 00:00:00 2001 From: Julien Miotte Date: Thu, 21 Jul 2011 12:07:56 +0200 Subject: [PATCH 0039/2237] According to PEP7, restricting lines to 79 chars, fixing code structure. --- pygit2.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/pygit2.c b/pygit2.c index d3c8e4f46..d489c45bf 100644 --- a/pygit2.c +++ b/pygit2.c @@ -42,8 +42,8 @@ typedef struct { PyObject *index; /* It will be None for a bare repository */ } Repository; -/* The structs for some of the object subtypes are identical except for the - * type of their object pointers. */ +/* The structs for some of the object subtypes are identical except for + * the type of their object pointers. */ #define OBJECT_STRUCT(_name, _ptr_type, _ptr_name) \ typedef struct {\ PyObject_HEAD\ @@ -138,11 +138,12 @@ Error_set(int err) { assert(err < 0); if (err == GIT_ENOTFOUND) { /* KeyError expects the arg to be the missing key. If the caller - * called this instead of Error_set_py_obj, it means we don't know - * the key, but nor should we use git_lasterror. */ + * called this instead of Error_set_py_obj, it means we don't + * know the key, but nor should we use git_lasterror. */ PyErr_SetNone(PyExc_KeyError); return NULL; - } else if (err == GIT_EOSERR) { + } + else if (err == GIT_EOSERR) { PyErr_SetFromErrno(GitError); return NULL; } @@ -169,10 +170,12 @@ Error_set_py_obj(int err, PyObject *py_obj) { assert(err < 0); if (err == GIT_ENOTOID && !PyString_Check(py_obj)) { - PyErr_Format(PyExc_TypeError, "Git object id must be 40 byte hexadecimal str, or 20 byte binary str: %.200s", + PyErr_Format(PyExc_TypeError, + "Git object id must be 40 byte hexadecimal str, or 20 byte binary str: %.200s", py_obj->ob_type->tp_name); return NULL; - } else if (err == GIT_ENOTFOUND) { + } + else if (err == GIT_ENOTFOUND) { /* KeyError expects the arg to be the missing key. */ PyErr_SetObject(PyExc_KeyError, py_obj); return NULL; @@ -261,7 +264,8 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) { if (PyString_Size(py_str) == 20) { git_oid_fromraw(oid, (const unsigned char*)hex_or_bin); err = 0; - } else { + } + else { err = git_oid_fromstr(oid, hex_or_bin); } @@ -412,10 +416,12 @@ Repository_get_index(Repository *self, void *closure) { py_index->index = index; py_index->own_obj = 0; self->index = (PyObject*)py_index; - } else if (err == GIT_EBAREINDEX) { + } + else if (err == GIT_EBAREINDEX) { Py_INCREF(Py_None); self->index = Py_None; - } else { + } + else { return Error_set(err); } } @@ -1218,7 +1224,8 @@ Tree_fix_index(Tree *self, PyObject *py_index) { if (index >= slen) { PyErr_SetObject(PyExc_IndexError, py_index); return -1; - } else if (index < -slen) { + } + else if (index < -slen) { PyErr_SetObject(PyExc_IndexError, py_index); return -1; } @@ -1266,9 +1273,11 @@ static TreeEntry * Tree_getitem(Tree *self, PyObject *value) { if (PyString_Check(value)) { return Tree_getitem_by_name(self, value); - } else if (PyInt_Check(value)) { + } + else if (PyInt_Check(value)) { return Tree_getitem_by_index(self, value); - } else { + } + else { PyErr_Format(PyExc_TypeError, "Tree entry index must be int or str, not %.200s", value->ob_type->tp_name); @@ -1639,7 +1648,8 @@ Index_get_position(Index *self, PyObject *value) { Error_set_str(idx, path); return -1; } - } else if (PyInt_Check(value)) { + } + else if (PyInt_Check(value)) { idx = (int)PyInt_AsLong(value); if (idx == -1 && PyErr_Occurred()) return -1; @@ -1647,7 +1657,8 @@ Index_get_position(Index *self, PyObject *value) { PyErr_SetObject(PyExc_ValueError, value); return -1; } - } else { + } + else { PyErr_Format(PyExc_TypeError, "Index entry key must be int or str, not %.200s", value->ob_type->tp_name); From d4ca441c5bba60fdec5444ac5c33aca86693f1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 21 Jul 2011 12:36:12 +0200 Subject: [PATCH 0040/2237] Documentation: we have an index iterator now --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index daa2824f0..37c2d0b0c 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,7 @@ Index read: Iterate over all entries of the index: - >>> for i in xrange(len(index)): - ... entry = index[i] + >>> for entry in index: ... print entry.path, entry.sha Index write: From f4e75e5b6f388bc4463c6890e520aed7d45e32e5 Mon Sep 17 00:00:00 2001 From: Julien Miotte Date: Thu, 21 Jul 2011 11:43:16 +0200 Subject: [PATCH 0041/2237] New Repository method: status(). Using git2's status methods to be able to read the status of a repository. The returned status is a dictionnary with file paths as keys and status flags as values. --- pygit2.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pygit2.c b/pygit2.c index d489c45bf..98af20ae2 100644 --- a/pygit2.c +++ b/pygit2.c @@ -736,6 +736,30 @@ Repository_packall_references(Repository *self, PyObject *args) { Py_RETURN_NONE; } +static int read_status_cb(const char *path, unsigned int status_flags, + void *payload_dict) +{ + /* This is the callback that will be called in git_status_foreach. It + * will be called for every path.*/ + PyObject *flags; + + flags = PyInt_FromLong((long) status_flags); + PyDict_SetItemString(payload_dict, path, flags); + + return GIT_SUCCESS; +} + +static PyObject * +Repository_status(Repository *self, PyObject *args) +{ + PyObject *payload_dict; + + payload_dict = PyDict_New(); + git_status_foreach(self->repo, read_status_cb, payload_dict); + + return payload_dict; +} + static PyMethodDef Repository_methods[] = { {"create_commit", (PyCFunction)Repository_create_commit, METH_VARARGS, "Create a new commit object, return its SHA."}, @@ -764,6 +788,9 @@ static PyMethodDef Repository_methods[] = { "\"target\"."}, {"packall_references", (PyCFunction)Repository_packall_references, METH_NOARGS, "Pack all the loose references in the repository."}, + {"status", (PyCFunction)Repository_status, METH_NOARGS, "Reads the " + "status of the repository and returns a dictionnary with file paths " + "as keys and status flags as values.\nSee pygit2.GIT_STATUS_*."}, {NULL} }; @@ -2443,4 +2470,23 @@ initpygit2(void) PyModule_AddIntConstant(m,"GIT_REF_OID", GIT_REF_OID); PyModule_AddIntConstant(m,"GIT_REF_SYMBOLIC", GIT_REF_SYMBOLIC); PyModule_AddIntConstant(m,"GIT_REF_PACKED", GIT_REF_PACKED); + + /** Git status flags **/ + PyModule_AddIntConstant(m,"GIT_STATUS_CURRENT", GIT_STATUS_CURRENT); + + /* Flags for index status */ + PyModule_AddIntConstant(m,"GIT_STATUS_INDEX_NEW", GIT_STATUS_INDEX_NEW); + PyModule_AddIntConstant(m,"GIT_STATUS_INDEX_MODIFIED", + GIT_STATUS_INDEX_MODIFIED); + PyModule_AddIntConstant(m,"GIT_STATUS_INDEX_DELETED" , + GIT_STATUS_INDEX_DELETED); + + /* Flags for worktree status */ + PyModule_AddIntConstant(m,"GIT_STATUS_WT_NEW", GIT_STATUS_WT_NEW); + PyModule_AddIntConstant(m,"GIT_STATUS_WT_MODIFIED" , + GIT_STATUS_WT_MODIFIED); + PyModule_AddIntConstant(m,"GIT_STATUS_WT_DELETED", GIT_STATUS_WT_DELETED); + + /* Flags for ignored files */ + PyModule_AddIntConstant(m,"GIT_STATUS_IGNORED", GIT_STATUS_IGNORED); } From d313af21a0f9507590befbafaceac1c3987e6d7c Mon Sep 17 00:00:00 2001 From: Julien Miotte Date: Thu, 21 Jul 2011 12:03:15 +0200 Subject: [PATCH 0042/2237] Adding a DirtyRepoTestCase to be able to test Repository.status(). Also, adding a dirty repository in the data directory. --- test/data/dirtyrepo.tar | Bin 0 -> 61440 bytes test/utils.py | 8 +++++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 test/data/dirtyrepo.tar diff --git a/test/data/dirtyrepo.tar b/test/data/dirtyrepo.tar new file mode 100644 index 0000000000000000000000000000000000000000..d9ac4425a040fbc3956ab5d17078f2ef2a891246 GIT binary patch literal 61440 zcmeHQd5k1ic^?}>=;n|eKtLemxo6lpcKW_%W<6_mXKnA=dw_R0PS|6%x~h7nJl$Qr zRn@yYcnt{=QbLFz2uLh&5=bBf#Gyzb1O<^e1c}2$Bv!CQ{s0n1#zBc45J{9o%J2K$ ztM2N%o9WrLO^v+n?y7px2x8TE*4XTVqq$kPUmyQDUsi`5d{D@Tu*a^n5tS^X5ezy*J1a&8ay*D7)|;E4;!Yj zUNbMRm#vDqUbPLYY?;Q+=RpK61??EPnHOMptb1l8-9Hr+`;8B z=$}s&iT>G4E*t9q9iV*VrY-s_``<7tre{ua`6s)7$oijd+W)y?hSvXdDj(8+WFqe1 z!DDKd7h3*1K;kpFX_x#f`+t&C8cY7Os|5>GjP%~ zA1eO=`#+y1`OoHyq5Myp3_alm=-(*e$8LlHi2kGc|BD&6|5Mpi=>P9YunAx6qW>0CY&`hRF|8L^7?gcQk|9fHOC+AvA{&zv}6&lBp{~R3u(Ek~-|HAnH?(<i(~gr~Q8>mxBEl=6~%z=XB8DG0X16nSk{FX!J++ zUxD}k*?c;r|L#+NYN{9gH%-l$Bo~ZB|03lH!vCMe{y&ue-RFS*^sj2JXF8J@!BG1T z!Y=cF3q?u)e1YVFLT&tLy@*;JVSe@$4bgZ{|>E?XND?s=I0PiJ#Y{jco5 z0z!hJ{+}=%dVxEm+m3lI8le&(*Ya#2UvlP3TXw8Rbum>L?I*H7b@_6UQvu^GtCo)lDbrrej!!`MQm&W>pfpUEMFz*)+NN zQR*x9rZTBi1Xrt;C+4i06uXe`(f~zeZ52e?b3aw?oD_LNc=Ne{I8hG z!xx13Pm1b4L;HXCd8-fowc-S$z@yXOkN+V27t(+CiH{!o(|@$M&?ti>`sW6m|B}i0 z=l>}BXK(-n_J3jgZxrnA?3pV!JoL!j(T7tv{j&DW<-hs<+unWW4{mzv+&zw6lPR612oVb`5azu|BqzHrwgdw>1JSHE_| zyi)kjZ+!pQ%RjYz|K+Vu?tSOyExaK~_lfwIHfBC{*QfvLi=Tb&`wPGGufO)`7k~HO554T*2j2G0 zt1o$uc>P@B*j-;o(?4C*3)x&@+0?acKD(rAOUnfi4#X8I!?rvFnp-~X@ZpGk-3zxl}9<OGE!uansVT?ZX zHwzP^{y6nNpns9h|IK9!;rS2Onf}X$nOQECGDh0Kv5dLWQYo{nnZ;sWGnVv>nOoBH z+2!;ogB-g4BiQfn|Cs)nbRiX<|2GP1cl?aM{$H4yihTNpCwB}l1n|tlKp*{Y)yk85 zBfyWd{^KoTmzJ*6`mgH$vU&Lb!uap*GeCd(=Z$<;U(RF;=F+l;60lMs zlg}?{hNdko<;!Fhl*?&-lo1YF|FQNr*MFuz;(rJihVj2qkh{ZYeE)AQV~pYd{oH#V z{lKH|e)OepeZu*{$;{tpo|=E|)F*CTe(Meg6E?r*80e6H!*q4Ws(UE?ncPrF{s-m% zrPIFuuetv(Q2l=x|DPNU`awEft7%o!5Uu*JeZn-5hwE)xE~nax*#J|_3eOhQ0{6{8 zqePgG+YiG}@9Q1%zjor-u_LF}Z$EV8)UlIyPH5=El9nH1g$DCHmT|KszYvV8taxc*O?65a3)9l2)>S8v3> z7apH|{9E*d|2Z}F{O3*G@S0zon);PLoSJ&h;N!FZ&S#1Hp9410h!!Q3*&ieizoP5Ve-}mA5SHA0Ke)e6z_q$UsR_{v*ye|Rp_9%G# zso=eDBzUiChbLda(l&k2<*B_2K7T6s=SG76>R#}DoDfu^`}d9qKNb879q@b8KdRsd z&W{H?(1zjir-G-A1n=c7c%8Ta+>(OJp9(I%kHDv|=!DzC!SUHAULoPiPX!m;Hv-(3 zxbpn;ZZQB}pZGZimp>I;*`u%-u(fU7L{6Cw`h4ueF`gZlR7yZpE zdZl5El-f1Y*oN4DDGK)c`#*X958eV6p8qovZg=|7w7{{iqTuAS2;PHgM#XEbXpv>YB=l1+tfUPHP!LNWy{+Xv(vMJP6wi*ZM;s>P2kBpY)5FK z?$}gZh?fn!US(TYHLwMSq`E7=WRXt7pHPVr2GmijfE1c-1_X{|Ryrel+3;Wf}-8OuI8b{k|SXf>hLVNTQ?3+D}rs_DAg zh6x)0FbD#S02n6J*simMp_rNHc*+)LO)FK%K)_6B1dE7dsi2l0^g`=cOAKO(XcQI( z&B!q?#p{{_`yw7!NTKsm#QvmVUP{&)m5Rvh-;<8QDyc;y+JgiJE*`;&jKk zHY>ug%4MORq`8^Zy0?v!7EM#gdb}1Kq+w?AhF-{J^ishrEo-Sl%FO3V6fKx_}&1XF(qTC2=xXJ3w zoFpq96Uw-A6KxO7Q@3l5?Yg2~(L6d#(l`7-60>FyU9?NKW%L@yK0Iq)g4qZlY}S$S z344%c9H20 z7T(kxGy{ui7$HXKN;dSYieV;*^k+nDrfs4zam%WR(Q%kDaZc>n6OBxZ<2Ja_@0x8; zoLK~uO5ZZUkeXvR09^xKg&Yc)4MGNc+g!xCN8YSUKxqD$-^GUIn8u<221VU=FaxV% z5nI^eq+J1(T#-)Ax+2+d9Ts8FnO_vHRnu`ICu2{z7aL@Ws{ueJ>1@tTNDevx7@Xc& zAuF7ah7hl{QG+lc5=>Z1L?e9GQcTjDIW!*EHQ0s*7@wFXZr5PN$K!O5b`QYb>H!gd zlbG>Y`D!#G#L>0&14oapuFky#G(;uB>yjBr$~%-$c?U%*$=${{;j&5TY}5Bv!z;!7%Q2&X=7~@IOUW# zJxzvnyLzx`Pa>&+24FPws2P_m*B6zh{s8~scq)-dbiz=gxK)D$vKFQ73X#XKiM#0+ z&B5?tVp7e~0_*w~_z4fLbWC2bQj*8^_L4^y;#M0{uxVBf<`q-twoE^c`+%6TrzWMF zq>RVIozreCztArvBJ8E#tgGWedo) zC}5N>qA5M722M*t2@5sb!MJs596Pf^+{oSp1m8ZzD$O2QLKRts%0_q9G|QHQV-aZ> z?(O02{H=%Y^i}@x`uJbW-^O2 z*%&Yq6%x^l^Y|uOb{fvYtcbXkC}~?FjseWN$@9r+k%aM4Zy)i<;#D%3U}>A^XS#1cz~C(3#uE%g#Lm`YA|Wo2Ip5Q~YZCZE zbUGqOgpuvpBW9T2_U;v{I2=AO7$FwsX;h}G=`^&HCl0FZ0yY)mPNbC`IDP8yiF?+M z9J>AZDRC7X2V1hn2sk^JSlEAdKCv*9Y@^YNIGaw^XK891&_E)7(G{`tGXNcvSh#vs zNCQ${+rOVLhsMgNi$~A(A6taVHeBuc=8o5X8pn|JKUKs#e>?m?R0bI4|Bgz;9XvPc z`Y$&If#oi+`U_9n!j`hM4JmE0j^MpJxijK)>aV{>w-%g$?`I6pkL;s|?`NRrr)3Yw z1&6otP(N6|oYEQ?(__?{JqZr2;tASg#of&~LRpP^lsDD!D^OCT50QM8BrLc| zxfpXJ_9gJ{Y|uk9_ow&9W?mP2AiA<6W!F>w$??^i%8qRRQRKf6sQ;1iUzCA|_Wur~ zdi18F%Ks?-Ux+5Gsutrstu(-xlH4u=V@NVJl+qmQFUi4&CMC(E(>(ZOPx+Vc@!7H5 z`~3`A|BI9~#55B`D_(lMn~GAyK&HnX&EvNrG~&VTsA zYpje@1SWtRYDZVH?TQIY1#2`m`2@oyzNC@}<*gw>tHKa2^&4QR0m;ld$gR4~vM3I9cs$^u9HvLh^2m)mIo{2>v zCh~hW<~Ka4l}*Hm*jSfQE5u`(W+Vbt`)EXnOUga-A?8b`LF2?&RJ51vcHh$XhmnYh z{d+QgLqw}uk;O$Byx`O=w?SJ+-t5D;<<11)w>K6uDRC>NwO`yS=YubDGW+NKCtHCE z^n{oZO+#TKCgQfdAX~S7OCVj-JWbKzl>xnWck0uDH3<_apg7utpz%1Vk3KqPUWQMl zq)T2I);jp?e6*862ej^dw5@Ll?|`N}!=^e?4V#)_Si|On9Q>6#MG9LY-XAC(jjH@V zx&(;q{DD_0eUV&9{%$Dnd~DwLs0A9bV=dNQv>IE0KPiSLinA9M7R2jSzF`2SbUef7 zEcG!qtx);x)Q z<+Jo9YcZx~$50vBcrzqk2~R6ea$!*#w!ktX%nf7_1Z@FabT{GuURS_YXYN8NP(JGuC5UaL`y054r0fL@_v%Ry|MKROXE9l^Y@jTJCHsD%I3oCjDA)kilc9uM$ zw8lZ(XyB%}E6&Yw_?;%-mR$;ltRu+nnZ8y8EUB;}cvmK72p(#~n$9?LdXR zf$KvNamcKg9vK93Q66K)14oIjQIIXwGz(D{=ZzRYU-nH|%G76e3>n}JhX#}gp zW;!)*3~xa7Q$u<&@*3MqOh4JfI7~Zu^gTGOa}oh%FK@L`ve$S6{5GlaN=vF!r+Abr zLqu(kxhYAl20fbzXmK*3;;Y$z)Na=}obT|!fOCK{6Bd8OZfh`Ns)LUoYDM0D(hFr z(gDYhOvcrO+raxFT({h)z%rJmA?e7fMmdw5Rxo-BM2q9d*SbQfl%yv^B{YzFJHOdm z{^7%R%o6e$CgKAgEdS|rKHug4#WAyC{CDTn9S)&g{@3jKwqtE<;x(-Lyue#o3ybvs zCA`$NX4zs*9M<5NvZZiiH^wRQl2c+bVTyKI@FxY+e>nBWe@JCtbJ8pAr{QqD&n_); z-ZkaFA~Hhx-|RRel|ud}c1QtgjV(jo74Ytda)1D%qgFtv9zTN%15#>>Q-;W|)v7l{ zP82eV;JkKGCkRk8Y53o`??&B40=`z|A1=JxNS(fnTs8PLwx9xONaW3($@mSFVJvgi zuH5_CO!!67uck}+RFop;?rBFY7QJ(#bS7YoS zkQ*@aY|26jd5;s6-0f<~Mj{)uQdv+f#RYK{+2ztu75gw?%A@9eKfLm$<1~;%-W;@H zdNOgHt1zfCLb*M-2ljFV25{SLNix=99Mi|(E@ZJRlH~)Y^l?h?20=g+M}}PgP7*fFU<`$ngjNB>d_b zf9_C8PGIW9;J0y}sB5!d2-v2&WpXfpM^RB5(l$Cmk{h{Y>wU-jH*rm`GCaLX^CCU6hm0 zpmv0V3~~?KK{{W9H0p8BjvH{IN%4?yQK_Kzr{fVUB2|VG*C=`6U{{*EtOY0c^VddCr z$q|#>*XS!q%2Z(HUK<$tJOEa;I*D&2k~}|mCKX@S;^lLD=S7m9kIZ4^TE$D%=jqo2 z!mXoLXfAoShOAOKy!1J~MQ(W-I~<5rG1mmy0TaoX^D`1~H&)f`f<^PVzywsDY4&8pwJ%aA=%pwsKO_6J( zd4XKWk@lx!az^MNeNLe;LXY7eMHdw7HWdsFiSkF)2%D_O%HA)Jffh|gqqp9A_Rxv7 z_1ljhicatFFB;|aVYRx;>^H>gHc+I6c{~IerQhb!Xtk{(!-oOhN zc!@a^%Ni6Twh%bh0gD583w1>57Yv$y{WcZ2j@&oePr}9v_TUd;k-A2AcwV~jCBS`X(}e{KMV##+%l2Q zjH)lkx1dnFN2S&hZTCWf@~T61Vg6iM-2%yKm5{B~`C|lSZ*N5cxdTL<30pOAw$9LYYKf6LVaP$KvNAZ4{CaSRJ;Kw!8;=Q4w^ z$_VW=!02sXB6!8wAnkZq_I zR5b%`Dn)|Wr-r~CDm7%flB`gXdKP@a`R!nBzgiU|mBjRCfr1R1c#5XLB3N!XL~*JX zMu{w>OJY_Tou18|lk#A3ksZy!YCy@YUr!7L0{t(Gq2}R8!xn2(?r#B^+bdP$4+4ZZ zYEopmLiPAuvy00D#K3C!)uJJSelj^zPW4%Bo z8S7|26oLLT%_SfjjUHIU6lncg;|*W?A`aSN+L`VBLBP(9e{_!KSBRTP#X z0+z-JP0@%SI_6!YzcD&5u}LddpC=Sg@Dp-Ff}@L&oko=_I9iIg+l02j1mVn}$6M12 z4kpt*7>U?Kf`MVA&YmZy2vrGBJ*aL+Bq4CsH2NS1VtqrTJvAn|A@cE2v|uLv8&-Wn{)Bj@j5C zzp(2Wkam@XiDkKpYuy=#oQX)b?0($70~2kot$13~a``OYMrWi;S)8k47R!b5@=`9B zE^GP1a;{()x>3+FDN$G|7qwD3XX1V)vy>_>EiISxnUbE$Wli14rc1@;@=`iSTGCBQ zPp4EZj6N4dy|98|HJ4OD5GNiX#Kwp5EYjAh?Z7*!#Mq|>gkj{J2T+hAZ=)78`829X z>9i0eSvzLZu=OPLF55$)NgA3WNOeRk1$eSz*!$?>H$8P-Nf1KCy(c%_}`lldKnQ2^wWROpDXO zLrr1rlRFQ{$LR%FAlnM#dmUatbU4JP2}u(bt8OzWtBI0^y#OLov?6Z6yX0tKP*ouZNj|9l6XOnz+UjRrj5_+j?I7LKBo z^|vt@0h!|y1!m8;;dMo*6AkF_DvH4%6oc6JMG@C2C<7LVBLa{-e?~;kEu`uHNdsqD zAspEjTRSv(94f|v5Zkqo@j-p`)c?HxOJi#S=KnDLpUNQFqoe*m6YBp#RGZwrQT4x4 z5Xw%67zFHd*w*r}JBk)eLk6oQ96P4fT=MYI0nj9^ZsK$n z^>r7X$&ul@LF$ly%8;1&Sjhex^!`66fDZZ3(ksEj^FJm|iEe<{k8&Aq$ELLV!SG6m xfe-^B20{#k7zi;CVj#poh=C9TAqGMWgct}h5Mm(2K!|}510e=N3_PPS@c#yZon-(3 literal 0 HcmV?d00001 diff --git a/test/utils.py b/test/utils.py index 324569425..0fcfc1cf8 100644 --- a/test/utils.py +++ b/test/utils.py @@ -66,8 +66,10 @@ def setUp(self): class RepoTestCase(BaseTestCase): + repo_dir = 'testrepo' + def setUp(self): - repo_dir = 'testrepo' + repo_dir = self.repo_dir repo_path = os.path.join(os.path.dirname(__file__), 'data', repo_dir) temp_dir = tempfile.mkdtemp() tar = tarfile.open(repo_path + '.tar') @@ -76,3 +78,7 @@ def setUp(self): self._temp_dir = temp_dir temp_repo_path = os.path.join(temp_dir, repo_dir, '.git') self.repo = pygit2.Repository(temp_repo_path) + +class DirtyRepoTestCase(RepoTestCase): + + repo_dir = 'dirtyrepo' From 561a01d5fcd695fed25724bd11aec466a19a037a Mon Sep 17 00:00:00 2001 From: Julien Miotte Date: Thu, 21 Jul 2011 12:07:10 +0200 Subject: [PATCH 0043/2237] New test for Repository.status(). --- test/__init__.py | 2 +- test/test_status.py | 79 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 test/test_status.py diff --git a/test/__init__.py b/test/__init__.py index cebf864d1..ac535258d 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -36,7 +36,7 @@ names = ['blob', 'commit', 'index', 'refs', 'repository', 'revwalk', 'tag', - 'tree'] + 'tree', 'status'] def test_suite(): modules = ['test.test_%s' % n for n in names] return unittest.defaultTestLoader.loadTestsFromNames(modules) diff --git a/test/test_status.py b/test/test_status.py new file mode 100644 index 000000000..41dc4dbea --- /dev/null +++ b/test/test_status.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2011 Julien Miotte +# +# 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. + +"""Tests for revision walk.""" + +__author__ = 'mike.perdide@gmail.com (Julien Miotte)' + +import unittest + +import pygit2 +import utils + +EXPECTED = { + "current_file": pygit2.GIT_STATUS_CURRENT, + "file_deleted": pygit2.GIT_STATUS_WT_DELETED, + "modified_file": pygit2.GIT_STATUS_WT_MODIFIED, + "new_file": pygit2.GIT_STATUS_WT_NEW, + + "staged_changes": pygit2.GIT_STATUS_INDEX_MODIFIED, + "staged_changes_file_deleted": pygit2.GIT_STATUS_INDEX_MODIFIED | + pygit2.GIT_STATUS_WT_DELETED, + "staged_changes_file_modified": pygit2.GIT_STATUS_INDEX_MODIFIED | + pygit2.GIT_STATUS_WT_MODIFIED, + + "staged_delete": pygit2.GIT_STATUS_INDEX_DELETED, + "staged_delete_file_modified": pygit2.GIT_STATUS_INDEX_DELETED | + pygit2.GIT_STATUS_WT_NEW, + "staged_new": pygit2.GIT_STATUS_INDEX_NEW, + + "staged_new_file_deleted": pygit2.GIT_STATUS_INDEX_NEW | + pygit2.GIT_STATUS_WT_DELETED, + "staged_new_file_modified": pygit2.GIT_STATUS_INDEX_NEW | + pygit2.GIT_STATUS_WT_MODIFIED, + + "subdir/current_file": pygit2.GIT_STATUS_CURRENT, + "subdir/deleted_file": pygit2.GIT_STATUS_WT_DELETED, + "subdir/modified_file": pygit2.GIT_STATUS_WT_MODIFIED, + "subdir/new_file": pygit2.GIT_STATUS_WT_NEW, +} + +class StatusTest(utils.DirtyRepoTestCase): + + def test_status(self): + """ + For every file in the status, check that the flags are correct. + """ + git_status = self.repo.status() + for filepath, status in git_status.items(): + self.assertTrue(filepath in git_status) + self.assertEqual(status, git_status[filepath]) + + +if __name__ == '__main__': + unittest.main() From 1c9602e58ee3a7479c14d67434dcfc312fa7c02b Mon Sep 17 00:00:00 2001 From: Julien Miotte Date: Thu, 28 Jul 2011 11:26:02 +0200 Subject: [PATCH 0044/2237] Following PEP7 recommendations on function definition style. --- pygit2.c | 273 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 182 insertions(+), 91 deletions(-) diff --git a/pygit2.c b/pygit2.c index d489c45bf..614dc9564 100644 --- a/pygit2.c +++ b/pygit2.c @@ -116,7 +116,8 @@ static PyTypeObject ReferenceType; static PyObject *GitError; static PyObject * -Error_type(int err) { +Error_type(int err) +{ switch (err) { case GIT_ENOTFOUND: return PyExc_KeyError; @@ -134,7 +135,8 @@ Error_type(int err) { } static PyObject * -Error_set(int err) { +Error_set(int err) +{ assert(err < 0); if (err == GIT_ENOTFOUND) { /* KeyError expects the arg to be the missing key. If the caller @@ -152,7 +154,8 @@ Error_set(int err) { } static PyObject * -Error_set_str(int err, const char *str) { +Error_set_str(int err, const char *str) +{ if (err == GIT_ENOTFOUND) { /* KeyError expects the arg to be the missing key. */ PyErr_Format(PyExc_KeyError, "%s", str); @@ -163,7 +166,8 @@ Error_set_str(int err, const char *str) { } static PyObject * -Error_set_py_obj(int err, PyObject *py_obj) { +Error_set_py_obj(int err, PyObject *py_obj) +{ PyObject *py_str; char *str; @@ -188,7 +192,8 @@ Error_set_py_obj(int err, PyObject *py_obj) { } static PyObject * -lookup_object(Repository *repo, const git_oid *oid, git_otype type) { +lookup_object(Repository *repo, const git_oid *oid, git_otype type) +{ int err; char hex[GIT_OID_HEXSZ + 1]; git_object *obj; @@ -251,7 +256,8 @@ wrap_reference(git_reference * c_reference) } static int -py_str_to_git_oid(PyObject *py_str, git_oid *oid) { +py_str_to_git_oid(PyObject *py_str, git_oid *oid) +{ const char *hex_or_bin; int err; @@ -287,7 +293,8 @@ git_oid_to_py_str(const git_oid *oid) } static int -Repository_init(Repository *self, PyObject *args, PyObject *kwds) { +Repository_init(Repository *self, PyObject *args, PyObject *kwds) +{ char *path; int err; @@ -310,7 +317,8 @@ Repository_init(Repository *self, PyObject *args, PyObject *kwds) { } static void -Repository_dealloc(Repository *self) { +Repository_dealloc(Repository *self) +{ if (self->repo) git_repository_free(self->repo); Py_XDECREF(self->index); @@ -318,7 +326,8 @@ Repository_dealloc(Repository *self) { } static int -Repository_contains(Repository *self, PyObject *value) { +Repository_contains(Repository *self, PyObject *value) +{ git_oid oid; if (!py_str_to_git_oid(value, &oid)) @@ -328,7 +337,8 @@ Repository_contains(Repository *self, PyObject *value) { } static PyObject * -Repository_getitem(Repository *self, PyObject *value) { +Repository_getitem(Repository *self, PyObject *value) +{ git_oid oid; if (!py_str_to_git_oid(value, &oid)) @@ -339,12 +349,14 @@ Repository_getitem(Repository *self, PyObject *value) { static int Repository_read_raw(git_odb_object **obj, git_repository *repo, - const git_oid *oid) { + const git_oid *oid) +{ return git_odb_read(obj, git_repository_database(repo), oid); } static PyObject * -Repository_read(Repository *self, PyObject *py_hex) { +Repository_read(Repository *self, PyObject *py_hex) +{ git_oid oid; int err; git_odb_object *obj; @@ -399,7 +411,8 @@ Repository_write(Repository *self, PyObject *args) } static PyObject * -Repository_get_index(Repository *self, void *closure) { +Repository_get_index(Repository *self, void *closure) +{ int err; git_index *index; Index *py_index; @@ -431,7 +444,8 @@ Repository_get_index(Repository *self, void *closure) { } static PyObject * -Repository_get_path(Repository *self, void *closure) { +Repository_get_path(Repository *self, void *closure) +{ const char *c_path; c_path = git_repository_path(self->repo, GIT_REPO_PATH); @@ -439,7 +453,8 @@ Repository_get_path(Repository *self, void *closure) { } static PyObject * -Repository_get_workdir(Repository *self, void *closure) { +Repository_get_workdir(Repository *self, void *closure) +{ const char *c_path; c_path = git_repository_path(self->repo, GIT_REPO_PATH_WORKDIR); @@ -500,13 +515,15 @@ Repository_walk(Repository *self, PyObject *args) } static PyObject * -build_person(const git_signature *signature) { +build_person(const git_signature *signature) +{ return Py_BuildValue("(ssLi)", signature->name, signature->email, signature->when.time, signature->when.offset); } static int -signature_converter(PyObject *value, git_signature **out) { +signature_converter(PyObject *value, git_signature **out) +{ char *name, *email; long long time; int offset; @@ -526,7 +543,8 @@ signature_converter(PyObject *value, git_signature **out) { } static PyObject * -free_parents(git_commit **parents, int n) { +free_parents(git_commit **parents, int n) +{ int i; for (i = 0; i < n; i++) @@ -536,7 +554,8 @@ free_parents(git_commit **parents, int n) { } static PyObject * -Repository_create_commit(Repository *self, PyObject *args) { +Repository_create_commit(Repository *self, PyObject *args) +{ git_signature *author, *committer; char *message, *update_ref; git_oid oid; @@ -589,7 +608,8 @@ Repository_create_commit(Repository *self, PyObject *args) { } static PyObject * -Repository_create_tag(Repository *self, PyObject *args) { +Repository_create_tag(Repository *self, PyObject *args) +{ char *tag_name, *message; git_signature *tagger; git_oid oid; @@ -622,7 +642,8 @@ Repository_create_tag(Repository *self, PyObject *args) { } static PyObject * -Repository_listall_references(Repository *self, PyObject *args) { +Repository_listall_references(Repository *self, PyObject *args) +{ unsigned list_flags=GIT_REF_LISTALL; git_strarray c_result; PyObject *py_result, *py_string; @@ -663,7 +684,8 @@ Repository_listall_references(Repository *self, PyObject *args) { } static PyObject * -Repository_lookup_reference(Repository *self, PyObject *py_name) { +Repository_lookup_reference(Repository *self, PyObject *py_name) +{ git_reference *c_reference; char *c_name; int err; @@ -683,7 +705,8 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) { } static PyObject * -Repository_create_reference(Repository *self, PyObject *args) { +Repository_create_reference(Repository *self, PyObject *args) +{ git_reference *c_reference; char *c_name; git_oid oid; @@ -704,7 +727,8 @@ Repository_create_reference(Repository *self, PyObject *args) { } static PyObject * -Repository_create_symbolic_reference(Repository *self, PyObject *args) { +Repository_create_symbolic_reference(Repository *self, PyObject *args) +{ git_reference *c_reference; char *c_name, *c_target; int err; @@ -724,7 +748,8 @@ Repository_create_symbolic_reference(Repository *self, PyObject *args) { } static PyObject * -Repository_packall_references(Repository *self, PyObject *args) { +Repository_packall_references(Repository *self, PyObject *args) +{ int err; /* 1- Pack */ @@ -845,12 +870,14 @@ Object_dealloc(Object* self) } static PyObject * -Object_get_type(Object *self) { +Object_get_type(Object *self) +{ return PyInt_FromLong(git_object_type(self->obj)); } static PyObject * -Object_get_sha(Object *self) { +Object_get_sha(Object *self) +{ const git_oid *oid; oid = git_object_id(self->obj); @@ -861,7 +888,8 @@ Object_get_sha(Object *self) { } static PyObject * -Object_read_raw(Object *self) { +Object_read_raw(Object *self) +{ const git_oid *id; git_odb_object *obj; int err; @@ -944,36 +972,42 @@ static PyTypeObject ObjectType = { }; static PyObject * -Commit_get_message_short(Commit *commit) { +Commit_get_message_short(Commit *commit) +{ return PyString_FromString(git_commit_message_short(commit->commit)); } static PyObject * -Commit_get_message(Commit *commit) { +Commit_get_message(Commit *commit) +{ return PyString_FromString(git_commit_message(commit->commit)); } static PyObject * -Commit_get_commit_time(Commit *commit) { +Commit_get_commit_time(Commit *commit) +{ return PyLong_FromLong(git_commit_time(commit->commit)); } static PyObject * -Commit_get_committer(Commit *commit) { +Commit_get_committer(Commit *commit) +{ const git_signature *signature = git_commit_committer(commit->commit); return build_person(signature); } static PyObject * -Commit_get_author(Commit *commit) { +Commit_get_author(Commit *commit) +{ const git_signature *signature = git_commit_author(commit->commit); return build_person(signature); } static PyObject * -Commit_get_tree(Commit *commit) { +Commit_get_tree(Commit *commit) +{ git_tree *tree; Tree *py_tree; int err; @@ -1082,28 +1116,33 @@ static PyTypeObject CommitType = { }; static void -TreeEntry_dealloc(TreeEntry *self) { +TreeEntry_dealloc(TreeEntry *self) +{ Py_XDECREF(self->tree); self->ob_type->tp_free((PyObject *)self); } static PyObject * -TreeEntry_get_attributes(TreeEntry *self) { +TreeEntry_get_attributes(TreeEntry *self) +{ return PyInt_FromLong(git_tree_entry_attributes(self->entry)); } static PyObject * -TreeEntry_get_name(TreeEntry *self) { +TreeEntry_get_name(TreeEntry *self) +{ return PyString_FromString(git_tree_entry_name(self->entry)); } static PyObject * -TreeEntry_get_sha(TreeEntry *self) { +TreeEntry_get_sha(TreeEntry *self) +{ return git_oid_to_py_str(git_tree_entry_id(self->entry)); } static PyObject * -TreeEntry_to_object(TreeEntry *self) { +TreeEntry_to_object(TreeEntry *self) +{ const git_oid *entry_oid; entry_oid = git_tree_entry_id(self->entry); @@ -1166,13 +1205,15 @@ static PyTypeObject TreeEntryType = { }; static Py_ssize_t -Tree_len(Tree *self) { +Tree_len(Tree *self) +{ assert(self->tree); return (Py_ssize_t)git_tree_entrycount(self->tree); } static int -Tree_contains(Tree *self, PyObject *py_name) { +Tree_contains(Tree *self, PyObject *py_name) +{ char *name; name = PyString_AsString(py_name); @@ -1183,7 +1224,8 @@ Tree_contains(Tree *self, PyObject *py_name) { } static TreeEntry * -wrap_tree_entry(const git_tree_entry *entry, Tree *tree) { +wrap_tree_entry(const git_tree_entry *entry, Tree *tree) +{ TreeEntry *py_entry = NULL; py_entry = (TreeEntry*)TreeEntryType.tp_alloc(&TreeEntryType, 0); if (!py_entry) @@ -1196,7 +1238,8 @@ wrap_tree_entry(const git_tree_entry *entry, Tree *tree) { } static TreeEntry * -Tree_getitem_by_name(Tree *self, PyObject *py_name) { +Tree_getitem_by_name(Tree *self, PyObject *py_name) +{ char *name; const git_tree_entry *entry; @@ -1210,7 +1253,8 @@ Tree_getitem_by_name(Tree *self, PyObject *py_name) { } static int -Tree_fix_index(Tree *self, PyObject *py_index) { +Tree_fix_index(Tree *self, PyObject *py_index) +{ long index; size_t len; long slen; @@ -1238,7 +1282,8 @@ Tree_fix_index(Tree *self, PyObject *py_index) { } static PyObject * -Tree_iter(Tree *self) { +Tree_iter(Tree *self) +{ TreeIter *iter; iter = PyObject_New(TreeIter, &TreeIterType); @@ -1253,7 +1298,8 @@ Tree_iter(Tree *self) { } static TreeEntry * -Tree_getitem_by_index(Tree *self, PyObject *py_index) { +Tree_getitem_by_index(Tree *self, PyObject *py_index) +{ int index; const git_tree_entry *entry; @@ -1270,7 +1316,8 @@ Tree_getitem_by_index(Tree *self, PyObject *py_index) { } static TreeEntry * -Tree_getitem(Tree *self, PyObject *value) { +Tree_getitem(Tree *self, PyObject *value) +{ if (PyString_Check(value)) { return Tree_getitem_by_name(self, value); } @@ -1345,13 +1392,15 @@ static PyTypeObject TreeType = { }; static void -TreeIter_dealloc(TreeIter *self) { +TreeIter_dealloc(TreeIter *self) +{ Py_CLEAR(self->owner); PyObject_Del(self); } static TreeEntry * -TreeIter_iternext(TreeIter *self) { +TreeIter_iternext(TreeIter *self) +{ const git_tree_entry *tree_entry; tree_entry = git_tree_entry_byindex(self->owner->tree, self->i); @@ -1441,13 +1490,15 @@ static PyTypeObject BlobType = { }; static void -Tag_dealloc(Tag *self) { +Tag_dealloc(Tag *self) +{ Py_XDECREF(self->target); self->ob_type->tp_free((PyObject*)self); } static PyObject * -Tag_get_target(Tag *self) { +Tag_get_target(Tag *self) +{ const git_oid *target_oid; git_otype target_type; @@ -1464,7 +1515,8 @@ Tag_get_target(Tag *self) { } static PyObject * -Tag_get_name(Tag *self) { +Tag_get_name(Tag *self) +{ const char *name; name = git_tag_name(self->tag); if (!name) @@ -1473,7 +1525,8 @@ Tag_get_name(Tag *self) { } static PyObject * -Tag_get_tagger(Tag *tag) { +Tag_get_tagger(Tag *tag) +{ const git_signature *signature = git_tag_tagger(tag->tag); if (!signature) Py_RETURN_NONE; @@ -1481,7 +1534,8 @@ Tag_get_tagger(Tag *tag) { } static PyObject * -Tag_get_message(Tag *self) { +Tag_get_message(Tag *self) +{ const char *message; message = git_tag_message(self->tag); if (!message) @@ -1541,7 +1595,8 @@ static PyTypeObject TagType = { }; static int -Index_init(Index *self, PyObject *args, PyObject *kwds) { +Index_init(Index *self, PyObject *args, PyObject *kwds) +{ char *path; int err; @@ -1574,7 +1629,8 @@ Index_dealloc(Index* self) } static PyObject * -Index_add(Index *self, PyObject *args) { +Index_add(Index *self, PyObject *args) +{ int err; const char *path; int stage=0; @@ -1590,13 +1646,15 @@ Index_add(Index *self, PyObject *args) { } static PyObject * -Index_clear(Index *self) { +Index_clear(Index *self) +{ git_index_clear(self->index); Py_RETURN_NONE; } static PyObject * -Index_find(Index *self, PyObject *py_path) { +Index_find(Index *self, PyObject *py_path) +{ char *path; long idx; @@ -1612,7 +1670,8 @@ Index_find(Index *self, PyObject *py_path) { } static PyObject * -Index_read(Index *self) { +Index_read(Index *self) +{ int err; err = git_index_read(self->index); @@ -1623,7 +1682,8 @@ Index_read(Index *self) { } static PyObject * -Index_write(Index *self) { +Index_write(Index *self) +{ int err; err = git_index_write(self->index); @@ -1635,7 +1695,8 @@ Index_write(Index *self) { /* This is an internal function, used by Index_getitem and Index_setitem */ static int -Index_get_position(Index *self, PyObject *value) { +Index_get_position(Index *self, PyObject *value) +{ char *path; int idx; @@ -1669,7 +1730,8 @@ Index_get_position(Index *self, PyObject *value) { } static int -Index_contains(Index *self, PyObject *value) { +Index_contains(Index *self, PyObject *value) +{ char *path; int idx; @@ -1688,7 +1750,8 @@ Index_contains(Index *self, PyObject *value) { } static PyObject * -Index_iter(Index *self) { +Index_iter(Index *self) +{ IndexIter *iter; iter = PyObject_New(IndexIter, &IndexIterType); @@ -1702,12 +1765,14 @@ Index_iter(Index *self) { } static Py_ssize_t -Index_len(Index *self) { +Index_len(Index *self) +{ return (Py_ssize_t)git_index_entrycount(self->index); } static PyObject * -wrap_index_entry(git_index_entry *entry, Index *index) { +wrap_index_entry(git_index_entry *entry, Index *index) +{ IndexEntry *py_entry; py_entry = (IndexEntry*)IndexEntryType.tp_alloc(&IndexEntryType, 0); @@ -1720,7 +1785,8 @@ wrap_index_entry(git_index_entry *entry, Index *index) { } static PyObject * -Index_getitem(Index *self, PyObject *value) { +Index_getitem(Index *self, PyObject *value) +{ int idx; git_index_entry *index_entry; @@ -1738,7 +1804,8 @@ Index_getitem(Index *self, PyObject *value) { } static int -Index_setitem(Index *self, PyObject *key, PyObject *value) { +Index_setitem(Index *self, PyObject *key, PyObject *value) +{ int err; int idx; @@ -1762,7 +1829,8 @@ Index_setitem(Index *self, PyObject *key, PyObject *value) { } static PyObject * -Index_create_tree(Index *self) { +Index_create_tree(Index *self) +{ git_oid oid; int err; @@ -1853,13 +1921,15 @@ static PyTypeObject IndexType = { static void -IndexIter_dealloc(IndexIter *self) { +IndexIter_dealloc(IndexIter *self) +{ Py_CLEAR(self->owner); PyObject_Del(self); } static PyObject * -IndexIter_iternext(IndexIter *self) { +IndexIter_iternext(IndexIter *self) +{ git_index_entry *index_entry; index_entry = git_index_get(self->owner->index, self->i); @@ -1902,22 +1972,26 @@ static PyTypeObject IndexIterType = { }; static void -IndexEntry_dealloc(IndexEntry *self) { +IndexEntry_dealloc(IndexEntry *self) +{ self->ob_type->tp_free((PyObject*)self); } static PyObject * -IndexEntry_get_mode(IndexEntry *self) { +IndexEntry_get_mode(IndexEntry *self) +{ return PyInt_FromLong(self->entry->mode); } static PyObject * -IndexEntry_get_path(IndexEntry *self) { +IndexEntry_get_path(IndexEntry *self) +{ return PyString_FromString(self->entry->path); } static PyObject * -IndexEntry_get_sha(IndexEntry *self) { +IndexEntry_get_sha(IndexEntry *self) +{ return git_oid_to_py_str(&self->entry->oid); } @@ -1971,14 +2045,16 @@ static PyTypeObject IndexEntryType = { }; static void -Walker_dealloc(Walker *self) { +Walker_dealloc(Walker *self) +{ git_revwalk_free(self->walk); Py_DECREF(self->repo); self->ob_type->tp_free((PyObject*)self); } static PyObject * -Walker_hide(Walker *self, PyObject *py_hex) { +Walker_hide(Walker *self, PyObject *py_hex) +{ int err; git_oid oid; @@ -1993,7 +2069,8 @@ Walker_hide(Walker *self, PyObject *py_hex) { } static PyObject * -Walker_push(Walker *self, PyObject *py_hex) { +Walker_push(Walker *self, PyObject *py_hex) +{ int err; git_oid oid; @@ -2008,7 +2085,8 @@ Walker_push(Walker *self, PyObject *py_hex) { } static PyObject * -Walker_sort(Walker *self, PyObject *py_sort_mode) { +Walker_sort(Walker *self, PyObject *py_sort_mode) +{ int sort_mode; sort_mode = (int)PyInt_AsLong(py_sort_mode); @@ -2021,19 +2099,22 @@ Walker_sort(Walker *self, PyObject *py_sort_mode) { } static PyObject * -Walker_reset(Walker *self) { +Walker_reset(Walker *self) +{ git_revwalk_reset(self->walk); Py_RETURN_NONE; } static PyObject * -Walker_iter(Walker *self) { +Walker_iter(Walker *self) +{ Py_INCREF(self); return (PyObject*)self; } static PyObject * -Walker_iternext(Walker *self) { +Walker_iternext(Walker *self) +{ int err; git_commit *commit; Commit *py_commit; @@ -2112,7 +2193,8 @@ static PyTypeObject WalkerType = { }; static PyObject * -Reference_delete(Reference *self, PyObject *args) { +Reference_delete(Reference *self, PyObject *args) +{ int err; /* 1- Delete the reference */ @@ -2128,7 +2210,8 @@ Reference_delete(Reference *self, PyObject *args) { } static PyObject * -Reference_rename(Reference *self, PyObject *py_name) { +Reference_rename(Reference *self, PyObject *py_name) +{ char *c_name; int err; @@ -2147,7 +2230,8 @@ Reference_rename(Reference *self, PyObject *py_name) { } static PyObject * -Reference_resolve(Reference *self, PyObject *args) { +Reference_resolve(Reference *self, PyObject *args) +{ git_reference *c_reference; int err; @@ -2161,7 +2245,8 @@ Reference_resolve(Reference *self, PyObject *args) { } static PyObject * -Reference_get_target(Reference *self) { +Reference_get_target(Reference *self) +{ const char * c_name; /* 1- Get the target */ @@ -2176,7 +2261,8 @@ Reference_get_target(Reference *self) { } static int -Reference_set_target(Reference *self, PyObject *py_name) { +Reference_set_target(Reference *self, PyObject *py_name) +{ char *c_name; int err; @@ -2197,7 +2283,8 @@ Reference_set_target(Reference *self, PyObject *py_name) { } static PyObject * -Reference_get_name(Reference *self) { +Reference_get_name(Reference *self) +{ const char *c_name; c_name = git_reference_name(self->reference); @@ -2205,7 +2292,8 @@ Reference_get_name(Reference *self) { } static PyObject * -Reference_get_sha(Reference *self) { +Reference_get_sha(Reference *self) +{ const git_oid *oid; /* 1- Get the oid (only for "direct" references) */ @@ -2223,7 +2311,8 @@ Reference_get_sha(Reference *self) { } static int -Reference_set_sha(Reference *self, PyObject *py_sha) { +Reference_set_sha(Reference *self, PyObject *py_sha) +{ git_oid oid; int err; @@ -2243,7 +2332,8 @@ Reference_set_sha(Reference *self, PyObject *py_sha) { } static PyObject * -Reference_get_type(Reference *self) { +Reference_get_type(Reference *self) +{ git_rtype c_type; c_type = git_reference_type(self->reference); @@ -2315,7 +2405,8 @@ static PyTypeObject ReferenceType = { }; static PyObject * -init_repository(PyObject *self, PyObject *args) { +init_repository(PyObject *self, PyObject *args) +{ git_repository *repo; Repository *py_repo; const char *path; From 60e1562cd1081198c43456fe1118b9086f755714 Mon Sep 17 00:00:00 2001 From: Julien Miotte Date: Thu, 28 Jul 2011 11:35:05 +0200 Subject: [PATCH 0045/2237] Adding an example in the documentation for reading the status. --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 37c2d0b0c..f24f4cf62 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,14 @@ Index read: >>> sha = index['path/to/file'].sha # from path to sha >>> blob = repo[sha] # from sha to blob +Inspect the status of the repository: + + >>> from pygit2 import GIT_STATUS_CURRENT + >>> status = repo.status() + >>> for filepath, flags in status.items(): + ... if flags != GIT_STATUS_CURRENT: + ... print "Filepath %s isn't clean" % filepath + Iterate over all entries of the index: >>> for entry in index: From 7545cdf9f3e13c9d65edbedc8a739c812a38e82b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 4 Aug 2011 10:29:45 +0200 Subject: [PATCH 0046/2237] Updato use new 'git_signature_new' prototype --- pygit2.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pygit2.c b/pygit2.c index 37a3c24d5..1963ef0b7 100644 --- a/pygit2.c +++ b/pygit2.c @@ -522,23 +522,22 @@ build_person(const git_signature *signature) } static int -signature_converter(PyObject *value, git_signature **out) +signature_converter(PyObject *value, git_signature **signature) { char *name, *email; long long time; int offset; - git_signature *signature; + int err; if (!PyArg_ParseTuple(value, "ssLi", &name, &email, &time, &offset)) return 0; - signature = git_signature_new(name, email, time, offset); - if (signature == NULL) { - PyErr_SetNone(PyExc_MemoryError); + err = git_signature_new(signature, name, email, time, offset); + if (err < 0) { + Error_set(err); return 0; } - *out = signature; return 1; } From 7f7a512e66dfb0fc447b133fa16f30f220338a61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 5 Aug 2011 12:52:43 +0200 Subject: [PATCH 0047/2237] Fix issue #39, add missing Py_INCREF --- pygit2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pygit2.c b/pygit2.c index 1963ef0b7..623ad63c7 100644 --- a/pygit2.c +++ b/pygit2.c @@ -425,6 +425,7 @@ Repository_get_index(Repository *self, void *closure) py_index = (Index*)IndexType.tp_alloc(&IndexType, 0); if (!py_index) return PyErr_NoMemory(); + Py_INCREF(self); py_index->repo = self; py_index->index = index; py_index->own_obj = 0; From bfd20dc4b44c67f47ca0c1d0538d4b5bba0ed519 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 8 Aug 2011 23:42:01 +0200 Subject: [PATCH 0048/2237] Somewhat better test coverage - create_repository - IndexEntry.path - Correct SHAs in Index iteration - Opening a bare index & attempting to add to it - Walker.reset - Walker.push - Walker.sort --- test/test_index.py | 16 +++++++++++++--- test/test_repository.py | 12 +++++++++++- test/test_revwalk.py | 20 ++++++++++++++++++++ test/utils.py | 5 +++++ 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/test/test_index.py b/test/test_index.py index f2d82f73c..0f3b5d128 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -31,9 +31,11 @@ __author__ = 'jdavid@itaapy.com (J. David Ibáñez)' import unittest +import os import utils +import pygit2 class IndexBareTest(utils.BareRepoTestCase): @@ -57,6 +59,7 @@ def test_read(self): sha = 'a520c24d85fbfc815d385957eed41406ca5a860b' self.assertTrue('hello.txt' in index) self.assertEqual(index['hello.txt'].sha, sha) + self.assertEqual(index['hello.txt'].path, 'hello.txt') self.assertEqual(index[1].sha, sha) def test_add(self): @@ -93,9 +96,10 @@ def test_iter(self): index = self.repo.index n = len(index) self.assertEqual(len(list(index)), n) - # FIXME This fails - #entries = [index[x] for x in xrange(n)] - #self.assertEqual(list(index), entries) + + # Compare SHAs, not IndexEntry object identity + entries = [index[x].sha for x in xrange(n)] + self.assertEqual(list(x.sha for x in index), entries) def test_mode(self): """ @@ -106,6 +110,12 @@ def test_mode(self): hello_mode = index['hello.txt'].mode self.assertEqual(hello_mode, 33188) + def test_bare_index(self): + index = pygit2.Index(os.path.join(self.repo.path, 'index')) + self.assertEqual([x.sha for x in index], + [x.sha for x in self.repo.index]) + + self.assertRaises(pygit2.GitError, lambda: index.add('bye.txt', 0)) if __name__ == '__main__': unittest.main() diff --git a/test/test_repository.py b/test/test_repository.py index 445c2e616..ed1535bdd 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -31,9 +31,11 @@ import binascii import unittest +import os from os.path import join, abspath -from pygit2 import GitError, GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT +from pygit2 import (GitError, GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, + init_repository) import utils A_HEX_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' @@ -111,6 +113,14 @@ def test_get_workdir(self): self.assertEqual(directory, expected) +class NewRepositoryTest(utils.NoRepoTestCase): + def test_new_repo(self): + repo = init_repository(self.temp_dir, False) + + hex_sha = repo.write(GIT_OBJ_BLOB, "Test") + self.assertEqual(len(hex_sha), 40) + + assert os.path.exists(os.path.join(self._temp_dir, '.git')) if __name__ == '__main__': unittest.main() diff --git a/test/test_revwalk.py b/test/test_revwalk.py index 5fa912dfc..4bf527520 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -61,6 +61,26 @@ def test_hide(self): walker.hide('4ec4389a8068641da2d6578db0419484972284c8') self.assertEqual(len(list(walker)), 2) + def test_reset(self): + walker = self.repo.walk(log[0], GIT_SORT_TIME) + walker.reset() + out = [ x.sha for x in walker ] + self.assertEqual(out, []) + + def test_push(self): + walker = self.repo.walk(log[-1], GIT_SORT_TIME) + out = [ x.sha for x in walker ] + self.assertEqual(out, log[-1:]) + walker.reset() + walker.push(log[0]) + out = [ x.sha for x in walker ] + self.assertEqual(out, log) + + def test_sort(self): + walker = self.repo.walk(log[0], GIT_SORT_TIME) + walker.sort(GIT_SORT_TIME | GIT_SORT_REVERSE) + out = [ x.sha for x in walker ] + self.assertEqual(out, list(reversed(log))) if __name__ == '__main__': unittest.main() diff --git a/test/utils.py b/test/utils.py index 0fcfc1cf8..e973a0240 100644 --- a/test/utils.py +++ b/test/utils.py @@ -79,6 +79,11 @@ def setUp(self): temp_repo_path = os.path.join(temp_dir, repo_dir, '.git') self.repo = pygit2.Repository(temp_repo_path) +class NoRepoTestCase(BaseTestCase): + def setUp(self): + self.temp_dir = tempfile.mkdtemp() + self._temp_dir = self.temp_dir + class DirtyRepoTestCase(RepoTestCase): repo_dir = 'dirtyrepo' From 7b8ae0e10c6593dafca6680db4575514bd5b9475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 10 Aug 2011 22:52:42 +0200 Subject: [PATCH 0049/2237] Python 3, now pygit2 builds Tests do not yet pass. --- pygit2.c | 171 ++++++++++++++++++++++++++++++-------------------- test/utils.py | 8 ++- 2 files changed, 108 insertions(+), 71 deletions(-) diff --git a/pygit2.c b/pygit2.c index 623ad63c7..59aa7b580 100644 --- a/pygit2.c +++ b/pygit2.c @@ -29,10 +29,25 @@ #include #include -/* Define PyVarObject_HEAD_INIT for Python 2.5 */ +/* Python 2.5 support */ +#ifndef Py_TYPE + #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) +#endif #ifndef PyVarObject_HEAD_INIT -#define PyVarObject_HEAD_INIT(type, size) \ - PyObject_HEAD_INIT(type) size, + #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, +#endif + +/* Python 3 support */ +#if PY_MAJOR_VERSION >= 3 + #define PyInt_AsLong PyLong_AsLong + #define PyInt_Check PyLong_Check + #define PyInt_FromLong PyLong_FromLong + #define PyString_AS_STRING PyBytes_AS_STRING + #define PyString_AsString PyBytes_AsString + #define PyString_Check PyBytes_Check + #define PyString_FromString PyBytes_FromString + #define PyString_FromStringAndSize PyBytes_FromStringAndSize + #define PyString_Size PyBytes_Size #endif @@ -175,8 +190,8 @@ Error_set_py_obj(int err, PyObject *py_obj) if (err == GIT_ENOTOID && !PyString_Check(py_obj)) { PyErr_Format(PyExc_TypeError, - "Git object id must be 40 byte hexadecimal str, or 20 byte binary str: %.200s", - py_obj->ob_type->tp_name); + "Git object id must be byte string, not: %.200s", + Py_TYPE(py_obj)->tp_name); return NULL; } else if (err == GIT_ENOTFOUND) { @@ -322,7 +337,7 @@ Repository_dealloc(Repository *self) if (self->repo) git_repository_free(self->repo); Py_XDECREF(self->index); - self->ob_type->tp_free((PyObject*)self); + Py_TYPE(self)->tp_free((PyObject*)self); } static int @@ -847,8 +862,7 @@ static PyMappingMethods Repository_as_mapping = { }; static PyTypeObject RepositoryType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "pygit2.Repository", /* tp_name */ sizeof(Repository), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -893,7 +907,7 @@ Object_dealloc(Object* self) { git_object_close(self->obj); Py_XDECREF(self->repo); - self->ob_type->tp_free((PyObject*)self); + Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * @@ -957,8 +971,7 @@ static PyMethodDef Object_methods[] = { }; static PyTypeObject ObjectType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "pygit2.Object", /* tp_name */ sizeof(Object), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -1101,8 +1114,7 @@ static PyGetSetDef Commit_getseters[] = { }; static PyTypeObject CommitType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "pygit2.Commit", /* tp_name */ sizeof(Commit), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -1146,7 +1158,7 @@ static void TreeEntry_dealloc(TreeEntry *self) { Py_XDECREF(self->tree); - self->ob_type->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free((PyObject *)self); } static PyObject * @@ -1190,8 +1202,7 @@ static PyMethodDef TreeEntry_methods[] = { }; static PyTypeObject TreeEntryType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "pygit2.TreeEntry", /* tp_name */ sizeof(TreeEntry), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -1354,7 +1365,7 @@ Tree_getitem(Tree *self, PyObject *value) else { PyErr_Format(PyExc_TypeError, "Tree entry index must be int or str, not %.200s", - value->ob_type->tp_name); + Py_TYPE(value)->tp_name); return NULL; } } @@ -1377,8 +1388,7 @@ static PyMappingMethods Tree_as_mapping = { }; static PyTypeObject TreeType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "pygit2.Tree", /* tp_name */ sizeof(Tree), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -1475,8 +1485,7 @@ static PyGetSetDef Blob_getseters[] = { }; static PyTypeObject BlobType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "pygit2.Blob", /* tp_name */ sizeof(Blob), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -1520,7 +1529,7 @@ static void Tag_dealloc(Tag *self) { Py_XDECREF(self->target); - self->ob_type->tp_free((PyObject*)self); + Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * @@ -1580,8 +1589,7 @@ static PyGetSetDef Tag_getseters[] = { }; static PyTypeObject TagType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "pygit2.Tag", /* tp_name */ sizeof(Tag), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -1652,7 +1660,7 @@ Index_dealloc(Index* self) if (self->own_obj) git_index_free(self->index); Py_XDECREF(self->repo); - self->ob_type->tp_free((PyObject*)self); + Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * @@ -1749,7 +1757,7 @@ Index_get_position(Index *self, PyObject *value) else { PyErr_Format(PyExc_TypeError, "Index entry key must be int or str, not %.200s", - value->ob_type->tp_name); + Py_TYPE(value)->tp_name); return -1; } @@ -1905,8 +1913,7 @@ static PyMappingMethods Index_as_mapping = { }; static PyTypeObject IndexType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "pygit2.Index", /* tp_name */ sizeof(Index), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -2001,7 +2008,7 @@ static PyTypeObject IndexIterType = { static void IndexEntry_dealloc(IndexEntry *self) { - self->ob_type->tp_free((PyObject*)self); + Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * @@ -2030,8 +2037,7 @@ static PyGetSetDef IndexEntry_getseters[] = { }; static PyTypeObject IndexEntryType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "pygit2.IndexEntry", /* tp_name */ sizeof(IndexEntry), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -2076,7 +2082,7 @@ Walker_dealloc(Walker *self) { git_revwalk_free(self->walk); Py_DECREF(self->repo); - self->ob_type->tp_free((PyObject*)self); + Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * @@ -2178,8 +2184,7 @@ static PyMethodDef Walker_methods[] = { }; static PyTypeObject WalkerType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "pygit2.Walker", /* tp_name */ sizeof(Walker), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -2198,7 +2203,7 @@ static PyTypeObject WalkerType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ "Revision walker", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ @@ -2390,8 +2395,7 @@ static PyGetSetDef Reference_getseters[] = { }; static PyTypeObject ReferenceType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "pygit2.Reference", /* tp_name */ sizeof(Reference), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -2466,55 +2470,50 @@ static PyMethodDef module_methods[] = { {NULL} }; -PyMODINIT_FUNC -initpygit2(void) +PyObject* +moduleinit(PyObject* m) { - PyObject* m; + if (m == NULL) + return NULL; GitError = PyErr_NewException("pygit2.GitError", NULL, NULL); RepositoryType.tp_new = PyType_GenericNew; if (PyType_Ready(&RepositoryType) < 0) - return; + return NULL; /* Do not set 'tp_new' for Git objects. To create Git objects use the * Repository.create_XXX methods */ if (PyType_Ready(&ObjectType) < 0) - return; + return NULL; CommitType.tp_base = &ObjectType; if (PyType_Ready(&CommitType) < 0) - return; + return NULL; TreeType.tp_base = &ObjectType; if (PyType_Ready(&TreeType) < 0) - return; + return NULL; BlobType.tp_base = &ObjectType; if (PyType_Ready(&BlobType) < 0) - return; + return NULL; TagType.tp_base = &ObjectType; if (PyType_Ready(&TagType) < 0) - return; + return NULL; TreeEntryType.tp_new = PyType_GenericNew; if (PyType_Ready(&TreeEntryType) < 0) - return; + return NULL; IndexType.tp_new = PyType_GenericNew; if (PyType_Ready(&IndexType) < 0) - return; + return NULL; IndexEntryType.tp_new = PyType_GenericNew; if (PyType_Ready(&IndexEntryType) < 0) - return; + return NULL; WalkerType.tp_new = PyType_GenericNew; if (PyType_Ready(&WalkerType) < 0) - return; + return NULL; ReferenceType.tp_new = PyType_GenericNew; if (PyType_Ready(&ReferenceType) < 0) - return; - - m = Py_InitModule3("pygit2", module_methods, - "Python bindings for libgit2."); - - if (m == NULL) - return; + return NULL; Py_INCREF(GitError); PyModule_AddObject(m, "GitError", GitError); @@ -2558,26 +2557,60 @@ initpygit2(void) PyModule_AddIntConstant(m, "GIT_SORT_TOPOLOGICAL", GIT_SORT_TOPOLOGICAL); PyModule_AddIntConstant(m, "GIT_SORT_TIME", GIT_SORT_TIME); PyModule_AddIntConstant(m, "GIT_SORT_REVERSE", GIT_SORT_REVERSE); - PyModule_AddIntConstant(m,"GIT_REF_OID", GIT_REF_OID); - PyModule_AddIntConstant(m,"GIT_REF_SYMBOLIC", GIT_REF_SYMBOLIC); - PyModule_AddIntConstant(m,"GIT_REF_PACKED", GIT_REF_PACKED); + PyModule_AddIntConstant(m, "GIT_REF_OID", GIT_REF_OID); + PyModule_AddIntConstant(m, "GIT_REF_SYMBOLIC", GIT_REF_SYMBOLIC); + PyModule_AddIntConstant(m, "GIT_REF_PACKED", GIT_REF_PACKED); /** Git status flags **/ - PyModule_AddIntConstant(m,"GIT_STATUS_CURRENT", GIT_STATUS_CURRENT); + PyModule_AddIntConstant(m, "GIT_STATUS_CURRENT", GIT_STATUS_CURRENT); /* Flags for index status */ - PyModule_AddIntConstant(m,"GIT_STATUS_INDEX_NEW", GIT_STATUS_INDEX_NEW); - PyModule_AddIntConstant(m,"GIT_STATUS_INDEX_MODIFIED", + PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_NEW", GIT_STATUS_INDEX_NEW); + PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_MODIFIED", GIT_STATUS_INDEX_MODIFIED); - PyModule_AddIntConstant(m,"GIT_STATUS_INDEX_DELETED" , + PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_DELETED" , GIT_STATUS_INDEX_DELETED); /* Flags for worktree status */ - PyModule_AddIntConstant(m,"GIT_STATUS_WT_NEW", GIT_STATUS_WT_NEW); - PyModule_AddIntConstant(m,"GIT_STATUS_WT_MODIFIED" , + PyModule_AddIntConstant(m, "GIT_STATUS_WT_NEW", GIT_STATUS_WT_NEW); + PyModule_AddIntConstant(m, "GIT_STATUS_WT_MODIFIED" , GIT_STATUS_WT_MODIFIED); - PyModule_AddIntConstant(m,"GIT_STATUS_WT_DELETED", GIT_STATUS_WT_DELETED); + PyModule_AddIntConstant(m, "GIT_STATUS_WT_DELETED", GIT_STATUS_WT_DELETED); /* Flags for ignored files */ - PyModule_AddIntConstant(m,"GIT_STATUS_IGNORED", GIT_STATUS_IGNORED); -} + PyModule_AddIntConstant(m, "GIT_STATUS_IGNORED", GIT_STATUS_IGNORED); + + return m; +} + + +#if PY_MAJOR_VERSION < 3 + PyMODINIT_FUNC + initpygit2(void) + { + PyObject* m; + m = Py_InitModule3("pygit2", module_methods, + "Python bindings for libgit2."); + moduleinit(m); + } +#else + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "pygit2", /* m_name */ + "Python bindings for libgit2.", /* m_doc */ + -1, /* m_size */ + module_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ + }; + + PyMODINIT_FUNC + PyInit_pygit2(void) + { + PyObject* m; + m = PyModule_Create(&moduledef); + return moduleinit(m); + } +#endif diff --git a/test/utils.py b/test/utils.py index e973a0240..ddc0ce0af 100644 --- a/test/utils.py +++ b/test/utils.py @@ -29,6 +29,7 @@ import os import shutil +import sys import tarfile import tempfile import unittest @@ -44,8 +45,11 @@ def tearDown(self): def assertRaisesWithArg(self, exc_class, arg, func, *args, **kwargs): try: func(*args, **kwargs) - except exc_class, e: - self.assertEqual((arg,), e.args) + except exc_class: + # XXX Use the 'exc_class as exc_value' syntax as soon as we drop + # support for Python 2.5 + exc_value = sys.exc_info()[1] + self.assertEqual((arg,), exc_value.args) else: self.fail('%s(%r) not raised' % (exc_class.__name__, arg)) From b634a19bd4bc5c47ebf14d9abf3157b2adaf1270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 13 Aug 2011 20:36:37 +0200 Subject: [PATCH 0050/2237] Update to latest changes in libgit2 Commit.message_short removed, Commit.message_encoding added --- pygit2.c | 16 +++++++++++----- test/test_commit.py | 6 ++++-- test/test_tag.py | 2 +- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/pygit2.c b/pygit2.c index 59aa7b580..56e5d90bc 100644 --- a/pygit2.c +++ b/pygit2.c @@ -613,7 +613,7 @@ Repository_create_commit(Repository *self, PyObject *args) } err = git_commit_create(&oid, self->repo, update_ref, author, committer, - message, tree, parent_count, (const git_commit**)parents); + NULL, message, tree, parent_count, (const git_commit**)parents); git_tree_close(tree); free_parents(parents, parent_count); if (err < 0) @@ -1012,9 +1012,15 @@ static PyTypeObject ObjectType = { }; static PyObject * -Commit_get_message_short(Commit *commit) +Commit_get_message_encoding(Commit *commit) { - return PyString_FromString(git_commit_message_short(commit->commit)); + char *encoding; + + encoding = git_commit_message_encoding(commit->commit); + if (encoding == NULL) + Py_RETURN_NONE; + + return PyString_FromString(encoding); } static PyObject * @@ -1100,8 +1106,8 @@ Commit_get_parents(Commit *commit) } static PyGetSetDef Commit_getseters[] = { - {"message_short", (getter)Commit_get_message_short, NULL, "short message", - NULL}, + {"message_encoding", (getter)Commit_get_message_encoding, NULL, + "message encoding", NULL}, {"message", (getter)Commit_get_message, NULL, "message", NULL}, {"commit_time", (getter)Commit_get_commit_time, NULL, "commit time", NULL}, diff --git a/test/test_commit.py b/test/test_commit.py index deb7bd5af..e308961b5 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -46,7 +46,8 @@ def test_read_commit(self): self.assertEqual(1, len(parents)) self.assertEqual('c2792cfa289ae6321ecf2cd5806c2194b0fd070c', parents[0].sha) - self.assertEqual('Second test data commit.', commit.message_short) + self.assertEqual(None, commit.message_encoding) + #self.assertEqual('Second test data commit.', commit.message_short) self.assertEqual(('Second test data commit.\n\n' 'This commit has some additional text.\n'), commit.message) @@ -76,8 +77,9 @@ def test_new_commit(self): self.assertEqual(GIT_OBJ_COMMIT, commit.type) self.assertEqual('30bb126a4959290987fc07ea49f92be276dce9d6', commit.sha) + self.assertEqual(None, commit.message_encoding) self.assertEqual(message, commit.message) - self.assertEqual('New commit.', commit.message_short) + #self.assertEqual('New commit.', commit.message_short) self.assertEqual(12346, commit.commit_time) self.assertEqual(committer, commit.committer) self.assertEqual(author, commit.author) diff --git a/test/test_tag.py b/test/test_tag.py index a8b447b78..8b3a598c0 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -52,7 +52,7 @@ def test_read_tag(self): commit = tag.target del tag - self.assertEqual('Initial test data commit.', commit.message_short) + self.assertEqual('Initial test data commit.\n', commit.message) def test_new_tag(self): name = 'thetag' From 7035598c73cbc5fa48ec5abdb27b166e9aa07a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 13 Aug 2011 21:50:51 +0200 Subject: [PATCH 0051/2237] Fix warning in 'Commit_get_message_encoding' --- pygit2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index 56e5d90bc..487e117d2 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1014,7 +1014,7 @@ static PyTypeObject ObjectType = { static PyObject * Commit_get_message_encoding(Commit *commit) { - char *encoding; + const char *encoding; encoding = git_commit_message_encoding(commit->commit); if (encoding == NULL) From aaa3d533a82087e6a680e39c55a825dc89e1bc75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 13 Aug 2011 22:05:43 +0200 Subject: [PATCH 0052/2237] Drop support for Python 2.5 It still compiles and probably works fine. But unit tests do not run and official support for Python 2.5 has been dropped. --- README.md | 5 +++-- test/utils.py | 6 +----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f24f4cf62..a39fdcaab 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ pygit2 - libgit2 bindings in Python ===================================== -pygit2 is a set of Python 2.5+ bindings to the libgit2 linkable C Git library. +pygit2 is a set of Python bindings to the libgit2 linkable C Git library. +The supported versions of Python are 2.6, 2.7, 3.1 and 3.2 INSTALLING AND RUNNING ======================== @@ -16,7 +17,7 @@ For instance, in Debian-based systems run: $ sudo apt-get install zlib1g-dev libssl-dev -Also, make sure you have Python 2.5+ installed together with the Python development headers. +Also, make sure you have Python 2.6+ installed together with the Python development headers. When those are installed, you can install pygit2: diff --git a/test/utils.py b/test/utils.py index ddc0ce0af..f531e45a2 100644 --- a/test/utils.py +++ b/test/utils.py @@ -29,7 +29,6 @@ import os import shutil -import sys import tarfile import tempfile import unittest @@ -45,10 +44,7 @@ def tearDown(self): def assertRaisesWithArg(self, exc_class, arg, func, *args, **kwargs): try: func(*args, **kwargs) - except exc_class: - # XXX Use the 'exc_class as exc_value' syntax as soon as we drop - # support for Python 2.5 - exc_value = sys.exc_info()[1] + except exc_class as exc_value: self.assertEqual((arg,), exc_value.args) else: self.fail('%s(%r) not raised' % (exc_class.__name__, arg)) From 7950ee1116e52ef73df5a1f916980ef13ccaa836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 13 Aug 2011 22:46:54 +0200 Subject: [PATCH 0053/2237] Use Python 3 string literals for the unit tests Now unit tests are broken. --- test/test_blob.py | 6 ++++-- test/test_commit.py | 6 ++++-- test/test_index.py | 10 ++++++---- test/test_refs.py | 8 ++++---- test/test_repository.py | 6 ++++-- test/test_revwalk.py | 7 +++++-- test/test_status.py | 6 ++++-- test/test_tag.py | 6 ++++-- test/test_tree.py | 6 ++++-- test/utils.py | 5 +++-- 10 files changed, 42 insertions(+), 24 deletions(-) diff --git a/test/test_blob.py b/test/test_blob.py index 65082afd5..40a7e06ee 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -27,13 +27,15 @@ """Tests for Blob objects.""" -__author__ = 'dborowitz@google.com (Dave Borowitz)' - +from __future__ import unicode_literals import unittest import pygit2 import utils + +__author__ = 'dborowitz@google.com (Dave Borowitz)' + BLOB_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' diff --git a/test/test_commit.py b/test/test_commit.py index e308961b5..6834826be 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -27,13 +27,15 @@ """Tests for Commit objects.""" -__author__ = 'dborowitz@google.com (Dave Borowitz)' - +from __future__ import unicode_literals import unittest from pygit2 import GIT_OBJ_COMMIT import utils + +__author__ = 'dborowitz@google.com (Dave Borowitz)' + COMMIT_SHA = '5fe808e8953c12735680c257f56600cb0de44b10' diff --git a/test/test_index.py b/test/test_index.py index 0f3b5d128..7ea604552 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -28,14 +28,16 @@ """Tests for Index files.""" -__author__ = 'jdavid@itaapy.com (J. David Ibáñez)' - -import unittest +from __future__ import unicode_literals import os +import unittest +import pygit2 import utils -import pygit2 + +__author__ = 'jdavid@itaapy.com (J. David Ibáñez)' + class IndexBareTest(utils.BareRepoTestCase): diff --git a/test/test_refs.py b/test/test_refs.py index 575560e85..8b16122ba 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -28,14 +28,14 @@ """Tests for reference objects.""" - -__author__ = 'david.versmisse@itaapy.com (David Versmisse)' - +from __future__ import unicode_literals import unittest -import utils + from pygit2 import GIT_REF_OID, GIT_REF_SYMBOLIC +import utils +__author__ = 'david.versmisse@itaapy.com (David Versmisse)' LAST_COMMIT = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' diff --git a/test/test_repository.py b/test/test_repository.py index ed1535bdd..d34f86837 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -27,8 +27,7 @@ """Tests for Repository objects.""" -__author__ = 'dborowitz@google.com (Dave Borowitz)' - +from __future__ import unicode_literals import binascii import unittest import os @@ -38,6 +37,9 @@ init_repository) import utils + +__author__ = 'dborowitz@google.com (Dave Borowitz)' + A_HEX_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' A_BIN_SHA = binascii.unhexlify(A_HEX_SHA) diff --git a/test/test_revwalk.py b/test/test_revwalk.py index 4bf527520..5ea249799 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -28,13 +28,16 @@ """Tests for revision walk.""" -__author__ = 'jdavid@itaapy.com (J. David Ibáñez)' - +from __future__ import unicode_literals import unittest from pygit2 import GIT_SORT_TIME, GIT_SORT_REVERSE import utils + +__author__ = 'jdavid@itaapy.com (J. David Ibáñez)' + + # In the order given by git log log = [ '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98', diff --git a/test/test_status.py b/test/test_status.py index 41dc4dbea..a01d6714b 100644 --- a/test/test_status.py +++ b/test/test_status.py @@ -28,13 +28,15 @@ """Tests for revision walk.""" -__author__ = 'mike.perdide@gmail.com (Julien Miotte)' - +from __future__ import unicode_literals import unittest import pygit2 import utils + +__author__ = 'mike.perdide@gmail.com (Julien Miotte)' + EXPECTED = { "current_file": pygit2.GIT_STATUS_CURRENT, "file_deleted": pygit2.GIT_STATUS_WT_DELETED, diff --git a/test/test_tag.py b/test/test_tag.py index 8b3a598c0..d797c4da1 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -27,13 +27,15 @@ """Tests for Tag objects.""" -__author__ = 'dborowitz@google.com (Dave Borowitz)' - +from __future__ import unicode_literals import unittest import pygit2 import utils + +__author__ = 'dborowitz@google.com (Dave Borowitz)' + TAG_SHA = '3d2962987c695a29f1f80b6c3aa4ec046ef44369' diff --git a/test/test_tree.py b/test/test_tree.py index b48308f82..96b7e97d7 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -27,14 +27,16 @@ """Tests for Commit objects.""" -__author__ = 'dborowitz@google.com (Dave Borowitz)' - +from __future__ import unicode_literals import operator import unittest import pygit2 import utils + +__author__ = 'dborowitz@google.com (Dave Borowitz)' + TREE_SHA = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' SUBTREE_SHA = '614fd9a3094bf618ea938fffc00e7d1a54f89ad0' diff --git a/test/utils.py b/test/utils.py index f531e45a2..6ccf9094b 100644 --- a/test/utils.py +++ b/test/utils.py @@ -25,8 +25,6 @@ """Test utilities for libgit2.""" -__author__ = 'dborowitz@google.com (Dave Borowitz)' - import os import shutil import tarfile @@ -36,6 +34,9 @@ import pygit2 +__author__ = 'dborowitz@google.com (Dave Borowitz)' + + class BaseTestCase(unittest.TestCase): def tearDown(self): From 81bfabea73ffcfbff7198fc4b86520c8cd20dadf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 13 Aug 2011 23:22:17 +0200 Subject: [PATCH 0054/2237] Use byte strings for raw-sha and text for hex-sha As suggested by Douglas Morrison in issue 43: https://github.com/libgit2/pygit2/issues/43 (Unit tests still broken.) --- pygit2.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/pygit2.c b/pygit2.c index 487e117d2..25094b6ff 100644 --- a/pygit2.c +++ b/pygit2.c @@ -276,26 +276,37 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) const char *hex_or_bin; int err; - hex_or_bin = PyString_AsString(py_str); - if (hex_or_bin == NULL) { - Error_set_py_obj(GIT_ENOTOID, py_str); - return 0; - } - - if (PyString_Size(py_str) == 20) { + /* Case 1: raw sha */ + if (PyString_Check(py_str)) { + hex_or_bin = PyString_AsString(py_str); + if (hex_or_bin == NULL) + return 0; git_oid_fromraw(oid, (const unsigned char*)hex_or_bin); - err = 0; - } - else { - err = git_oid_fromstr(oid, hex_or_bin); + return 1; } - if (err < 0) { - Error_set_py_obj(err, py_str); - return 0; + /* Case 2: hex sha */ + if (PyUnicode_Check(py_str)) { + py_str = PyUnicode_AsASCIIString(py_str); + if (py_str == NULL) + return 0; + hex_or_bin = PyString_AsString(py_str); + Py_DECREF(py_str); + if (hex_or_bin == NULL) + return 0; + err = git_oid_fromstr(oid, hex_or_bin); + if (err < 0) { + Error_set_py_obj(err, py_str); + return 0; + } + return 1; } - return 1; + /* Type error */ + PyErr_Format(PyExc_TypeError, + "Git object id must be byte or a text string, not: %.200s", + Py_TYPE(py_str)->tp_name); + return 0; } static PyObject* From 7f6568038a78dfab98ee6fac71ba5ee4d3692503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 15 Aug 2011 22:26:53 +0200 Subject: [PATCH 0055/2237] Fix blob unit tests --- test/test_blob.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_blob.py b/test/test_blob.py index 40a7e06ee..db0169889 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -45,8 +45,8 @@ def test_read_blob(self): blob = self.repo[BLOB_SHA] self.assertTrue(isinstance(blob, pygit2.Blob)) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) - self.assertEqual('a contents\n', blob.data) - self.assertEqual('a contents\n', blob.read_raw()) + self.assertEqual(b'a contents\n', blob.data) + self.assertEqual(b'a contents\n', blob.read_raw()) if __name__ == '__main__': From caccfb4006f99bdd1cbd1ef74c0ac7235d8dd0ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 15 Aug 2011 22:54:06 +0200 Subject: [PATCH 0056/2237] Return text strings for hex-sha (like Object.sha) --- pygit2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index 25094b6ff..477ceb54f 100644 --- a/pygit2.c +++ b/pygit2.c @@ -315,7 +315,7 @@ git_oid_to_py_str(const git_oid *oid) char hex[GIT_OID_HEXSZ]; git_oid_fmt(hex, oid); - return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); + return PyUnicode_DecodeASCII(hex, GIT_OID_HEXSZ, "strict"); } static int From 18846c1b55ddba680663ea0415216ef55a3a23b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 15 Aug 2011 23:16:31 +0200 Subject: [PATCH 0057/2237] Now Commit.message returns a text string --- pygit2.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index 477ceb54f..feb083fc0 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1037,7 +1037,14 @@ Commit_get_message_encoding(Commit *commit) static PyObject * Commit_get_message(Commit *commit) { - return PyString_FromString(git_commit_message(commit->commit)); + const char *encoding; + const char *message; + int len; + + encoding = git_commit_message_encoding(commit->commit); + message = git_commit_message(commit->commit); + len = strlen(message); + return PyUnicode_Decode(message, (Py_ssize_t)len, encoding, "strict"); } static PyObject * From 11997d8a8585b3f02469867d5742e2f4566bf173 Mon Sep 17 00:00:00 2001 From: Erik van Zijst Date: Tue, 16 Aug 2011 16:47:05 +1000 Subject: [PATCH 0058/2237] Added support for commit_time_offset Signed-off-by: Erik van Zijst --- pygit2.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pygit2.c b/pygit2.c index 614dc9564..b30bcbe90 100644 --- a/pygit2.c +++ b/pygit2.c @@ -989,6 +989,12 @@ Commit_get_commit_time(Commit *commit) return PyLong_FromLong(git_commit_time(commit->commit)); } +static PyObject * +Commit_get_commit_time_offset(Commit *commit) +{ + return PyLong_FromLong(git_commit_time_offset(commit->commit)); +} + static PyObject * Commit_get_committer(Commit *commit) { @@ -1065,6 +1071,8 @@ static PyGetSetDef Commit_getseters[] = { {"message", (getter)Commit_get_message, NULL, "message", NULL}, {"commit_time", (getter)Commit_get_commit_time, NULL, "commit time", NULL}, + {"commit_time_offset", (getter)Commit_get_commit_time_offset, NULL, + "commit time offset", NULL}, {"committer", (getter)Commit_get_committer, NULL, "committer", NULL}, {"author", (getter)Commit_get_author, NULL, "author", NULL}, {"tree", (getter)Commit_get_tree, NULL, "tree object", NULL}, From 5cc4ba23d4212e0f78f59d19b8aa0fa67f8280a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 16 Aug 2011 22:54:05 +0200 Subject: [PATCH 0059/2237] tests: replace xrange by range This fixes one test with Python 3. --- test/test_index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_index.py b/test/test_index.py index 7ea604552..a006398f8 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -100,7 +100,7 @@ def test_iter(self): self.assertEqual(len(list(index)), n) # Compare SHAs, not IndexEntry object identity - entries = [index[x].sha for x in xrange(n)] + entries = [index[x].sha for x in range(n)] self.assertEqual(list(x.sha for x in index), entries) def test_mode(self): From 323d2e23cda5bac3326e986b7e24f167ce1d0162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 16 Aug 2011 22:56:45 +0200 Subject: [PATCH 0060/2237] Fix revision walker --- pygit2.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pygit2.c b/pygit2.c index feb083fc0..0a9f7a732 100644 --- a/pygit2.c +++ b/pygit2.c @@ -504,11 +504,6 @@ Repository_walk(Repository *self, PyObject *args) if (!PyArg_ParseTuple(args, "OI", &value, &sort)) return NULL; - if (value != Py_None && !PyString_Check(value)) { - PyErr_SetObject(PyExc_TypeError, value); - return NULL; - } - err = git_revwalk_new(&walk, self->repo); if (err < 0) return Error_set(err); From 4e9a34b8703bda7ff54bc45c7a7de21c1562888a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 18 Aug 2011 01:26:09 +0200 Subject: [PATCH 0061/2237] Fix segfault on Repository_write This likely fixes issue #44 --- pygit2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pygit2.c b/pygit2.c index 7cc0890c7..1fd016f12 100644 --- a/pygit2.c +++ b/pygit2.c @@ -26,6 +26,7 @@ * Boston, MA 02110-1301, USA. */ +#define PY_SSIZE_T_CLEAN #include #include From 60f50d9f08ae979cb9cc7e2965a6847b9d0b8a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 19 Aug 2011 00:31:37 +0200 Subject: [PATCH 0062/2237] Support 'index[path]' where path is a text string Encode text strings to UTF-8. --- pygit2.c | 54 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/pygit2.c b/pygit2.c index 0a9f7a732..b68fd3bd2 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1747,6 +1747,31 @@ Index_write(Index *self) Py_RETURN_NONE; } +char * +py_str_to_c_str(PyObject *value) +{ + char *c_str; + + /* Case 1: byte string */ + if (PyString_Check(value)) + return PyString_AsString(value); + + /* Case 2: text string */ + if (PyUnicode_Check(value)) { + value = PyUnicode_AsUTF8String(value); + if (value == NULL) + return NULL; + c_str = PyString_AsString(value); + Py_DECREF(value); + return c_str; + } + + /* Type error */ + PyErr_Format(PyExc_TypeError, "unexpected %.200s", + Py_TYPE(value)->tp_name); + return NULL; +} + /* This is an internal function, used by Index_getitem and Index_setitem */ static int Index_get_position(Index *self, PyObject *value) @@ -1754,17 +1779,8 @@ Index_get_position(Index *self, PyObject *value) char *path; int idx; - if (PyString_Check(value)) { - path = PyString_AsString(value); - if (!path) - return -1; - idx = git_index_find(self->index, path); - if (idx < 0) { - Error_set_str(idx, path); - return -1; - } - } - else if (PyInt_Check(value)) { + /* Case 1: integer */ + if (PyInt_Check(value)) { idx = (int)PyInt_AsLong(value); if (idx == -1 && PyErr_Occurred()) return -1; @@ -1772,14 +1788,18 @@ Index_get_position(Index *self, PyObject *value) PyErr_SetObject(PyExc_ValueError, value); return -1; } + return idx; } - else { - PyErr_Format(PyExc_TypeError, - "Index entry key must be int or str, not %.200s", - Py_TYPE(value)->tp_name); + + /* Case 2: byte or text string */ + path = py_str_to_c_str(value); + if (!path) + return -1; + idx = git_index_find(self->index, path); + if (idx < 0) { + Error_set_str(idx, path); return -1; } - return idx; } @@ -1789,7 +1809,7 @@ Index_contains(Index *self, PyObject *value) char *path; int idx; - path = PyString_AsString(value); + path = py_str_to_c_str(value); if (!path) return -1; idx = git_index_find(self->index, path); From 500a14839804999625000658cf15f4d8c32af14c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 19 Aug 2011 00:44:48 +0200 Subject: [PATCH 0063/2237] tests: fix syntax error on octal literals for Py 3 --- test/test_tree.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/test_tree.py b/test/test_tree.py index 96b7e97d7..3864911b1 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -57,27 +57,27 @@ def test_read_tree(self): self.assertRaisesWithArg(IndexError, 3, lambda: tree[3]) self.assertEqual(3, len(tree)) - a_sha = '7f129fd57e31e935c6d60a0c794efe4e6927664b' + sha = '7f129fd57e31e935c6d60a0c794efe4e6927664b' self.assertTrue('a' in tree) - self.assertTreeEntryEqual(tree[0], a_sha, 'a', 0100644) - self.assertTreeEntryEqual(tree[-3], a_sha, 'a', 0100644) - self.assertTreeEntryEqual(tree['a'], a_sha, 'a', 0100644) + self.assertTreeEntryEqual(tree[0], sha, 'a', 0o0100644) + self.assertTreeEntryEqual(tree[-3], sha, 'a', 0o0100644) + self.assertTreeEntryEqual(tree['a'], sha, 'a', 0o0100644) - b_sha = '85f120ee4dac60d0719fd51731e4199aa5a37df6' + sha = '85f120ee4dac60d0719fd51731e4199aa5a37df6' self.assertTrue('b' in tree) - self.assertTreeEntryEqual(tree[1], b_sha, 'b', 0100644) - self.assertTreeEntryEqual(tree[-2], b_sha, 'b', 0100644) - self.assertTreeEntryEqual(tree['b'], b_sha, 'b', 0100644) + self.assertTreeEntryEqual(tree[1], sha, 'b', 0o0100644) + self.assertTreeEntryEqual(tree[-2], sha, 'b', 0o0100644) + self.assertTreeEntryEqual(tree['b'], sha, 'b', 0o0100644) def test_read_subtree(self): tree = self.repo[TREE_SHA] subtree_entry = tree['c'] - self.assertTreeEntryEqual(subtree_entry, SUBTREE_SHA, 'c', 0040000) + self.assertTreeEntryEqual(subtree_entry, SUBTREE_SHA, 'c', 0o0040000) subtree = subtree_entry.to_object() self.assertEqual(1, len(subtree)) - self.assertTreeEntryEqual( - subtree[0], '297efb891a47de80be0cfe9c639e4b8c9b450989', 'd', 0100644) + sha = '297efb891a47de80be0cfe9c639e4b8c9b450989' + self.assertTreeEntryEqual(subtree[0], sha, 'd', 0o0100644) # XXX Creating new trees was removed from libgit2 by v0.11.0, we # deactivate this test temporarily, since the feature may come back in @@ -85,15 +85,15 @@ def test_read_subtree(self): def xtest_new_tree(self): tree = pygit2.Tree(self.repo) self.assertEqual(0, len(tree)) - tree.add_entry('1' * 40, 'x', 0100644) - tree.add_entry('2' * 40, 'y', 0100755) + tree.add_entry('1' * 40, 'x', 0o0100644) + tree.add_entry('2' * 40, 'y', 0o0100755) self.assertEqual(2, len(tree)) self.assertTrue('x' in tree) self.assertTrue('y' in tree) self.assertRaisesWithArg(KeyError, '1' * 40, tree['x'].to_object) - tree.add_entry('3' * 40, 'z1', 0100644) - tree.add_entry('4' * 40, 'z2', 0100644) + tree.add_entry('3' * 40, 'z1', 0o0100644) + tree.add_entry('4' * 40, 'z2', 0o0100644) self.assertEqual(4, len(tree)) del tree['z1'] del tree[2] @@ -122,4 +122,4 @@ def test_iterate_tree(self): if __name__ == '__main__': - unittest.main() + unittest.main() From 8137dc84b5d41d89feffef9a9f882c122e3c30a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 19 Aug 2011 08:49:46 +0200 Subject: [PATCH 0064/2237] Support 'tree[name]' where name is a text string --- pygit2.c | 90 +++++++++++++++++++++++++------------------------------- 1 file changed, 40 insertions(+), 50 deletions(-) diff --git a/pygit2.c b/pygit2.c index b68fd3bd2..0bd2e8d4c 100644 --- a/pygit2.c +++ b/pygit2.c @@ -318,6 +318,31 @@ git_oid_to_py_str(const git_oid *oid) return PyUnicode_DecodeASCII(hex, GIT_OID_HEXSZ, "strict"); } +char * +py_str_to_c_str(PyObject *value) +{ + char *c_str; + + /* Case 1: byte string */ + if (PyString_Check(value)) + return PyString_AsString(value); + + /* Case 2: text string */ + if (PyUnicode_Check(value)) { + value = PyUnicode_AsUTF8String(value); + if (value == NULL) + return NULL; + c_str = PyString_AsString(value); + Py_DECREF(value); + return c_str; + } + + /* Type error */ + PyErr_Format(PyExc_TypeError, "unexpected %.200s", + Py_TYPE(value)->tp_name); + return NULL; +} + static int Repository_init(Repository *self, PyObject *args, PyObject *kwds) { @@ -1273,7 +1298,7 @@ Tree_contains(Tree *self, PyObject *py_name) { char *name; - name = PyString_AsString(py_name); + name = py_str_to_c_str(py_name); if (name == NULL) return -1; @@ -1294,21 +1319,6 @@ wrap_tree_entry(const git_tree_entry *entry, Tree *tree) return py_entry; } -static TreeEntry * -Tree_getitem_by_name(Tree *self, PyObject *py_name) -{ - char *name; - const git_tree_entry *entry; - - name = PyString_AS_STRING(py_name); - entry = git_tree_entry_byname(self->tree, name); - if (!entry) { - PyErr_SetObject(PyExc_KeyError, py_name); - return NULL; - } - return wrap_tree_entry(entry, self); -} - static int Tree_fix_index(Tree *self, PyObject *py_index) { @@ -1375,18 +1385,23 @@ Tree_getitem_by_index(Tree *self, PyObject *py_index) static TreeEntry * Tree_getitem(Tree *self, PyObject *value) { - if (PyString_Check(value)) { - return Tree_getitem_by_name(self, value); - } - else if (PyInt_Check(value)) { + char *name; + const git_tree_entry *entry; + + /* Case 1: integer */ + if (PyInt_Check(value)) return Tree_getitem_by_index(self, value); - } - else { - PyErr_Format(PyExc_TypeError, - "Tree entry index must be int or str, not %.200s", - Py_TYPE(value)->tp_name); + + /* Case 2: byte or text string */ + name = py_str_to_c_str(value); + if (name == NULL) + return NULL; + entry = git_tree_entry_byname(self->tree, name); + if (!entry) { + PyErr_SetObject(PyExc_KeyError, value); return NULL; } + return wrap_tree_entry(entry, self); } static PySequenceMethods Tree_as_sequence = { @@ -1747,31 +1762,6 @@ Index_write(Index *self) Py_RETURN_NONE; } -char * -py_str_to_c_str(PyObject *value) -{ - char *c_str; - - /* Case 1: byte string */ - if (PyString_Check(value)) - return PyString_AsString(value); - - /* Case 2: text string */ - if (PyUnicode_Check(value)) { - value = PyUnicode_AsUTF8String(value); - if (value == NULL) - return NULL; - c_str = PyString_AsString(value); - Py_DECREF(value); - return c_str; - } - - /* Type error */ - PyErr_Format(PyExc_TypeError, "unexpected %.200s", - Py_TYPE(value)->tp_name); - return NULL; -} - /* This is an internal function, used by Index_getitem and Index_setitem */ static int Index_get_position(Index *self, PyObject *value) From 585ce44c21912b1fd928b46c99d39056373599b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 19 Aug 2011 21:38:25 +0200 Subject: [PATCH 0065/2237] tests: fix last test for Python 2 --- test/test_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_repository.py b/test/test_repository.py index d34f86837..63b7676d2 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -59,7 +59,7 @@ def test_read(self): self.assertEqual((GIT_OBJ_BLOB, 'a contents 2\n'), a2) def test_write(self): - data = "hello world" + data = b"hello world" # invalid object type self.assertRaises(GitError, self.repo.write, GIT_OBJ_ANY, data) From ae1d178d7a82e1e57b685512eec317579c0e1477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 21 Aug 2011 00:49:05 +0200 Subject: [PATCH 0066/2237] Fix most unit tests with Python 3 --- pygit2.c | 32 ++++++++++++++++++-------------- test/test_repository.py | 2 +- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/pygit2.c b/pygit2.c index 90d8e1b6e..bba247000 100644 --- a/pygit2.c +++ b/pygit2.c @@ -344,6 +344,9 @@ py_str_to_c_str(PyObject *value) return NULL; } +#define c_str_to_py_str(c_str) \ + PyUnicode_DecodeUTF8(c_str, strlen(c_str), "strict") + static int Repository_init(Repository *self, PyObject *args, PyObject *kwds) { @@ -502,7 +505,7 @@ Repository_get_path(Repository *self, void *closure) const char *c_path; c_path = git_repository_path(self->repo, GIT_REPO_PATH); - return PyString_FromString(c_path); + return c_str_to_py_str(c_path); } static PyObject * @@ -514,7 +517,7 @@ Repository_get_workdir(Repository *self, void *closure) if (c_path == NULL) Py_RETURN_NONE; - return PyString_FromString(c_path); + return c_str_to_py_str(c_path); } static PyObject * @@ -707,15 +710,16 @@ Repository_listall_references(Repository *self, PyObject *args) return Error_set(err); /* 3- Create a new PyTuple */ - if ( (py_result = PyTuple_New(c_result.count)) == NULL) { + py_result = PyTuple_New(c_result.count); + if (py_result == NULL) { git_strarray_free(&c_result); return NULL; } /* 4- Fill it */ for (index=0; index < c_result.count; index++) { - if ((py_string = PyString_FromString( (c_result.strings)[index] )) - == NULL) { + py_string = c_str_to_py_str((c_result.strings)[index]); + if (py_string == NULL) { Py_XDECREF(py_result); git_strarray_free(&c_result); return NULL; @@ -738,7 +742,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) int err; /* 1- Get the C name */ - c_name = PyString_AsString(py_name); + c_name = py_str_to_c_str(py_name); if (c_name == NULL) return NULL; @@ -1223,7 +1227,7 @@ TreeEntry_get_attributes(TreeEntry *self) static PyObject * TreeEntry_get_name(TreeEntry *self) { - return PyString_FromString(git_tree_entry_name(self->entry)); + return c_str_to_py_str(git_tree_entry_name(self->entry)); } static PyObject * @@ -1600,7 +1604,7 @@ Tag_get_name(Tag *self) name = git_tag_name(self->tag); if (!name) Py_RETURN_NONE; - return PyString_FromString(name); + return c_str_to_py_str(name); } static PyObject * @@ -1619,7 +1623,7 @@ Tag_get_message(Tag *self) message = git_tag_message(self->tag); if (!message) Py_RETURN_NONE; - return PyString_FromString(message); + return c_str_to_py_str(message); } static PyGetSetDef Tag_getseters[] = { @@ -2058,7 +2062,7 @@ IndexEntry_get_mode(IndexEntry *self) static PyObject * IndexEntry_get_path(IndexEntry *self) { - return PyString_FromString(self->entry->path); + return c_str_to_py_str(self->entry->path); } static PyObject * @@ -2286,7 +2290,7 @@ Reference_rename(Reference *self, PyObject *py_name) int err; /* 1- Get the C name */ - c_name = PyString_AsString(py_name); + c_name = py_str_to_c_str(py_name); if (c_name == NULL) return NULL; @@ -2327,7 +2331,7 @@ Reference_get_target(Reference *self) } /* 2- Make a PyString and return it */ - return PyString_FromString(c_name); + return c_str_to_py_str(c_name); } static int @@ -2337,7 +2341,7 @@ Reference_set_target(Reference *self, PyObject *py_name) int err; /* 1- Get the C name */ - c_name = PyString_AsString(py_name); + c_name = py_str_to_c_str(py_name); if (c_name == NULL) return -1; @@ -2358,7 +2362,7 @@ Reference_get_name(Reference *self) const char *c_name; c_name = git_reference_name(self->reference); - return PyString_FromString(c_name); + return c_str_to_py_str(c_name); } static PyObject * diff --git a/test/test_repository.py b/test/test_repository.py index 63b7676d2..4f4546a8a 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -79,7 +79,7 @@ def test_lookup_blob(self): self.assertRaises(TypeError, lambda: self.repo[123]) self.assertEqual(self.repo[A_BIN_SHA].sha, A_HEX_SHA) a = self.repo[A_HEX_SHA] - self.assertEqual('a contents\n', a.read_raw()) + self.assertEqual(b'a contents\n', a.read_raw()) self.assertEqual(A_HEX_SHA, a.sha) self.assertEqual(GIT_OBJ_BLOB, a.type) From b84b79acd8ce6f58236c4dd9ae168943f34c1665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 24 Aug 2011 00:07:20 +0200 Subject: [PATCH 0067/2237] Fix running the tests with Python 3 Now "python3 setup.py test" works. --- test/test_blob.py | 3 ++- test/test_commit.py | 3 ++- test/test_index.py | 3 ++- test/test_refs.py | 3 ++- test/test_repository.py | 3 ++- test/test_revwalk.py | 3 ++- test/test_status.py | 3 ++- test/test_tag.py | 3 ++- test/test_tree.py | 3 ++- 9 files changed, 18 insertions(+), 9 deletions(-) diff --git a/test/test_blob.py b/test/test_blob.py index db0169889..cb21ad2c4 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -27,11 +27,12 @@ """Tests for Blob objects.""" +from __future__ import absolute_import from __future__ import unicode_literals import unittest import pygit2 -import utils +from . import utils __author__ = 'dborowitz@google.com (Dave Borowitz)' diff --git a/test/test_commit.py b/test/test_commit.py index 6834826be..fea9ead4c 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -27,11 +27,12 @@ """Tests for Commit objects.""" +from __future__ import absolute_import from __future__ import unicode_literals import unittest from pygit2 import GIT_OBJ_COMMIT -import utils +from . import utils __author__ = 'dborowitz@google.com (Dave Borowitz)' diff --git a/test/test_index.py b/test/test_index.py index a006398f8..2da7e5d80 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -28,12 +28,13 @@ """Tests for Index files.""" +from __future__ import absolute_import from __future__ import unicode_literals import os import unittest import pygit2 -import utils +from . import utils __author__ = 'jdavid@itaapy.com (J. David Ibáñez)' diff --git a/test/test_refs.py b/test/test_refs.py index 8b16122ba..de2f86883 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -28,11 +28,12 @@ """Tests for reference objects.""" +from __future__ import absolute_import from __future__ import unicode_literals import unittest from pygit2 import GIT_REF_OID, GIT_REF_SYMBOLIC -import utils +from . import utils __author__ = 'david.versmisse@itaapy.com (David Versmisse)' diff --git a/test/test_repository.py b/test/test_repository.py index 4f4546a8a..ce953feff 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -27,6 +27,7 @@ """Tests for Repository objects.""" +from __future__ import absolute_import from __future__ import unicode_literals import binascii import unittest @@ -35,7 +36,7 @@ from pygit2 import (GitError, GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, init_repository) -import utils +from . import utils __author__ = 'dborowitz@google.com (Dave Borowitz)' diff --git a/test/test_revwalk.py b/test/test_revwalk.py index 5ea249799..cc65d98b5 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -28,11 +28,12 @@ """Tests for revision walk.""" +from __future__ import absolute_import from __future__ import unicode_literals import unittest from pygit2 import GIT_SORT_TIME, GIT_SORT_REVERSE -import utils +from . import utils __author__ = 'jdavid@itaapy.com (J. David Ibáñez)' diff --git a/test/test_status.py b/test/test_status.py index a01d6714b..e594b5a98 100644 --- a/test/test_status.py +++ b/test/test_status.py @@ -28,11 +28,12 @@ """Tests for revision walk.""" +from __future__ import absolute_import from __future__ import unicode_literals import unittest import pygit2 -import utils +from . import utils __author__ = 'mike.perdide@gmail.com (Julien Miotte)' diff --git a/test/test_tag.py b/test/test_tag.py index d797c4da1..ee9974656 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -27,11 +27,12 @@ """Tests for Tag objects.""" +from __future__ import absolute_import from __future__ import unicode_literals import unittest import pygit2 -import utils +from . import utils __author__ = 'dborowitz@google.com (Dave Borowitz)' diff --git a/test/test_tree.py b/test/test_tree.py index 3864911b1..3252c69bf 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -27,12 +27,13 @@ """Tests for Commit objects.""" +from __future__ import absolute_import from __future__ import unicode_literals import operator import unittest import pygit2 -import utils +from . import utils __author__ = 'dborowitz@google.com (Dave Borowitz)' From d863a8ea1713b73918a4f4b7680503c1988094e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 24 Aug 2011 07:30:09 +0200 Subject: [PATCH 0068/2237] Remove broken test with Python 3 Now all tests pass with Python 3. --- test/test_repository.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/test_repository.py b/test/test_repository.py index ce953feff..c3e13759c 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -67,9 +67,6 @@ def test_write(self): hex_sha = self.repo.write(GIT_OBJ_BLOB, data) self.assertEqual(len(hex_sha), 40) - # works as buffer as well - self.assertEqual(hex_sha, self.repo.write(GIT_OBJ_BLOB, buffer(data))) - def test_contains(self): self.assertRaises(TypeError, lambda: 123 in self.repo) self.assertTrue(A_BIN_SHA in self.repo) From fd5aab4e56e2acf7de143a43f6b15497656ec949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 24 Aug 2011 07:32:21 +0200 Subject: [PATCH 0069/2237] tests: minor coding style fix, use 4 spaces indent --- test/test_blob.py | 2 +- test/test_commit.py | 2 +- test/test_repository.py | 2 +- test/test_tag.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_blob.py b/test/test_blob.py index cb21ad2c4..96e4de0c0 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -51,4 +51,4 @@ def test_read_blob(self): if __name__ == '__main__': - unittest.main() + unittest.main() diff --git a/test/test_commit.py b/test/test_commit.py index fea9ead4c..168173a93 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -105,4 +105,4 @@ def test_modify_commit(self): if __name__ == '__main__': - unittest.main() + unittest.main() diff --git a/test/test_repository.py b/test/test_repository.py index c3e13759c..36643dca8 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -123,4 +123,4 @@ def test_new_repo(self): assert os.path.exists(os.path.join(self._temp_dir, '.git')) if __name__ == '__main__': - unittest.main() + unittest.main() diff --git a/test/test_tag.py b/test/test_tag.py index ee9974656..402a3a97d 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -88,4 +88,4 @@ def test_modify_tag(self): if __name__ == '__main__': - unittest.main() + unittest.main() From f09a02a2ee51d1bb11faed945a1d3714cf917f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 24 Aug 2011 07:42:05 +0200 Subject: [PATCH 0070/2237] Use UTF-8 encoding in all Python files --- test/__init__.py | 2 +- test/test_blob.py | 2 +- test/test_commit.py | 2 +- test/test_index.py | 1 - test/test_refs.py | 1 - test/test_repository.py | 2 +- test/test_revwalk.py | 1 - test/test_status.py | 1 - test/test_tag.py | 2 +- test/test_tree.py | 2 +- test/utils.py | 2 ++ 11 files changed, 8 insertions(+), 10 deletions(-) diff --git a/test/__init__.py b/test/__init__.py index ac535258d..237975745 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +# -*- coding: UTF-8 -*- # # Copyright 2010 Google, Inc. # diff --git a/test/test_blob.py b/test/test_blob.py index 96e4de0c0..42d92d110 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +# -*- coding: UTF-8 -*- # # Copyright 2010 Google, Inc. # diff --git a/test/test_commit.py b/test/test_commit.py index 168173a93..7fd298977 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +# -*- coding: UTF-8 -*- # # Copyright 2010 Google, Inc. # diff --git a/test/test_index.py b/test/test_index.py index 2da7e5d80..3c51792ea 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # # Copyright 2011 Itaapy diff --git a/test/test_refs.py b/test/test_refs.py index de2f86883..6f4ae84cf 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # # Copyright 2011 Itaapy diff --git a/test/test_repository.py b/test/test_repository.py index 36643dca8..65a83d8fe 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +# -*- coding: UTF-8 -*- # # Copyright 2010 Google, Inc. # diff --git a/test/test_revwalk.py b/test/test_revwalk.py index cc65d98b5..6180260f8 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # # Copyright 2011 Itaapy diff --git a/test/test_status.py b/test/test_status.py index e594b5a98..39d646259 100644 --- a/test/test_status.py +++ b/test/test_status.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # # Copyright 2011 Julien Miotte diff --git a/test/test_tag.py b/test/test_tag.py index 402a3a97d..5c7eb1202 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +# -*- coding: UTF-8 -*- # # Copyright 2010 Google, Inc. # diff --git a/test/test_tree.py b/test/test_tree.py index 3252c69bf..2b26dddf9 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +# -*- coding: UTF-8 -*- # # Copyright 2010 Google, Inc. # diff --git a/test/utils.py b/test/utils.py index 6ccf9094b..55035cbc9 100644 --- a/test/utils.py +++ b/test/utils.py @@ -1,3 +1,5 @@ +# -*- coding: UTF-8 -*- +# # Copyright 2010 Google, Inc. # # This file is free software; you can redistribute it and/or modify From 05058d81676bf8ce5da4979402558b5a12e3754e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 24 Aug 2011 08:52:41 +0200 Subject: [PATCH 0071/2237] Commit messages with non-ascii chars now work And so do author & committers names --- pygit2.c | 52 ++++++++++++++++++++++++++++++--------------- test/test_commit.py | 6 +++--- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/pygit2.c b/pygit2.c index bba247000..2b24ef86a 100644 --- a/pygit2.c +++ b/pygit2.c @@ -566,23 +566,30 @@ Repository_walk(Repository *self, PyObject *args) } static PyObject * -build_person(const git_signature *signature) +build_person(const git_signature *signature, const char *encoding) { - return Py_BuildValue("(ssLi)", signature->name, signature->email, + PyObject *name; + + name = PyUnicode_Decode(signature->name, strlen(signature->name), + encoding, "strict"); + return Py_BuildValue("(NsLi)", name, signature->email, signature->when.time, signature->when.offset); } static int signature_converter(PyObject *value, git_signature **signature) { + PyObject *py_name; char *name, *email; long long time; int offset; int err; - if (!PyArg_ParseTuple(value, "ssLi", &name, &email, &time, &offset)) + if (!PyArg_ParseTuple(value, "OsLi", &py_name, &email, &time, &offset)) return 0; + name = py_str_to_c_str(py_name); + err = git_signature_new(signature, name, email, time, offset); if (err < 0) { Error_set(err); @@ -610,20 +617,22 @@ Repository_create_commit(Repository *self, PyObject *args) char *message, *update_ref; git_oid oid; git_tree *tree; - PyObject *py_parents, *py_parent; + PyObject *py_message, *py_parents, *py_parent; int parent_count; git_commit **parents; int err, i; - if (!PyArg_ParseTuple(args, "zO&O&sO&O!", + if (!PyArg_ParseTuple(args, "zO&O&OO&O!", &update_ref, signature_converter, &author, signature_converter, &committer, - &message, + &py_message, py_str_to_git_oid, &oid, &PyList_Type, &py_parents)) return NULL; + message = py_str_to_c_str(py_message); + err = git_tree_lookup(&tree, self->repo, &oid); if (err < 0) return Error_set(err); @@ -1062,14 +1071,13 @@ Commit_get_message_encoding(Commit *commit) static PyObject * Commit_get_message(Commit *commit) { - const char *encoding; - const char *message; - int len; + const char *message, *encoding; - encoding = git_commit_message_encoding(commit->commit); message = git_commit_message(commit->commit); - len = strlen(message); - return PyUnicode_Decode(message, (Py_ssize_t)len, encoding, "strict"); + encoding = git_commit_message_encoding(commit->commit); + if (encoding == NULL) + encoding = "utf-8"; + return PyUnicode_Decode(message, strlen(message), encoding, "strict"); } static PyObject * @@ -1087,17 +1095,27 @@ Commit_get_commit_time_offset(Commit *commit) static PyObject * Commit_get_committer(Commit *commit) { - const git_signature *signature = git_commit_committer(commit->commit); + const git_signature *signature; + const char *encoding; - return build_person(signature); + signature = git_commit_committer(commit->commit); + encoding = git_commit_message_encoding(commit->commit); + if (encoding == NULL) + encoding = "utf-8"; + return build_person(signature, encoding); } static PyObject * Commit_get_author(Commit *commit) { - const git_signature *signature = git_commit_author(commit->commit); + const git_signature *signature; + const char *encoding; - return build_person(signature); + signature = git_commit_author(commit->commit); + encoding = git_commit_message_encoding(commit->commit); + if (encoding == NULL) + encoding = "utf-8"; + return build_person(signature, encoding); } static PyObject * @@ -1613,7 +1631,7 @@ Tag_get_tagger(Tag *tag) const git_signature *signature = git_tag_tagger(tag->tag); if (!signature) Py_RETURN_NONE; - return build_person(signature); + return build_person(signature, "utf-8"); } static PyObject * diff --git a/test/test_commit.py b/test/test_commit.py index 7fd298977..6520ec131 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -67,9 +67,9 @@ def test_read_commit(self): def test_new_commit(self): repo = self.repo - message = 'New commit.\n\nMessage.\n' + message = 'New commit.\n\nMessage with non-ascii chars: ééé.\n' committer = ('John Doe', 'jdoe@example.com', 12346, 0) - author = ('Jane Doe', 'jdoe2@example.com', 12345, 0) + author = ('J. David Ibáñez', 'jdavid@example.com', 12345, 0) tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' parents = [COMMIT_SHA] @@ -78,7 +78,7 @@ def test_new_commit(self): commit = repo[sha] self.assertEqual(GIT_OBJ_COMMIT, commit.type) - self.assertEqual('30bb126a4959290987fc07ea49f92be276dce9d6', + self.assertEqual('98286caaab3f1fde5bf52c8369b2b0423bad743b', commit.sha) self.assertEqual(None, commit.message_encoding) self.assertEqual(message, commit.message) From 0b23ea3feac09d8cd07156db5353995a9e461c73 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 31 Aug 2011 15:51:42 -0700 Subject: [PATCH 0072/2237] Use realpath instead of abspath to properly handle symlinked temp dirs Fixes issue #47 --- test/test_repository.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/test_repository.py b/test/test_repository.py index 65a83d8fe..28f2011c9 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -32,7 +32,7 @@ import binascii import unittest import os -from os.path import join, abspath +from os.path import join, realpath from pygit2 import (GitError, GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, init_repository) @@ -91,8 +91,8 @@ def test_lookup_commit(self): commit.message) def test_get_path(self): - directory = abspath(self.repo.path) - expected = abspath(join(self._temp_dir, 'testrepo.git')) + directory = realpath(self.repo.path) + expected = realpath(join(self._temp_dir, 'testrepo.git')) self.assertEqual(directory, expected) def test_get_workdir(self): @@ -103,13 +103,13 @@ def test_get_workdir(self): class RepositoryTest_II(utils.RepoTestCase): def test_get_path(self): - directory = abspath(self.repo.path) - expected = abspath(join(self._temp_dir, 'testrepo', '.git')) + directory = realpath(self.repo.path) + expected = realpath(join(self._temp_dir, 'testrepo', '.git')) self.assertEqual(directory, expected) def test_get_workdir(self): - directory = abspath(self.repo.workdir) - expected = abspath(join(self._temp_dir, 'testrepo')) + directory = realpath(self.repo.workdir) + expected = realpath(join(self._temp_dir, 'testrepo')) self.assertEqual(directory, expected) From 54dd6714f24717309b57a235f3bbc492b20f2fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 2 Sep 2011 15:43:44 +0200 Subject: [PATCH 0073/2237] Methods to create objects return raw oid Now methods that create new objects return the raw oid instead of the hexadecimal form. --- pygit2.c | 13 ++++++++----- test/test_index.py | 4 +++- test/test_repository.py | 10 ++++++---- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/pygit2.c b/pygit2.c index 2b24ef86a..e78174473 100644 --- a/pygit2.c +++ b/pygit2.c @@ -310,7 +310,10 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) return 0; } -static PyObject* +#define git_oid_to_python(oid) \ + PyString_FromStringAndSize(oid.id, GIT_OID_RAWSZ) + +static PyObject * git_oid_to_py_str(const git_oid *oid) { char hex[GIT_OID_HEXSZ]; @@ -462,7 +465,7 @@ Repository_write(Repository *self, PyObject *args) if (err < 0) return Error_set_str(err, "failed to write data"); - return git_oid_to_py_str(&oid); + return git_oid_to_python(oid); } static PyObject * @@ -663,7 +666,7 @@ Repository_create_commit(Repository *self, PyObject *args) if (err < 0) return Error_set(err); - return git_oid_to_py_str(&oid); + return git_oid_to_python(oid); } static PyObject * @@ -697,7 +700,7 @@ Repository_create_tag(Repository *self, PyObject *args) if (err < 0) return NULL; - return git_oid_to_py_str(&oid); + return git_oid_to_python(oid); } static PyObject * @@ -1933,7 +1936,7 @@ Index_create_tree(Index *self) if (err < 0) return Error_set(err); - return git_oid_to_py_str(&oid); + return git_oid_to_python(oid); } static PyMethodDef Index_methods[] = { diff --git a/test/test_index.py b/test/test_index.py index 3c51792ea..6e7307862 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -29,6 +29,7 @@ from __future__ import absolute_import from __future__ import unicode_literals +from binascii import b2a_hex import os import unittest @@ -91,7 +92,8 @@ def test_write(self): self.assertTrue('bye.txt' in index) def test_create_tree(self): - sha = self.repo.index.create_tree() + oid = self.repo.index.create_tree() + sha = b2a_hex(oid).decode('ascii') self.assertEqual(sha, 'fd937514cb799514d4b81bb24c5fcfeb6472b245') def test_iter(self): diff --git a/test/test_repository.py b/test/test_repository.py index 28f2011c9..56a401f57 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -64,8 +64,9 @@ def test_write(self): # invalid object type self.assertRaises(GitError, self.repo.write, GIT_OBJ_ANY, data) - hex_sha = self.repo.write(GIT_OBJ_BLOB, data) - self.assertEqual(len(hex_sha), 40) + oid = self.repo.write(GIT_OBJ_BLOB, data) + self.assertEqual(type(oid), bytes) + self.assertEqual(len(oid), 20) def test_contains(self): self.assertRaises(TypeError, lambda: 123 in self.repo) @@ -117,8 +118,9 @@ class NewRepositoryTest(utils.NoRepoTestCase): def test_new_repo(self): repo = init_repository(self.temp_dir, False) - hex_sha = repo.write(GIT_OBJ_BLOB, "Test") - self.assertEqual(len(hex_sha), 40) + oid = repo.write(GIT_OBJ_BLOB, "Test") + self.assertEqual(type(oid), bytes) + self.assertEqual(len(oid), 20) assert os.path.exists(os.path.join(self._temp_dir, '.git')) From 11ff1842b7a621361a8110029b4be1558009ddb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 2 Sep 2011 16:12:05 +0200 Subject: [PATCH 0074/2237] Add Object.oid to get the raw object id --- pygit2.c | 19 ++++++++++++++++--- test/test_blob.py | 4 ++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/pygit2.c b/pygit2.c index e78174473..62e2933b6 100644 --- a/pygit2.c +++ b/pygit2.c @@ -959,9 +959,15 @@ Object_dealloc(Object* self) } static PyObject * -Object_get_type(Object *self) +Object_get_oid(Object *self) { - return PyInt_FromLong(git_object_type(self->obj)); + const git_oid *oid; + + oid = git_object_id(self->obj); + if (!oid) + Py_RETURN_NONE; + + return PyString_FromStringAndSize(oid->id, GIT_OID_RAWSZ); } static PyObject * @@ -976,6 +982,12 @@ Object_get_sha(Object *self) return git_oid_to_py_str(oid); } +static PyObject * +Object_get_type(Object *self) +{ + return PyInt_FromLong(git_object_type(self->obj)); +} + static PyObject * Object_read_raw(Object *self) { @@ -1007,8 +1019,9 @@ Object_read_raw(Object *self) } static PyGetSetDef Object_getseters[] = { - {"type", (getter)Object_get_type, NULL, "type number", NULL}, + {"oid", (getter)Object_get_oid, NULL, "object id", NULL}, {"sha", (getter)Object_get_sha, NULL, "hex SHA", NULL}, + {"type", (getter)Object_get_type, NULL, "type number", NULL}, {NULL} }; diff --git a/test/test_blob.py b/test/test_blob.py index 42d92d110..50a9eb557 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -29,6 +29,7 @@ from __future__ import absolute_import from __future__ import unicode_literals +from binascii import b2a_hex import unittest import pygit2 @@ -44,6 +45,9 @@ class BlobTest(utils.BareRepoTestCase): def test_read_blob(self): blob = self.repo[BLOB_SHA] + self.assertEqual(blob.sha, BLOB_SHA) + sha = b2a_hex(blob.oid).decode('ascii') + self.assertEqual(sha, BLOB_SHA) self.assertTrue(isinstance(blob, pygit2.Blob)) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) self.assertEqual(b'a contents\n', blob.data) From 9f2e61cd8fa05e9ad60f0fbbfc54c93b09958e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 2 Sep 2011 16:48:35 +0200 Subject: [PATCH 0075/2237] Add TreeEntry.oid, IndexEntry.oid, Reference.oid --- pygit2.c | 47 ++++++++++++++++++++++++++++++++++++++++++----- test/test_refs.py | 2 +- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/pygit2.c b/pygit2.c index 62e2933b6..b4d3f0f45 100644 --- a/pygit2.c +++ b/pygit2.c @@ -859,7 +859,7 @@ static PyMethodDef Repository_methods[] = { "Read raw object data from the repository."}, {"write", (PyCFunction)Repository_write, METH_VARARGS, "Write raw object data into the repository. First arg is the object\n" - "type, the second one a buffer with data. Return the hexadecimal sha\n" + "type, the second one a buffer with data. Return the object id (sha)\n" "of the created object."}, {"listall_references", (PyCFunction)Repository_listall_references, METH_VARARGS, @@ -1264,6 +1264,15 @@ TreeEntry_get_name(TreeEntry *self) return c_str_to_py_str(git_tree_entry_name(self->entry)); } +static PyObject * +TreeEntry_get_oid(TreeEntry *self) +{ + const git_oid *oid; + + oid = git_tree_entry_id(self->entry); + return PyString_FromStringAndSize(oid->id, GIT_OID_RAWSZ); +} + static PyObject * TreeEntry_get_sha(TreeEntry *self) { @@ -1282,6 +1291,7 @@ TreeEntry_to_object(TreeEntry *self) static PyGetSetDef TreeEntry_getseters[] = { {"attributes", (getter)TreeEntry_get_attributes, NULL, "attributes", NULL}, {"name", (getter)TreeEntry_get_name, NULL, "name", NULL}, + {"oid", (getter)TreeEntry_get_oid, NULL, "oid", NULL}, {"sha", (getter)TreeEntry_get_sha, NULL, "sha", NULL}, {NULL} }; @@ -2099,6 +2109,12 @@ IndexEntry_get_path(IndexEntry *self) return c_str_to_py_str(self->entry->path); } +static PyObject * +IndexEntry_get_oid(IndexEntry *self) +{ + return git_oid_to_python(self->entry->oid); +} + static PyObject * IndexEntry_get_sha(IndexEntry *self) { @@ -2108,6 +2124,7 @@ IndexEntry_get_sha(IndexEntry *self) static PyGetSetDef IndexEntry_getseters[] = { {"mode", (getter)IndexEntry_get_mode, NULL, "mode", NULL}, {"path", (getter)IndexEntry_get_path, NULL, "path", NULL}, + {"oid", (getter)IndexEntry_get_oid, NULL, "object id", NULL}, {"sha", (getter)IndexEntry_get_sha, NULL, "hex SHA", NULL}, {NULL}, }; @@ -2400,7 +2417,7 @@ Reference_get_name(Reference *self) } static PyObject * -Reference_get_sha(Reference *self) +Reference_get_oid(Reference *self) { const git_oid *oid; @@ -2415,11 +2432,11 @@ Reference_get_sha(Reference *self) } /* 2- Convert and return it */ - return git_oid_to_py_str(oid); + return PyString_FromStringAndSize(oid->id, GIT_OID_RAWSZ); } static int -Reference_set_sha(Reference *self, PyObject *py_sha) +Reference_set_oid(Reference *self, PyObject *py_sha) { git_oid oid; int err; @@ -2439,6 +2456,25 @@ Reference_set_sha(Reference *self, PyObject *py_sha) return 0; } +static PyObject * +Reference_get_sha(Reference *self) +{ + const git_oid *oid; + + /* 1- Get the oid (only for "direct" references) */ + oid = git_reference_oid(self->reference); + if (oid == NULL) + { + PyErr_Format(PyExc_ValueError, + "sha is only available if the reference is direct " + "(i.e. not symbolic)"); + return NULL; + } + + /* 2- Convert and return it */ + return git_oid_to_py_str(oid); +} + static PyObject * Reference_get_type(Reference *self) { @@ -2461,8 +2497,9 @@ static PyMethodDef Reference_methods[] = { static PyGetSetDef Reference_getseters[] = { {"name", (getter)Reference_get_name, NULL, "The full name of a reference.", NULL}, - {"sha", (getter)Reference_get_sha, (setter)Reference_set_sha, "hex SHA", + {"oid", (getter)Reference_get_oid, (setter)Reference_set_oid, "object id", NULL}, + {"sha", (getter)Reference_get_sha, NULL, "hex SHA", NULL}, {"target", (getter)Reference_get_target, (setter)Reference_set_target, "target", NULL}, {"type", (getter)Reference_get_type, NULL, diff --git a/test/test_refs.py b/test/test_refs.py index 6f4ae84cf..21c07fd20 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -81,7 +81,7 @@ def test_reference_get_sha(self): def test_reference_set_sha(self): NEW_COMMIT = '5ebeeebb320790caf276b9fc8b24546d63316533' reference = self.repo.lookup_reference('refs/heads/master') - reference.sha = NEW_COMMIT + reference.oid = NEW_COMMIT self.assertEqual(reference.sha, NEW_COMMIT) From 83642a69541a4c93ef1706805214eb7e936f5e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 2 Sep 2011 16:53:17 +0200 Subject: [PATCH 0076/2237] Rename '.sha' to '.hex' --- pygit2.c | 38 +++++++++++++++++++------------------- test/test_blob.py | 2 +- test/test_commit.py | 12 ++++++------ test/test_index.py | 14 +++++++------- test/test_refs.py | 8 ++++---- test/test_repository.py | 6 +++--- test/test_revwalk.py | 12 ++++++------ test/test_tag.py | 6 +++--- test/test_tree.py | 8 ++++---- 9 files changed, 53 insertions(+), 53 deletions(-) diff --git a/pygit2.c b/pygit2.c index b4d3f0f45..c87dae409 100644 --- a/pygit2.c +++ b/pygit2.c @@ -971,7 +971,7 @@ Object_get_oid(Object *self) } static PyObject * -Object_get_sha(Object *self) +Object_get_hex(Object *self) { const git_oid *oid; @@ -994,7 +994,7 @@ Object_read_raw(Object *self) const git_oid *id; git_odb_object *obj; int err; - PyObject *result = NULL, *py_sha = NULL; + PyObject *result = NULL, *py_hex = NULL; id = git_object_id(self->obj); if (!id) @@ -1002,8 +1002,8 @@ Object_read_raw(Object *self) err = Repository_read_raw(&obj, self->repo->repo, id); if (err < 0) { - py_sha = Object_get_sha(self); - Error_set_py_obj(err, py_sha); + py_hex = Object_get_hex(self); + Error_set_py_obj(err, py_hex); goto cleanup; } @@ -1014,13 +1014,13 @@ Object_read_raw(Object *self) git_odb_object_close(obj); cleanup: - Py_XDECREF(py_sha); + Py_XDECREF(py_hex); return result; } static PyGetSetDef Object_getseters[] = { {"oid", (getter)Object_get_oid, NULL, "object id", NULL}, - {"sha", (getter)Object_get_sha, NULL, "hex SHA", NULL}, + {"hex", (getter)Object_get_hex, NULL, "hex oid", NULL}, {"type", (getter)Object_get_type, NULL, "type number", NULL}, {NULL} }; @@ -1274,7 +1274,7 @@ TreeEntry_get_oid(TreeEntry *self) } static PyObject * -TreeEntry_get_sha(TreeEntry *self) +TreeEntry_get_hex(TreeEntry *self) { return git_oid_to_py_str(git_tree_entry_id(self->entry)); } @@ -1291,8 +1291,8 @@ TreeEntry_to_object(TreeEntry *self) static PyGetSetDef TreeEntry_getseters[] = { {"attributes", (getter)TreeEntry_get_attributes, NULL, "attributes", NULL}, {"name", (getter)TreeEntry_get_name, NULL, "name", NULL}, - {"oid", (getter)TreeEntry_get_oid, NULL, "oid", NULL}, - {"sha", (getter)TreeEntry_get_sha, NULL, "sha", NULL}, + {"oid", (getter)TreeEntry_get_oid, NULL, "object id", NULL}, + {"hex", (getter)TreeEntry_get_hex, NULL, "hex oid", NULL}, {NULL} }; @@ -2116,7 +2116,7 @@ IndexEntry_get_oid(IndexEntry *self) } static PyObject * -IndexEntry_get_sha(IndexEntry *self) +IndexEntry_get_hex(IndexEntry *self) { return git_oid_to_py_str(&self->entry->oid); } @@ -2125,7 +2125,7 @@ static PyGetSetDef IndexEntry_getseters[] = { {"mode", (getter)IndexEntry_get_mode, NULL, "mode", NULL}, {"path", (getter)IndexEntry_get_path, NULL, "path", NULL}, {"oid", (getter)IndexEntry_get_oid, NULL, "object id", NULL}, - {"sha", (getter)IndexEntry_get_sha, NULL, "hex SHA", NULL}, + {"hex", (getter)IndexEntry_get_hex, NULL, "hex oid", NULL}, {NULL}, }; @@ -2426,7 +2426,7 @@ Reference_get_oid(Reference *self) if (oid == NULL) { PyErr_Format(PyExc_ValueError, - "sha is only available if the reference is direct " + "oid is only available if the reference is direct " "(i.e. not symbolic)"); return NULL; } @@ -2436,17 +2436,17 @@ Reference_get_oid(Reference *self) } static int -Reference_set_oid(Reference *self, PyObject *py_sha) +Reference_set_oid(Reference *self, PyObject *py_hex) { git_oid oid; int err; - /* 1- Get the oid from the py_sha */ - if (!py_str_to_git_oid(py_sha, &oid)) + /* 1- Get the oid */ + if (!py_str_to_git_oid(py_hex, &oid)) return -1; /* 2- Set the oid */ - err = git_reference_set_oid (self->reference, &oid); + err = git_reference_set_oid(self->reference, &oid); if (err < 0) { Error_set(err); return -1; @@ -2457,7 +2457,7 @@ Reference_set_oid(Reference *self, PyObject *py_sha) } static PyObject * -Reference_get_sha(Reference *self) +Reference_get_hex(Reference *self) { const git_oid *oid; @@ -2466,7 +2466,7 @@ Reference_get_sha(Reference *self) if (oid == NULL) { PyErr_Format(PyExc_ValueError, - "sha is only available if the reference is direct " + "oid is only available if the reference is direct " "(i.e. not symbolic)"); return NULL; } @@ -2499,7 +2499,7 @@ static PyGetSetDef Reference_getseters[] = { "The full name of a reference.", NULL}, {"oid", (getter)Reference_get_oid, (setter)Reference_set_oid, "object id", NULL}, - {"sha", (getter)Reference_get_sha, NULL, "hex SHA", NULL}, + {"hex", (getter)Reference_get_hex, NULL, "hex oid", NULL}, {"target", (getter)Reference_get_target, (setter)Reference_set_target, "target", NULL}, {"type", (getter)Reference_get_type, NULL, diff --git a/test/test_blob.py b/test/test_blob.py index 50a9eb557..a8658724b 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -45,7 +45,7 @@ class BlobTest(utils.BareRepoTestCase): def test_read_blob(self): blob = self.repo[BLOB_SHA] - self.assertEqual(blob.sha, BLOB_SHA) + self.assertEqual(blob.hex, BLOB_SHA) sha = b2a_hex(blob.oid).decode('ascii') self.assertEqual(sha, BLOB_SHA) self.assertTrue(isinstance(blob, pygit2.Blob)) diff --git a/test/test_commit.py b/test/test_commit.py index 6520ec131..f67fd7483 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -44,11 +44,11 @@ class CommitTest(utils.BareRepoTestCase): def test_read_commit(self): commit = self.repo[COMMIT_SHA] - self.assertEqual(COMMIT_SHA, commit.sha) + self.assertEqual(COMMIT_SHA, commit.hex) parents = commit.parents self.assertEqual(1, len(parents)) self.assertEqual('c2792cfa289ae6321ecf2cd5806c2194b0fd070c', - parents[0].sha) + parents[0].hex) self.assertEqual(None, commit.message_encoding) #self.assertEqual('Second test data commit.', commit.message_short) self.assertEqual(('Second test data commit.\n\n' @@ -63,7 +63,7 @@ def test_read_commit(self): ('Dave Borowitz', 'dborowitz@google.com', 1288477363, -420), commit.author) self.assertEqual( - '967fce8df97cc71722d3c2a5930ef3e6f1d27b12', commit.tree.sha) + '967fce8df97cc71722d3c2a5930ef3e6f1d27b12', commit.tree.hex) def test_new_commit(self): repo = self.repo @@ -79,16 +79,16 @@ def test_new_commit(self): self.assertEqual(GIT_OBJ_COMMIT, commit.type) self.assertEqual('98286caaab3f1fde5bf52c8369b2b0423bad743b', - commit.sha) + commit.hex) self.assertEqual(None, commit.message_encoding) self.assertEqual(message, commit.message) #self.assertEqual('New commit.', commit.message_short) self.assertEqual(12346, commit.commit_time) self.assertEqual(committer, commit.committer) self.assertEqual(author, commit.author) - self.assertEqual(tree, commit.tree.sha) + self.assertEqual(tree, commit.tree.hex) self.assertEqual(1, len(commit.parents)) - self.assertEqual(COMMIT_SHA, commit.parents[0].sha) + self.assertEqual(COMMIT_SHA, commit.parents[0].hex) def test_modify_commit(self): message = 'New commit.\n\nMessage.\n' diff --git a/test/test_index.py b/test/test_index.py index 6e7307862..5df397b1e 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -61,9 +61,9 @@ def test_read(self): sha = 'a520c24d85fbfc815d385957eed41406ca5a860b' self.assertTrue('hello.txt' in index) - self.assertEqual(index['hello.txt'].sha, sha) + self.assertEqual(index['hello.txt'].hex, sha) self.assertEqual(index['hello.txt'].path, 'hello.txt') - self.assertEqual(index[1].sha, sha) + self.assertEqual(index[1].hex, sha) def test_add(self): index = self.repo.index @@ -73,7 +73,7 @@ def test_add(self): index.add('bye.txt') self.assertTrue('bye.txt' in index) self.assertEqual(len(index), 3) - self.assertEqual(index['bye.txt'].sha, sha) + self.assertEqual(index['bye.txt'].hex, sha) def test_clear(self): index = self.repo.index @@ -102,8 +102,8 @@ def test_iter(self): self.assertEqual(len(list(index)), n) # Compare SHAs, not IndexEntry object identity - entries = [index[x].sha for x in range(n)] - self.assertEqual(list(x.sha for x in index), entries) + entries = [index[x].hex for x in range(n)] + self.assertEqual(list(x.hex for x in index), entries) def test_mode(self): """ @@ -116,8 +116,8 @@ def test_mode(self): def test_bare_index(self): index = pygit2.Index(os.path.join(self.repo.path, 'index')) - self.assertEqual([x.sha for x in index], - [x.sha for x in self.repo.index]) + self.assertEqual([x.hex for x in index], + [x.hex for x in self.repo.index]) self.assertRaises(pygit2.GitError, lambda: index.add('bye.txt', 0)) diff --git a/test/test_refs.py b/test/test_refs.py index 21c07fd20..e457d8266 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -75,14 +75,14 @@ def test_lookup_reference(self): def test_reference_get_sha(self): reference = self.repo.lookup_reference('refs/heads/master') - self.assertEqual(reference.sha, LAST_COMMIT) + self.assertEqual(reference.hex, LAST_COMMIT) def test_reference_set_sha(self): NEW_COMMIT = '5ebeeebb320790caf276b9fc8b24546d63316533' reference = self.repo.lookup_reference('refs/heads/master') reference.oid = NEW_COMMIT - self.assertEqual(reference.sha, NEW_COMMIT) + self.assertEqual(reference.hex, NEW_COMMIT) def test_reference_get_type(self): @@ -128,7 +128,7 @@ def test_reference_resolve(self): self.assertEqual(reference.type, GIT_REF_SYMBOLIC) reference = reference.resolve() self.assertEqual(reference.type, GIT_REF_OID) - self.assertEqual(reference.sha, LAST_COMMIT) + self.assertEqual(reference.hex, LAST_COMMIT) def test_create_reference(self): @@ -138,7 +138,7 @@ def test_create_reference(self): refs = self.repo.listall_references() self.assertTrue('refs/tags/version1' in refs) reference = self.repo.lookup_reference('refs/tags/version1') - self.assertEqual(reference.sha, LAST_COMMIT) + self.assertEqual(reference.hex, LAST_COMMIT) def test_create_symbolic_reference(self): diff --git a/test/test_repository.py b/test/test_repository.py index 56a401f57..303ea5aca 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -76,16 +76,16 @@ def test_contains(self): def test_lookup_blob(self): self.assertRaises(TypeError, lambda: self.repo[123]) - self.assertEqual(self.repo[A_BIN_SHA].sha, A_HEX_SHA) + self.assertEqual(self.repo[A_BIN_SHA].hex, A_HEX_SHA) a = self.repo[A_HEX_SHA] self.assertEqual(b'a contents\n', a.read_raw()) - self.assertEqual(A_HEX_SHA, a.sha) + self.assertEqual(A_HEX_SHA, a.hex) self.assertEqual(GIT_OBJ_BLOB, a.type) def test_lookup_commit(self): commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10' commit = self.repo[commit_sha] - self.assertEqual(commit_sha, commit.sha) + self.assertEqual(commit_sha, commit.hex) self.assertEqual(GIT_OBJ_COMMIT, commit.type) self.assertEqual(('Second test data commit.\n\n' 'This commit has some additional text.\n'), diff --git a/test/test_revwalk.py b/test/test_revwalk.py index 6180260f8..34e386648 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -51,12 +51,12 @@ class WalkerTest(utils.RepoTestCase): def test_walk(self): walker = self.repo.walk(log[0], GIT_SORT_TIME) - out = [ x.sha for x in walker ] + out = [ x.hex for x in walker ] self.assertEqual(out, log) def test_reverse(self): walker = self.repo.walk(log[0], GIT_SORT_TIME | GIT_SORT_REVERSE) - out = [ x.sha for x in walker ] + out = [ x.hex for x in walker ] self.assertEqual(out, list(reversed(log))) def test_hide(self): @@ -67,22 +67,22 @@ def test_hide(self): def test_reset(self): walker = self.repo.walk(log[0], GIT_SORT_TIME) walker.reset() - out = [ x.sha for x in walker ] + out = [ x.hex for x in walker ] self.assertEqual(out, []) def test_push(self): walker = self.repo.walk(log[-1], GIT_SORT_TIME) - out = [ x.sha for x in walker ] + out = [ x.hex for x in walker ] self.assertEqual(out, log[-1:]) walker.reset() walker.push(log[0]) - out = [ x.sha for x in walker ] + out = [ x.hex for x in walker ] self.assertEqual(out, log) def test_sort(self): walker = self.repo.walk(log[0], GIT_SORT_TIME) walker.sort(GIT_SORT_TIME | GIT_SORT_REVERSE) - out = [ x.sha for x in walker ] + out = [ x.hex for x in walker ] self.assertEqual(out, list(reversed(log))) if __name__ == '__main__': diff --git a/test/test_tag.py b/test/test_tag.py index 5c7eb1202..0c8989df8 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -67,12 +67,12 @@ def test_new_tag(self): message) tag = self.repo[sha] - self.assertEqual('3ee44658fd11660e828dfc96b9b5c5f38d5b49bb', tag.sha) + self.assertEqual('3ee44658fd11660e828dfc96b9b5c5f38d5b49bb', tag.hex) self.assertEqual(name, tag.name) - self.assertEqual(target, tag.target.sha) + self.assertEqual(target, tag.target.hex) self.assertEqual(tagger, tag.tagger) self.assertEqual(message, tag.message) - self.assertEqual(name, self.repo[tag.sha].name) + self.assertEqual(name, self.repo[tag.hex].name) def test_modify_tag(self): name = 'thetag' diff --git a/test/test_tree.py b/test/test_tree.py index 2b26dddf9..571f702cc 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -45,7 +45,7 @@ class TreeTest(utils.BareRepoTestCase): def assertTreeEntryEqual(self, entry, sha, name, attributes): - self.assertEqual(entry.sha, sha) + self.assertEqual(entry.hex, sha) self.assertEqual(entry.name, name) self.assertEqual(entry.attributes, attributes, '0%o != 0%o' % (entry.attributes, attributes)) @@ -100,11 +100,11 @@ def xtest_new_tree(self): del tree[2] self.assertEqual(2, len(tree)) - self.assertEqual(None, tree.sha) + self.assertEqual(None, tree.hex) tree.write() contents = '100644 x\0%s100755 y\0%s' % ('\x11' * 20, '\x22' * 20) self.assertEqual((pygit2.GIT_OBJ_TREE, contents), - self.repo.read(tree.sha)) + self.repo.read(tree.hex)) def test_modify_tree(self): tree = self.repo[TREE_SHA] @@ -119,7 +119,7 @@ def test_iterate_tree(self): """ tree = self.repo[TREE_SHA] for tree_entry in tree: - self.assertEqual(tree_entry.sha, tree[tree_entry.name].sha) + self.assertEqual(tree_entry.hex, tree[tree_entry.name].hex) if __name__ == '__main__': From 78deb5578d013cdbdf086fa1d1a3e7d982e8888f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 3 Sep 2011 01:13:18 +0200 Subject: [PATCH 0077/2237] Update readme file --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a39fdcaab..32b30b90b 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,8 @@ Index read: >>> index = repo.index >>> index.read() - >>> sha = index['path/to/file'].sha # from path to sha - >>> blob = repo[sha] # from sha to blob + >>> oid = index['path/to/file'].oid # from path to object id + >>> blob = repo[oid] # from object id to object Inspect the status of the repository: @@ -59,7 +59,7 @@ Inspect the status of the repository: Iterate over all entries of the index: >>> for entry in index: - ... print entry.path, entry.sha + ... print entry.path, entry.hex Index write: @@ -70,20 +70,20 @@ Index write: Revision walking: >>> from pygit2 import GIT_SORT_TIME - >>> for commit in repo.walk(sha, GIT_SORT_TIME): - ... print commit.sha + >>> for commit in repo.walk(oid, GIT_SORT_TIME): + ... print commit.hex Read commit information: >>> master_ref = repo.lookup_reference("refs/heads/master") # Getting the Reference object - >>> commit = repo[master_ref.sha] + >>> commit = repo[master_ref.oid] >>> [name, email, timestamp, tz_offset] = commit.author # Read the commit authored infos >>> tree = commit.tree # Access the tree of the commit Iterate over all entries of the tree: >>> for entry in tree: - ... print entry.name, entry.sha, entry.attributes + ... print entry.name, entry.hex, entry.attributes CONTRIBUTING From d36ad6fd7a4d79b65710eb851a7a4dc91e1b637c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 3 Sep 2011 16:34:15 +0200 Subject: [PATCH 0078/2237] Working on the docs, talk more about objects --- README.md | 182 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 157 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 32b30b90b..b9e0e85ec 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ pygit2 - libgit2 bindings in Python pygit2 is a set of Python bindings to the libgit2 linkable C Git library. The supported versions of Python are 2.6, 2.7, 3.1 and 3.2 +Through this text Python 3 is used for the inline examples. Also, the Python +3 terminology is used (for instance we say text strings instead of unicode +strings). + INSTALLING AND RUNNING ======================== @@ -27,8 +31,124 @@ When those are installed, you can install pygit2: $ python setup.py test -USING -====== +Git objects +=========== + +In the first place Git is a key-value storage system. The values stored are +called *Git Objects*, there are four types (commits, trees, blobs and tags), +each type there is a Python class: + + # Open a repository + >>> from pygit2 import Repository + >>> repo = Repository('pygit2/.git') + # Get the last commit + >>> head = repo.lookup_reference('HEAD') + >>> head = head.resolve() + >>> commit = repo[head.oid] + # Show commits and trees + >>> commit + + >>> commit.tree + + +These four classes (``Commit``, ``Tree``, ``Blob`` and ``Tag``) inherit from +the ``pygit2.Object`` base class, which provides shared behaviour. A Git +object is identified by a unique *object id*, which is a binary byte string; +this is often represented as an hexadecimal text string: + + >>> commit.oid + b'x\xde\xb5W\x8d\x01<\xdb\xdf\x08o\xa1\xd1\xa3\xe7\xd9\x82\xe8\x88\x8f' + >>> commit.hex + '78deb5578d013cdbdf086fa1d1a3e7d982e8888f' + +The API of pygit2 accepts both the raw object id and its hexadecimal +representation of the object id, the difference is done based on its type +(a byte or a text string). + +This is the common interface for all Git objects: + + Object.type -- one of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, + GIT_OBJ_BLOB or GIT_OBJ_TAG constants + Object.oid -- the object id, a byte string 20 bytes long + Object.hex -- hexadecimal representation of the object id, a text + string 40 chars long + Object.read_raw() -- returns the byte string with the raw contents of the + of the object + +Objects can not be modified once they have been created. + + +Commits +----------------- + + Commit.author -- the author of the commit + Commit.committer -- the committer of the commit + Commit.message -- the message, a text string + Commit.tree -- the tree object attached to the commit + Commit.parents -- the list of parent commits + +The author and committer attributes of the commit are tuples with four +elements, the author name and email, the unix time and time offset in +minutes: + + >>> commit.author + ('J. David Ibáñez', 'jdavid.ibp@gmail.com', 1315005198, 120) + +Trees +----------------- + +A tree is a sorted collection of tree entries. It is similar to a folder or +directory in a file system. Each entry points to another tree or a blob. A +tree can be iterated, and partially implements the sequence and mapping +interfaces: + + # Number of entries + >>> tree = commit.tree + >>> len(tree) + 6 + # Iteratation + >>> for entry in tree: + ... print(entry.hex, entry.name) + ... + 7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa .gitignore + c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 COPYING + 32b30b90b062f66957d6790c3c155c289c34424e README.md + c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 pygit2.c + 85a67270a49ef16cdd3d328f06a3e4b459f09b27 setup.py + 3d8985bbec338eb4d47c5b01b863ee89d044bd53 test + # Get an entry by name + >>> entry = tree['pygit2.c'] + >>> entry + + # Get the object the entry points to + >>> blob = repo[entry.oid] + >>> blob + + +This is the interface of a tree entry: + + TreeEntry.name -- name of the tree entry + TreeEntry.oid -- the id of the git object + TreeEntry.hex -- hexadecimal representation of the oid + TreeEntry.attributes -- the Unix file attributes + TreeEntry.to_object() -- returns the git object (equivalent to repo[entry.oid]) + +Blobs +----------------- + +A blob is equivalent to a file in a file system. + + Blob.data -- the contents of the blob, a byte string + +Tags +----------------- + +XXX + + +The repository +================= + Initialize a Git repository: @@ -41,6 +161,26 @@ Open a repository: >>> from pygit2 import Repository >>> repo = Repository('test/.git') + +References +================= + +Reference lookup: + + >>> master_ref = repo.lookup_reference("refs/heads/master") + >>> commit = repo[master_ref.oid] + + +Revision walking +================= + + >>> from pygit2 import GIT_SORT_TIME + >>> for commit in repo.walk(oid, GIT_SORT_TIME): + ... print commit.hex + +The index file +================= + Index read: >>> index = repo.index @@ -48,14 +188,6 @@ Index read: >>> oid = index['path/to/file'].oid # from path to object id >>> blob = repo[oid] # from object id to object -Inspect the status of the repository: - - >>> from pygit2 import GIT_STATUS_CURRENT - >>> status = repo.status() - >>> for filepath, flags in status.items(): - ... if flags != GIT_STATUS_CURRENT: - ... print "Filepath %s isn't clean" % filepath - Iterate over all entries of the index: >>> for entry in index: @@ -67,23 +199,16 @@ Index write: >>> del index['path/to/file'] # git rm >>> index.write() # don't forget to save the changes -Revision walking: - - >>> from pygit2 import GIT_SORT_TIME - >>> for commit in repo.walk(oid, GIT_SORT_TIME): - ... print commit.hex - -Read commit information: +Status +================= - >>> master_ref = repo.lookup_reference("refs/heads/master") # Getting the Reference object - >>> commit = repo[master_ref.oid] - >>> [name, email, timestamp, tz_offset] = commit.author # Read the commit authored infos - >>> tree = commit.tree # Access the tree of the commit - -Iterate over all entries of the tree: +Inspect the status of the repository: - >>> for entry in tree: - ... print entry.name, entry.hex, entry.attributes + >>> from pygit2 import GIT_STATUS_CURRENT + >>> status = repo.status() + >>> for filepath, flags in status.items(): + ... if flags != GIT_STATUS_CURRENT: + ... print "Filepath %s isn't clean" % filepath CONTRIBUTING @@ -93,6 +218,13 @@ Fork libgit2/pygit2 on GitHub, make it awesomer (preferably in a branch named for the topic), send a pull request. +TODO +---------------- + +XXX + + + AUTHORS ============== From 7b3685aaeaee991d72fbe23b758622ac9f9a0c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 3 Sep 2011 23:58:34 +0200 Subject: [PATCH 0079/2237] git_object_id cannot return NULL --- pygit2.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/pygit2.c b/pygit2.c index c87dae409..4a282656e 100644 --- a/pygit2.c +++ b/pygit2.c @@ -964,8 +964,7 @@ Object_get_oid(Object *self) const git_oid *oid; oid = git_object_id(self->obj); - if (!oid) - Py_RETURN_NONE; + assert(oid); return PyString_FromStringAndSize(oid->id, GIT_OID_RAWSZ); } @@ -976,8 +975,7 @@ Object_get_hex(Object *self) const git_oid *oid; oid = git_object_id(self->obj); - if (!oid) - Py_RETURN_NONE; + assert(oid); return git_oid_to_py_str(oid); } @@ -991,31 +989,28 @@ Object_get_type(Object *self) static PyObject * Object_read_raw(Object *self) { - const git_oid *id; + const git_oid *oid; git_odb_object *obj; int err; - PyObject *result = NULL, *py_hex = NULL; + PyObject *aux = NULL; - id = git_object_id(self->obj); - if (!id) - Py_RETURN_NONE; /* in-memory object */ + oid = git_object_id(self->obj); + assert(oid); - err = Repository_read_raw(&obj, self->repo->repo, id); + err = Repository_read_raw(&obj, self->repo->repo, oid); if (err < 0) { - py_hex = Object_get_hex(self); - Error_set_py_obj(err, py_hex); - goto cleanup; + aux = git_oid_to_py_str(oid); + Error_set_py_obj(err, aux); + Py_XDECREF(aux); + return NULL; } - result = PyString_FromStringAndSize( + aux = PyString_FromStringAndSize( git_odb_object_data(obj), git_odb_object_size(obj)); git_odb_object_close(obj); - -cleanup: - Py_XDECREF(py_hex); - return result; + return aux; } static PyGetSetDef Object_getseters[] = { From f9b57d5eb9def4d2f1893b730ee17a622b8b30cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 4 Sep 2011 00:28:55 +0200 Subject: [PATCH 0080/2237] py_str_to_git_oid: don't decrease refcount too early Error handling still broken in py_str_to_git_oid, can be verified by typing repo['toto'] --- pygit2.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pygit2.c b/pygit2.c index 4a282656e..09e3e483d 100644 --- a/pygit2.c +++ b/pygit2.c @@ -274,6 +274,7 @@ wrap_reference(git_reference * c_reference) static int py_str_to_git_oid(PyObject *py_str, git_oid *oid) { + PyObject *py_hex; const char *hex_or_bin; int err; @@ -288,11 +289,11 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) /* Case 2: hex sha */ if (PyUnicode_Check(py_str)) { - py_str = PyUnicode_AsASCIIString(py_str); - if (py_str == NULL) + py_hex = PyUnicode_AsASCIIString(py_str); + if (py_hex == NULL) return 0; - hex_or_bin = PyString_AsString(py_str); - Py_DECREF(py_str); + hex_or_bin = PyString_AsString(py_hex); + Py_DECREF(py_hex); if (hex_or_bin == NULL) return 0; err = git_oid_fromstr(oid, hex_or_bin); From 5b52f8421bbd624765aa62d767e5f6df77bdb5a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 4 Sep 2011 00:40:25 +0200 Subject: [PATCH 0081/2237] docs: talk about the repository first --- README.md | 62 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index b9e0e85ec..cc6c850f5 100644 --- a/README.md +++ b/README.md @@ -31,20 +31,39 @@ When those are installed, you can install pygit2: $ python setup.py test +The repository +================= + +Everything starts by opening an existing repository: + + >>> from pygit2 import Repository + >>> repo = Repository('pygit2/.git') + +Or by creating a new one: + + >>> from pygit2 import init_repository + >>> bare = False + >>> repo = init_repository('test', bare) + +These are the basic attributes of a repository: + + Repository.path -- path to the Git repository + Repository.workdir -- path to the working directory, None in the case of + a bare repo + + Git objects =========== In the first place Git is a key-value storage system. The values stored are -called *Git Objects*, there are four types (commits, trees, blobs and tags), -each type there is a Python class: +called *objects*, there are four types (commits, trees, blobs and tags), +for each type pygit2 has a Python class: - # Open a repository - >>> from pygit2 import Repository - >>> repo = Repository('pygit2/.git') # Get the last commit >>> head = repo.lookup_reference('HEAD') >>> head = head.resolve() >>> commit = repo[head.oid] + # Show commits and trees >>> commit @@ -52,9 +71,9 @@ each type there is a Python class: These four classes (``Commit``, ``Tree``, ``Blob`` and ``Tag``) inherit from -the ``pygit2.Object`` base class, which provides shared behaviour. A Git -object is identified by a unique *object id*, which is a binary byte string; -this is often represented as an hexadecimal text string: +the ``Object`` base class, which provides shared behaviour. A Git object is +identified by a unique *object id*, which is a binary byte string; this is +often represented as an hexadecimal text string: >>> commit.oid b'x\xde\xb5W\x8d\x01<\xdb\xdf\x08o\xa1\xd1\xa3\xe7\xd9\x82\xe8\x88\x8f' @@ -62,8 +81,8 @@ this is often represented as an hexadecimal text string: '78deb5578d013cdbdf086fa1d1a3e7d982e8888f' The API of pygit2 accepts both the raw object id and its hexadecimal -representation of the object id, the difference is done based on its type -(a byte or a text string). +representation, the difference is done based on its type (a byte or a text +string). This is the common interface for all Git objects: @@ -87,7 +106,7 @@ Commits Commit.tree -- the tree object attached to the commit Commit.parents -- the list of parent commits -The author and committer attributes of the commit are tuples with four +The author and committer attributes of commit objects are tuples with four elements, the author name and email, the unix time and time offset in minutes: @@ -106,7 +125,8 @@ interfaces: >>> tree = commit.tree >>> len(tree) 6 - # Iteratation + + # Iteration >>> for entry in tree: ... print(entry.hex, entry.name) ... @@ -116,10 +136,12 @@ interfaces: c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 pygit2.c 85a67270a49ef16cdd3d328f06a3e4b459f09b27 setup.py 3d8985bbec338eb4d47c5b01b863ee89d044bd53 test + # Get an entry by name >>> entry = tree['pygit2.c'] >>> entry + # Get the object the entry points to >>> blob = repo[entry.oid] >>> blob @@ -146,22 +168,6 @@ Tags XXX -The repository -================= - - -Initialize a Git repository: - - >>> from pygit2 import init_repository - >>> bare = False - >>> repo = init_repository('test', bare) - -Open a repository: - - >>> from pygit2 import Repository - >>> repo = Repository('test/.git') - - References ================= From 1dde68691b16525f28c95157122dc007f1173cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 5 Oct 2011 23:11:59 +0200 Subject: [PATCH 0082/2237] Update setup file --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 85a67270a..2f03ae906 100644 --- a/setup.py +++ b/setup.py @@ -70,11 +70,11 @@ def run(self): setup(name='pygit2', description='Python bindings for libgit2.', keywords='git', - version='0.1', + version='0.15.0', url='http://github.com/libgit2/pygit2', license='GPLv2', maintainer='J. David Ibáñez', - maintainer_email='jdavid@itaapy.com', + maintainer_email='jdavid.ibp@gmail.com', long_description=""" Bindings for libgit2, a linkable C library for the Git version-control system. From ab96646e913b33baa5fd361e8ba40848276b35d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 10 Oct 2011 19:25:13 +0200 Subject: [PATCH 0083/2237] py_str_to_git_oid now supports short prefixes py_str_to_git_oid now returns the length of the oid on success. We don't yet do anything useful with the length, but every call to py_str_to_git_oid has been properly updated. This is half the work needed to fix issue #50. The changes made to py_str_to_git_oid are based on a patch by Hugh Cole-Baker. --- pygit2.c | 90 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/pygit2.c b/pygit2.c index 09e3e483d..9cf03eb1b 100644 --- a/pygit2.c +++ b/pygit2.c @@ -271,12 +271,13 @@ wrap_reference(git_reference * c_reference) return (PyObject *)py_reference; } -static int +static size_t py_str_to_git_oid(PyObject *py_str, git_oid *oid) { PyObject *py_hex; const char *hex_or_bin; int err; + size_t len; /* Case 1: raw sha */ if (PyString_Check(py_str)) { @@ -284,7 +285,7 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) if (hex_or_bin == NULL) return 0; git_oid_fromraw(oid, (const unsigned char*)hex_or_bin); - return 1; + return GIT_OID_HEXSZ; } /* Case 2: hex sha */ @@ -296,12 +297,13 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) Py_DECREF(py_hex); if (hex_or_bin == NULL) return 0; - err = git_oid_fromstr(oid, hex_or_bin); + len = strnlen(hex_or_bin, GIT_OID_HEXSZ); + err = git_oid_fromstrn(oid, hex_or_bin, len); if (err < 0) { Error_set_py_obj(err, py_str); return 0; } - return 1; + return len; } /* Type error */ @@ -311,6 +313,14 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) return 0; } +#define TODO_SUPPORT_SHORT_HEXS(len) \ + if (0 < len && len < GIT_OID_HEXSZ) {\ + PyErr_SetString(PyExc_NotImplementedError,\ + "short strings not yet supported");\ + len = 0;\ + } + + #define git_oid_to_python(oid) \ PyString_FromStringAndSize(oid.id, GIT_OID_RAWSZ) @@ -388,8 +398,11 @@ static int Repository_contains(Repository *self, PyObject *value) { git_oid oid; + size_t len; - if (!py_str_to_git_oid(value, &oid)) + len = py_str_to_git_oid(value, &oid); + TODO_SUPPORT_SHORT_HEXS(len) + if (len == 0) return -1; return git_odb_exists(git_repository_database(self->repo), &oid); @@ -399,8 +412,11 @@ static PyObject * Repository_getitem(Repository *self, PyObject *value) { git_oid oid; + size_t len; - if (!py_str_to_git_oid(value, &oid)) + len = py_str_to_git_oid(value, &oid); + TODO_SUPPORT_SHORT_HEXS(len) + if (len == 0) return NULL; return lookup_object(self, &oid, GIT_OBJ_ANY); @@ -419,8 +435,11 @@ Repository_read(Repository *self, PyObject *py_hex) git_oid oid; int err; git_odb_object *obj; + size_t len; - if (!py_str_to_git_oid(py_hex, &oid)) + len = py_str_to_git_oid(py_hex, &oid); + TODO_SUPPORT_SHORT_HEXS(len) + if (len == 0) return NULL; err = Repository_read_raw(&obj, self->repo, &oid); @@ -533,6 +552,7 @@ Repository_walk(Repository *self, PyObject *args) git_oid oid; git_revwalk *walk; Walker *py_walker; + size_t len; if (!PyArg_ParseTuple(args, "OI", &value, &sort)) return NULL; @@ -546,7 +566,9 @@ Repository_walk(Repository *self, PyObject *args) /* Push */ if (value != Py_None) { - if (!py_str_to_git_oid(value, &oid)) { + len = py_str_to_git_oid(value, &oid); + TODO_SUPPORT_SHORT_HEXS(len) + if (len == 0) { git_revwalk_free(walk); return NULL; } @@ -621,20 +643,26 @@ Repository_create_commit(Repository *self, PyObject *args) char *message, *update_ref; git_oid oid; git_tree *tree; - PyObject *py_message, *py_parents, *py_parent; + PyObject *py_oid, *py_message, *py_parents, *py_parent; int parent_count; git_commit **parents; int err, i; + size_t len; - if (!PyArg_ParseTuple(args, "zO&O&OO&O!", + if (!PyArg_ParseTuple(args, "zO&O&OOO!", &update_ref, signature_converter, &author, signature_converter, &committer, &py_message, - py_str_to_git_oid, &oid, + &py_oid, &PyList_Type, &py_parents)) return NULL; + len = py_str_to_git_oid(py_oid, &oid); + TODO_SUPPORT_SHORT_HEXS(len) + if (len == 0) + return NULL; + message = py_str_to_c_str(py_message); err = git_tree_lookup(&tree, self->repo, &oid); @@ -650,7 +678,9 @@ Repository_create_commit(Repository *self, PyObject *args) } for (i = 0; i < parent_count; i++) { py_parent = PyList_GET_ITEM(py_parents, i); - if (!py_str_to_git_oid(py_parent, &oid)) { + len = py_str_to_git_oid(py_parent, &oid); + TODO_SUPPORT_SHORT_HEXS(len) + if (len == 0) { git_tree_close(tree); return free_parents(parents, i); } @@ -673,21 +703,28 @@ Repository_create_commit(Repository *self, PyObject *args) static PyObject * Repository_create_tag(Repository *self, PyObject *args) { + PyObject *py_oid; char *tag_name, *message; git_signature *tagger; git_oid oid; git_object *target; int err, target_type; char hex[GIT_OID_HEXSZ + 1]; + size_t len; - if (!PyArg_ParseTuple(args, "sO&iO&s", + if (!PyArg_ParseTuple(args, "sOiO&s", &tag_name, - py_str_to_git_oid, &oid, + &py_oid, &target_type, signature_converter, &tagger, &message)) return NULL; + len = py_str_to_git_oid(py_oid, &oid); + TODO_SUPPORT_SHORT_HEXS(len) + if (len == 0) + return NULL; + err = git_object_lookup(&target, self->repo, &oid, target_type); if (err < 0) { git_oid_fmt(hex, &oid); @@ -771,14 +808,20 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) static PyObject * Repository_create_reference(Repository *self, PyObject *args) { + PyObject *py_oid; git_reference *c_reference; char *c_name; git_oid oid; int err; + size_t len; /* 1- Get the C variables */ - if (!PyArg_ParseTuple(args, "sO&", &c_name, - py_str_to_git_oid, &oid)) + if (!PyArg_ParseTuple(args, "sO", &c_name, &py_oid)) + return NULL; + + len = py_str_to_git_oid(py_oid, &oid); + TODO_SUPPORT_SHORT_HEXS(len) + if (len == 0) return NULL; /* 2- Create the reference */ @@ -2179,8 +2222,11 @@ Walker_hide(Walker *self, PyObject *py_hex) { int err; git_oid oid; + size_t len; - if (!py_str_to_git_oid(py_hex, &oid)) + len = py_str_to_git_oid(py_hex, &oid); + TODO_SUPPORT_SHORT_HEXS(len) + if (len == 0) return NULL; err = git_revwalk_hide(self->walk, &oid); @@ -2195,8 +2241,11 @@ Walker_push(Walker *self, PyObject *py_hex) { int err; git_oid oid; + size_t len; - if (!py_str_to_git_oid(py_hex, &oid)) + len = py_str_to_git_oid(py_hex, &oid); + TODO_SUPPORT_SHORT_HEXS(len) + if (len == 0) return NULL; err = git_revwalk_push(self->walk, &oid); @@ -2436,9 +2485,12 @@ Reference_set_oid(Reference *self, PyObject *py_hex) { git_oid oid; int err; + size_t len; /* 1- Get the oid */ - if (!py_str_to_git_oid(py_hex, &oid)) + len = py_str_to_git_oid(py_hex, &oid); + TODO_SUPPORT_SHORT_HEXS(len) + if (len == 0) return -1; /* 2- Set the oid */ From 1b14bf15bc54075f50683a6b38a188e7463559d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 14 Oct 2011 17:00:41 +0200 Subject: [PATCH 0084/2237] Fix warnings --- pygit2.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pygit2.c b/pygit2.c index 9cf03eb1b..3a613e03c 100644 --- a/pygit2.c +++ b/pygit2.c @@ -321,8 +321,8 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) } -#define git_oid_to_python(oid) \ - PyString_FromStringAndSize(oid.id, GIT_OID_RAWSZ) +#define git_oid_to_python(id) \ + PyString_FromStringAndSize((const char*)id, GIT_OID_RAWSZ) static PyObject * git_oid_to_py_str(const git_oid *oid) @@ -485,7 +485,7 @@ Repository_write(Repository *self, PyObject *args) if (err < 0) return Error_set_str(err, "failed to write data"); - return git_oid_to_python(oid); + return git_oid_to_python(oid.id); } static PyObject * @@ -697,7 +697,7 @@ Repository_create_commit(Repository *self, PyObject *args) if (err < 0) return Error_set(err); - return git_oid_to_python(oid); + return git_oid_to_python(oid.id); } static PyObject * @@ -738,7 +738,7 @@ Repository_create_tag(Repository *self, PyObject *args) if (err < 0) return NULL; - return git_oid_to_python(oid); + return git_oid_to_python(oid.id); } static PyObject * @@ -1010,7 +1010,7 @@ Object_get_oid(Object *self) oid = git_object_id(self->obj); assert(oid); - return PyString_FromStringAndSize(oid->id, GIT_OID_RAWSZ); + return git_oid_to_python(oid->id); } static PyObject * @@ -1309,7 +1309,7 @@ TreeEntry_get_oid(TreeEntry *self) const git_oid *oid; oid = git_tree_entry_id(self->entry); - return PyString_FromStringAndSize(oid->id, GIT_OID_RAWSZ); + return git_oid_to_python(oid->id); } static PyObject * @@ -1998,7 +1998,7 @@ Index_create_tree(Index *self) if (err < 0) return Error_set(err); - return git_oid_to_python(oid); + return git_oid_to_python(oid.id); } static PyMethodDef Index_methods[] = { @@ -2151,7 +2151,7 @@ IndexEntry_get_path(IndexEntry *self) static PyObject * IndexEntry_get_oid(IndexEntry *self) { - return git_oid_to_python(self->entry->oid); + return git_oid_to_python(self->entry->oid.id); } static PyObject * @@ -2477,7 +2477,7 @@ Reference_get_oid(Reference *self) } /* 2- Convert and return it */ - return PyString_FromStringAndSize(oid->id, GIT_OID_RAWSZ); + return git_oid_to_python(oid->id); } static int From 5e72746a7aa91d6d896565747c374fc3dba4ff29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 14 Oct 2011 17:53:06 +0200 Subject: [PATCH 0085/2237] Fix warning on Windows, don't use strnlen strnlen is a GNU extension --- pygit2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pygit2.c b/pygit2.c index 3a613e03c..4f7d50759 100644 --- a/pygit2.c +++ b/pygit2.c @@ -45,6 +45,7 @@ #define PyInt_FromLong PyLong_FromLong #define PyString_AS_STRING PyBytes_AS_STRING #define PyString_AsString PyBytes_AsString + #define PyString_AsStringAndSize PyBytes_AsStringAndSize #define PyString_Check PyBytes_Check #define PyString_FromString PyBytes_FromString #define PyString_FromStringAndSize PyBytes_FromStringAndSize @@ -275,9 +276,9 @@ static size_t py_str_to_git_oid(PyObject *py_str, git_oid *oid) { PyObject *py_hex; - const char *hex_or_bin; + char *hex_or_bin; int err; - size_t len; + Py_ssize_t len; /* Case 1: raw sha */ if (PyString_Check(py_str)) { @@ -293,11 +294,10 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) py_hex = PyUnicode_AsASCIIString(py_str); if (py_hex == NULL) return 0; - hex_or_bin = PyString_AsString(py_hex); + err = PyString_AsStringAndSize(py_hex, &hex_or_bin, &len); Py_DECREF(py_hex); - if (hex_or_bin == NULL) + if (err) return 0; - len = strnlen(hex_or_bin, GIT_OID_HEXSZ); err = git_oid_fromstrn(oid, hex_or_bin, len); if (err < 0) { Error_set_py_obj(err, py_str); From a91bc9fa195c52628733246f52e074623bee018c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 14 Oct 2011 18:43:19 +0200 Subject: [PATCH 0086/2237] Fix compilation on Windows --- pygit2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygit2.c b/pygit2.c index 4f7d50759..483b01278 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1579,7 +1579,7 @@ TreeIter_iternext(TreeIter *self) } static PyTypeObject TreeIterType = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) + PyVarObject_HEAD_INIT(NULL, 0) "pygit2.TreeIter", /* tp_name */ sizeof(TreeIter), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -2100,7 +2100,7 @@ IndexIter_iternext(IndexIter *self) } static PyTypeObject IndexIterType = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) + PyVarObject_HEAD_INIT(NULL, 0) "pygit2.IndexIter", /* tp_name */ sizeof(IndexIter), /* tp_basicsize */ 0, /* tp_itemsize */ From af983b989a7d4d5e695b7457b741802c4384b685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 17 Oct 2011 20:44:54 +0200 Subject: [PATCH 0087/2237] tests: fixing issue #20 There were two problems: - Windows refuses to remove a file that is in use. Solution, close the repo before cleaning the temporary directory. - Windows refuses to remove a read-only file. Solution, change mode to writable. --- test/test_repository.py | 2 +- test/utils.py | 62 ++++++++++++++++++++++++++++------------- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/test/test_repository.py b/test/test_repository.py index 303ea5aca..89edcbeb9 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -116,7 +116,7 @@ def test_get_workdir(self): class NewRepositoryTest(utils.NoRepoTestCase): def test_new_repo(self): - repo = init_repository(self.temp_dir, False) + repo = init_repository(self._temp_dir, False) oid = repo.write(GIT_OBJ_BLOB, "Test") self.assertEqual(type(oid), bytes) diff --git a/test/utils.py b/test/utils.py index 55035cbc9..b9c75493b 100644 --- a/test/utils.py +++ b/test/utils.py @@ -29,6 +29,7 @@ import os import shutil +import stat import tarfile import tempfile import unittest @@ -39,10 +40,31 @@ __author__ = 'dborowitz@google.com (Dave Borowitz)' -class BaseTestCase(unittest.TestCase): +def rmtree(path): + """In Windows a read-only file cannot be removed, and shutil.rmtree fails. + So we implement our own version of rmtree to address this issue. + """ + for root, dirs, files in os.walk(path, topdown=False): + for name in files: + filename = os.path.join(root, name) + try: + os.remove(filename) + except OSError: + # Try again + os.chmod(filename, stat.S_IWUSR) + os.remove(filename) + os.rmdir(root) + + +class NoRepoTestCase(unittest.TestCase): + + def setUp(self): + self._temp_dir = tempfile.mkdtemp() + self.repo = None def tearDown(self): - shutil.rmtree(self._temp_dir) + del self.repo + rmtree(self._temp_dir) def assertRaisesWithArg(self, exc_class, arg, func, *args, **kwargs): try: @@ -53,39 +75,39 @@ def assertRaisesWithArg(self, exc_class, arg, func, *args, **kwargs): self.fail('%s(%r) not raised' % (exc_class.__name__, arg)) -def open_repo(repo_dir): - repo_path = os.path.join(os.path.dirname(__file__), 'data', repo_dir) - temp_dir = tempfile.mkdtemp() - temp_repo_path = os.path.join(temp_dir, repo_dir) - shutil.copytree(repo_path, temp_repo_path) - return temp_dir, pygit2.Repository(temp_repo_path) +class BareRepoTestCase(NoRepoTestCase): - -class BareRepoTestCase(BaseTestCase): + repo_dir = 'testrepo.git' def setUp(self): - self._temp_dir, self.repo = open_repo('testrepo.git') + super(BareRepoTestCase, self).setUp() + repo_dir = self.repo_dir + repo_path = os.path.join(os.path.dirname(__file__), 'data', repo_dir) + temp_repo_path = os.path.join(self._temp_dir, repo_dir) -class RepoTestCase(BaseTestCase): + shutil.copytree(repo_path, temp_repo_path) + + self.repo = pygit2.Repository(temp_repo_path) + + +class RepoTestCase(NoRepoTestCase): repo_dir = 'testrepo' def setUp(self): + super(RepoTestCase, self).setUp() + repo_dir = self.repo_dir repo_path = os.path.join(os.path.dirname(__file__), 'data', repo_dir) - temp_dir = tempfile.mkdtemp() + temp_repo_path = os.path.join(self._temp_dir, repo_dir, '.git') + tar = tarfile.open(repo_path + '.tar') - tar.extractall(temp_dir) + tar.extractall(self._temp_dir) tar.close() - self._temp_dir = temp_dir - temp_repo_path = os.path.join(temp_dir, repo_dir, '.git') + self.repo = pygit2.Repository(temp_repo_path) -class NoRepoTestCase(BaseTestCase): - def setUp(self): - self.temp_dir = tempfile.mkdtemp() - self._temp_dir = self.temp_dir class DirtyRepoTestCase(RepoTestCase): From 73af642b8f9b0819f67ad12a07bcbe0b373c8aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 18 Oct 2011 11:27:32 +0200 Subject: [PATCH 0088/2237] tags: properly dealloc tags There were two memory leaks: we were not closing the git_tag object, and we were not decreasing the refcount on the repo. This finishes fixing issue #20 --- pygit2.c | 2 ++ test/test_tag.py | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index 483b01278..6a65f3a08 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1658,7 +1658,9 @@ static PyTypeObject BlobType = { static void Tag_dealloc(Tag *self) { + git_tag_close(self->tag); Py_XDECREF(self->target); + Py_XDECREF(self->repo); Py_TYPE(self)->tp_free((PyObject*)self); } diff --git a/test/test_tag.py b/test/test_tag.py index 0c8989df8..ad786f0ec 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -54,7 +54,6 @@ def test_read_tag(self): self.assertEqual('Tagged root commit.\n', tag.message) commit = tag.target - del tag self.assertEqual('Initial test data commit.\n', commit.message) def test_new_tag(self): From 160cf64abc273f023a5a0a8683f634c2e9e653fd Mon Sep 17 00:00:00 2001 From: Hugh Cole-Baker Date: Mon, 10 Oct 2011 23:48:35 +0100 Subject: [PATCH 0089/2237] Add support for using short hex string prefixes Allows Repository_getitem, Repository_read(_raw), Repository_create_commit, Repository_create_tag and Object_read_raw to use short hex strings to lookup objects. Also test getting objects from Repository using prefixes, looking up commit trees and parents by hex prefix, and looking up tag targets by prefix. Also stop raising TypeError if passing a too-short hex prefix to the lookup functions, instead use ValueError. --- pygit2.c | 45 ++++++++++++++++++++++++----------------- test/test_commit.py | 13 ++++++++---- test/test_repository.py | 22 ++++++++++++++++++++ test/test_tag.py | 9 +++++++-- 4 files changed, 64 insertions(+), 25 deletions(-) diff --git a/pygit2.c b/pygit2.c index 6a65f3a08..c1bf4df9c 100644 --- a/pygit2.c +++ b/pygit2.c @@ -190,9 +190,10 @@ Error_set_py_obj(int err, PyObject *py_obj) assert(err < 0); - if (err == GIT_ENOTOID && !PyString_Check(py_obj)) { + if (err == GIT_ENOTOID && !PyString_Check(py_obj) + && !PyUnicode_Check(py_obj)) { PyErr_Format(PyExc_TypeError, - "Git object id must be byte string, not: %.200s", + "Git object id must be byte or a text string, not: %.200s", Py_TYPE(py_obj)->tp_name); return NULL; } @@ -209,17 +210,19 @@ Error_set_py_obj(int err, PyObject *py_obj) } static PyObject * -lookup_object(Repository *repo, const git_oid *oid, git_otype type) +lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, + git_otype type) { int err; char hex[GIT_OID_HEXSZ + 1]; git_object *obj; Object *py_obj = NULL; - err = git_object_lookup(&obj, repo->repo, oid, type); + err = git_object_lookup_prefix(&obj, repo->repo, oid, + (unsigned int)len, type); if (err < 0) { git_oid_fmt(hex, oid); - hex[GIT_OID_HEXSZ] = '\0'; + hex[len] = '\0'; return Error_set_str(err, hex); } @@ -248,6 +251,12 @@ lookup_object(Repository *repo, const git_oid *oid, git_otype type) return (PyObject*)py_obj; } +static PyObject * +lookup_object(Repository *repo, const git_oid *oid, git_otype type) +{ + return lookup_object_prefix(repo, oid, GIT_OID_HEXSZ, type); +} + static git_otype int_to_loose_object_type(int type_id) { @@ -415,18 +424,18 @@ Repository_getitem(Repository *self, PyObject *value) size_t len; len = py_str_to_git_oid(value, &oid); - TODO_SUPPORT_SHORT_HEXS(len) if (len == 0) return NULL; - return lookup_object(self, &oid, GIT_OBJ_ANY); + return lookup_object_prefix(self, &oid, len, GIT_OBJ_ANY); } static int Repository_read_raw(git_odb_object **obj, git_repository *repo, - const git_oid *oid) + const git_oid *oid, size_t len) { - return git_odb_read(obj, git_repository_database(repo), oid); + return git_odb_read_prefix(obj, git_repository_database(repo), + oid, (unsigned int)len); } static PyObject * @@ -438,11 +447,10 @@ Repository_read(Repository *self, PyObject *py_hex) size_t len; len = py_str_to_git_oid(py_hex, &oid); - TODO_SUPPORT_SHORT_HEXS(len) if (len == 0) return NULL; - err = Repository_read_raw(&obj, self->repo, &oid); + err = Repository_read_raw(&obj, self->repo, &oid, len); if (err < 0) return Error_set_py_obj(err, py_hex); @@ -659,13 +667,12 @@ Repository_create_commit(Repository *self, PyObject *args) return NULL; len = py_str_to_git_oid(py_oid, &oid); - TODO_SUPPORT_SHORT_HEXS(len) if (len == 0) return NULL; message = py_str_to_c_str(py_message); - err = git_tree_lookup(&tree, self->repo, &oid); + err = git_tree_lookup_prefix(&tree, self->repo, &oid, (unsigned int)len); if (err < 0) return Error_set(err); @@ -679,12 +686,12 @@ Repository_create_commit(Repository *self, PyObject *args) for (i = 0; i < parent_count; i++) { py_parent = PyList_GET_ITEM(py_parents, i); len = py_str_to_git_oid(py_parent, &oid); - TODO_SUPPORT_SHORT_HEXS(len) if (len == 0) { git_tree_close(tree); return free_parents(parents, i); } - if (git_commit_lookup(&parents[i], self->repo, &oid)) { + if (git_commit_lookup_prefix(&parents[i], self->repo, &oid, + (unsigned int)len)) { git_tree_close(tree); return free_parents(parents, i); } @@ -721,14 +728,14 @@ Repository_create_tag(Repository *self, PyObject *args) return NULL; len = py_str_to_git_oid(py_oid, &oid); - TODO_SUPPORT_SHORT_HEXS(len) if (len == 0) return NULL; - err = git_object_lookup(&target, self->repo, &oid, target_type); + err = git_object_lookup_prefix(&target, self->repo, &oid, + (unsigned int)len, target_type); if (err < 0) { git_oid_fmt(hex, &oid); - hex[GIT_OID_HEXSZ] = '\0'; + hex[len] = '\0'; return Error_set_str(err, hex); } @@ -1041,7 +1048,7 @@ Object_read_raw(Object *self) oid = git_object_id(self->obj); assert(oid); - err = Repository_read_raw(&obj, self->repo->repo, oid); + err = Repository_read_raw(&obj, self->repo->repo, oid, GIT_OID_HEXSZ); if (err < 0) { aux = git_oid_to_py_str(oid); Error_set_py_obj(err, aux); diff --git a/test/test_commit.py b/test/test_commit.py index f67fd7483..663595926 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -71,10 +71,15 @@ def test_new_commit(self): committer = ('John Doe', 'jdoe@example.com', 12346, 0) author = ('J. David Ibáñez', 'jdavid@example.com', 12345, 0) tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' - - parents = [COMMIT_SHA] - sha = repo.create_commit(None, author, committer, message, tree, - parents) + tree_prefix = tree[:5] + too_short_prefix = tree[:3] + + parents = [COMMIT_SHA[:5]] + self.assertRaises(ValueError, repo.create_commit, None, author, + committer, message, too_short_prefix, parents) + + sha = repo.create_commit(None, author, committer, message, + tree_prefix, parents) commit = repo[sha] self.assertEqual(GIT_OBJ_COMMIT, commit.type) diff --git a/test/test_repository.py b/test/test_repository.py index 89edcbeb9..c62537f66 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -58,6 +58,10 @@ def test_read(self): a2 = self.repo.read('7f129fd57e31e935c6d60a0c794efe4e6927664b') self.assertEqual((GIT_OBJ_BLOB, 'a contents 2\n'), a2) + + a_hex_prefix = A_HEX_SHA[:4] + a3 = self.repo.read(a_hex_prefix) + self.assertEqual((GIT_OBJ_BLOB, 'a contents\n'), a3) def test_write(self): data = b"hello world" @@ -82,6 +86,12 @@ def test_lookup_blob(self): self.assertEqual(A_HEX_SHA, a.hex) self.assertEqual(GIT_OBJ_BLOB, a.type) + def test_lookup_blob_prefix(self): + a = self.repo[A_HEX_SHA[:5]] + self.assertEqual(b'a contents\n', a.read_raw()) + self.assertEqual(A_HEX_SHA, a.hex) + self.assertEqual(GIT_OBJ_BLOB, a.type) + def test_lookup_commit(self): commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10' commit = self.repo[commit_sha] @@ -91,6 +101,18 @@ def test_lookup_commit(self): 'This commit has some additional text.\n'), commit.message) + def test_lookup_commit_prefix(self): + commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10' + commit_sha_prefix = commit_sha[:7] + too_short_prefix = commit_sha[:3] + commit = self.repo[commit_sha_prefix] + self.assertEqual(commit_sha, commit.hex) + self.assertEqual(GIT_OBJ_COMMIT, commit.type) + self.assertEqual(('Second test data commit.\n\n' + 'This commit has some additional text.\n'), + commit.message) + self.assertRaises(ValueError, self.repo.__getitem__, too_short_prefix) + def test_get_path(self): directory = realpath(self.repo.path) expected = realpath(join(self._temp_dir, 'testrepo.git')) diff --git a/test/test_tag.py b/test/test_tag.py index ad786f0ec..a138882cf 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -62,8 +62,13 @@ def test_new_tag(self): message = 'Tag a blob.\n' tagger = ('John Doe', 'jdoe@example.com', 12347, 0) - sha = self.repo.create_tag(name, target, pygit2.GIT_OBJ_BLOB, tagger, - message) + target_prefix = target[:5] + too_short_prefix = target[:3] + self.assertRaises(ValueError, self.repo.create_tag, name, + too_short_prefix, pygit2.GIT_OBJ_BLOB, tagger, + message) + sha = self.repo.create_tag(name, target_prefix, pygit2.GIT_OBJ_BLOB, + tagger, message) tag = self.repo[sha] self.assertEqual('3ee44658fd11660e828dfc96b9b5c5f38d5b49bb', tag.hex) From 81c0ed328141cedc1a796f9178a6df80d671ac76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 23 Oct 2011 15:09:55 +0200 Subject: [PATCH 0090/2237] Python 3: fix 'Error_set_py_obj' --- pygit2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index c1bf4df9c..b3731930d 100644 --- a/pygit2.c +++ b/pygit2.c @@ -202,7 +202,7 @@ Error_set_py_obj(int err, PyObject *py_obj) PyErr_SetObject(PyExc_KeyError, py_obj); return NULL; } - py_str = PyObject_Str(py_obj); + py_str = PyObject_Bytes(py_obj); str = py_str ? PyString_AS_STRING(py_str) : ""; PyErr_Format(Error_type(err), "%s: %s", str, git_lasterror()); Py_XDECREF(py_str); From 9fadd57445ba040e6473527b14bc5c028676aaa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 12 Nov 2011 00:13:31 +0100 Subject: [PATCH 0091/2237] Fix typo in error string --- pygit2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygit2.c b/pygit2.c index b3731930d..2de2e19cf 100644 --- a/pygit2.c +++ b/pygit2.c @@ -378,7 +378,7 @@ Repository_init(Repository *self, PyObject *args, PyObject *kwds) if (kwds) { PyErr_SetString(PyExc_TypeError, - "Repository takes no keyword arugments"); + "Repository takes no keyword arguments"); return -1; } @@ -1776,7 +1776,7 @@ Index_init(Index *self, PyObject *args, PyObject *kwds) if (kwds) { PyErr_SetString(PyExc_TypeError, - "Index takes no keyword arugments"); + "Index takes no keyword arguments"); return -1; } From 4f5298d6b77f4fbe2ac96fe91bde6d93f64f3168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 12 Nov 2011 20:13:28 +0100 Subject: [PATCH 0092/2237] Repository.create_commit supports message encoding Now the 'Repository.create_commit' method expects one more argument, the message encoding. If 'None', it defaults to UTF-8. --- pygit2.c | 71 ++++++++++++++++++++++++++++----------------- test/test_commit.py | 31 ++++++++++++++++---- 2 files changed, 70 insertions(+), 32 deletions(-) diff --git a/pygit2.c b/pygit2.c index 2de2e19cf..010ad700b 100644 --- a/pygit2.c +++ b/pygit2.c @@ -343,7 +343,7 @@ git_oid_to_py_str(const git_oid *oid) } char * -py_str_to_c_str(PyObject *value) +py_str_to_c_str(PyObject *value, const char *encoding) { char *c_str; @@ -353,7 +353,10 @@ py_str_to_c_str(PyObject *value) /* Case 2: text string */ if (PyUnicode_Check(value)) { - value = PyUnicode_AsUTF8String(value); + if (encoding == NULL) + value = PyUnicode_AsUTF8String(value); + else + value = PyUnicode_AsEncodedString(value, encoding, "strict"); if (value == NULL) return NULL; c_str = PyString_AsString(value); @@ -610,27 +613,28 @@ build_person(const git_signature *signature, const char *encoding) signature->when.time, signature->when.offset); } -static int -signature_converter(PyObject *value, git_signature **signature) +static git_signature * +py_signature_to_git_signature(PyObject *value, const char* encoding) { PyObject *py_name; char *name, *email; long long time; int offset; int err; + git_signature *signature; if (!PyArg_ParseTuple(value, "OsLi", &py_name, &email, &time, &offset)) - return 0; + return NULL; - name = py_str_to_c_str(py_name); + name = py_str_to_c_str(py_name, encoding); - err = git_signature_new(signature, name, email, time, offset); + err = git_signature_new(&signature, name, email, time, offset); if (err < 0) { Error_set(err); - return 0; + return NULL; } - return 1; + return signature; } static PyObject * @@ -647,30 +651,39 @@ free_parents(git_commit **parents, int n) static PyObject * Repository_create_commit(Repository *self, PyObject *args) { + PyObject *py_author, *py_committer; + PyObject *py_oid, *py_message, *py_parents, *py_parent; git_signature *author, *committer; - char *message, *update_ref; + char *message, *update_ref, *encoding; git_oid oid; git_tree *tree; - PyObject *py_oid, *py_message, *py_parents, *py_parent; int parent_count; git_commit **parents; int err, i; size_t len; - if (!PyArg_ParseTuple(args, "zO&O&OOO!", + if (!PyArg_ParseTuple(args, "zO!O!zOOO!", &update_ref, - signature_converter, &author, - signature_converter, &committer, + &PyTuple_Type, &py_author, + &PyTuple_Type, &py_committer, + &encoding, &py_message, &py_oid, &PyList_Type, &py_parents)) return NULL; + author = py_signature_to_git_signature(py_author, encoding); + if (author == NULL) + return NULL; + committer = py_signature_to_git_signature(py_committer, encoding); + if (committer == NULL) + return NULL; + len = py_str_to_git_oid(py_oid, &oid); if (len == 0) return NULL; - message = py_str_to_c_str(py_message); + message = py_str_to_c_str(py_message, encoding); err = git_tree_lookup_prefix(&tree, self->repo, &oid, (unsigned int)len); if (err < 0) @@ -698,7 +711,7 @@ Repository_create_commit(Repository *self, PyObject *args) } err = git_commit_create(&oid, self->repo, update_ref, author, committer, - NULL, message, tree, parent_count, (const git_commit**)parents); + encoding, message, tree, parent_count, (const git_commit**)parents); git_tree_close(tree); free_parents(parents, parent_count); if (err < 0) @@ -710,7 +723,7 @@ Repository_create_commit(Repository *self, PyObject *args) static PyObject * Repository_create_tag(Repository *self, PyObject *args) { - PyObject *py_oid; + PyObject *py_oid, *py_tagger; char *tag_name, *message; git_signature *tagger; git_oid oid; @@ -719,14 +732,18 @@ Repository_create_tag(Repository *self, PyObject *args) char hex[GIT_OID_HEXSZ + 1]; size_t len; - if (!PyArg_ParseTuple(args, "sOiO&s", + if (!PyArg_ParseTuple(args, "sOiO!s", &tag_name, &py_oid, &target_type, - signature_converter, &tagger, + &PyTuple_Type, &py_tagger, &message)) return NULL; + tagger = py_signature_to_git_signature(py_tagger, NULL); + if (tagger == NULL) + return NULL; + len = py_str_to_git_oid(py_oid, &oid); if (len == 0) return NULL; @@ -799,7 +816,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) int err; /* 1- Get the C name */ - c_name = py_str_to_c_str(py_name); + c_name = py_str_to_c_str(py_name, NULL); if (c_name == NULL) return NULL; @@ -1127,7 +1144,7 @@ Commit_get_message_encoding(Commit *commit) if (encoding == NULL) Py_RETURN_NONE; - return PyString_FromString(encoding); + return PyUnicode_DecodeASCII(encoding, strlen(encoding), "strict"); } static PyObject * @@ -1401,7 +1418,7 @@ Tree_contains(Tree *self, PyObject *py_name) { char *name; - name = py_str_to_c_str(py_name); + name = py_str_to_c_str(py_name, NULL); if (name == NULL) return -1; @@ -1496,7 +1513,7 @@ Tree_getitem(Tree *self, PyObject *value) return Tree_getitem_by_index(self, value); /* Case 2: byte or text string */ - name = py_str_to_c_str(value); + name = py_str_to_c_str(value, NULL); if (name == NULL) return NULL; entry = git_tree_entry_byname(self->tree, name); @@ -1887,7 +1904,7 @@ Index_get_position(Index *self, PyObject *value) } /* Case 2: byte or text string */ - path = py_str_to_c_str(value); + path = py_str_to_c_str(value, NULL); if (!path) return -1; idx = git_index_find(self->index, path); @@ -1904,7 +1921,7 @@ Index_contains(Index *self, PyObject *value) char *path; int idx; - path = py_str_to_c_str(value); + path = py_str_to_c_str(value, NULL); if (!path) return -1; idx = git_index_find(self->index, path); @@ -2395,7 +2412,7 @@ Reference_rename(Reference *self, PyObject *py_name) int err; /* 1- Get the C name */ - c_name = py_str_to_c_str(py_name); + c_name = py_str_to_c_str(py_name, NULL); if (c_name == NULL) return NULL; @@ -2446,7 +2463,7 @@ Reference_set_target(Reference *self, PyObject *py_name) int err; /* 1- Get the C name */ - c_name = py_str_to_c_str(py_name); + c_name = py_str_to_c_str(py_name, NULL); if (c_name == NULL) return -1; diff --git a/test/test_commit.py b/test/test_commit.py index 663595926..322ac01f1 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -50,7 +50,6 @@ def test_read_commit(self): self.assertEqual('c2792cfa289ae6321ecf2cd5806c2194b0fd070c', parents[0].hex) self.assertEqual(None, commit.message_encoding) - #self.assertEqual('Second test data commit.', commit.message_short) self.assertEqual(('Second test data commit.\n\n' 'This commit has some additional text.\n'), commit.message) @@ -76,9 +75,9 @@ def test_new_commit(self): parents = [COMMIT_SHA[:5]] self.assertRaises(ValueError, repo.create_commit, None, author, - committer, message, too_short_prefix, parents) - - sha = repo.create_commit(None, author, committer, message, + committer, None, message, too_short_prefix, parents) + + sha = repo.create_commit(None, author, committer, None, message, tree_prefix, parents) commit = repo[sha] @@ -87,7 +86,29 @@ def test_new_commit(self): commit.hex) self.assertEqual(None, commit.message_encoding) self.assertEqual(message, commit.message) - #self.assertEqual('New commit.', commit.message_short) + self.assertEqual(12346, commit.commit_time) + self.assertEqual(committer, commit.committer) + self.assertEqual(author, commit.author) + self.assertEqual(tree, commit.tree.hex) + self.assertEqual(1, len(commit.parents)) + self.assertEqual(COMMIT_SHA, commit.parents[0].hex) + + def test_new_commit_encoding(self): + repo = self.repo + message = 'New commit.\n\nMessage with non-ascii chars: ééé.\n' + committer = ('John Doe', 'jdoe@example.com', 12346, 0) + author = ('J. David Ibáñez', 'jdavid@example.com', 12345, 0) + tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' + tree_prefix = tree[:5] + + parents = [COMMIT_SHA[:5]] + sha = repo.create_commit(None, author, committer, 'iso-8859-1', + message, tree_prefix, parents) + commit = repo[sha] + + self.assertEqual(GIT_OBJ_COMMIT, commit.type) + self.assertEqual('iso-8859-1', commit.message_encoding) + self.assertEqual(message, commit.message) self.assertEqual(12346, commit.commit_time) self.assertEqual(committer, commit.committer) self.assertEqual(author, commit.author) From 8d4c8415da71e14abeffde6896b6f75adee05a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 13 Nov 2011 12:47:08 +0100 Subject: [PATCH 0093/2237] Use file system encoding to decode/encode paths Instead of UTF-8. This is done for better interoperability with Git. --- pygit2.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/pygit2.c b/pygit2.c index 010ad700b..f27001309 100644 --- a/pygit2.c +++ b/pygit2.c @@ -373,6 +373,14 @@ py_str_to_c_str(PyObject *value, const char *encoding) #define c_str_to_py_str(c_str) \ PyUnicode_DecodeUTF8(c_str, strlen(c_str), "strict") +#define py_path_to_c_str(py_path) \ + py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding) + +#define c_str_to_py_path(c_str) \ + PyUnicode_Decode(c_str, strlen(c_str), \ + Py_FileSystemDefaultEncoding, "strict") + + static int Repository_init(Repository *self, PyObject *args, PyObject *kwds) { @@ -539,7 +547,7 @@ Repository_get_path(Repository *self, void *closure) const char *c_path; c_path = git_repository_path(self->repo, GIT_REPO_PATH); - return c_str_to_py_str(c_path); + return c_str_to_py_path(c_path); } static PyObject * @@ -551,7 +559,7 @@ Repository_get_workdir(Repository *self, void *closure) if (c_path == NULL) Py_RETURN_NONE; - return c_str_to_py_str(c_path); + return c_str_to_py_path(c_path); } static PyObject * @@ -792,7 +800,7 @@ Repository_listall_references(Repository *self, PyObject *args) /* 4- Fill it */ for (index=0; index < c_result.count; index++) { - py_string = c_str_to_py_str((c_result.strings)[index]); + py_string = c_str_to_py_path((c_result.strings)[index]); if (py_string == NULL) { Py_XDECREF(py_result); git_strarray_free(&c_result); @@ -816,7 +824,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) int err; /* 1- Get the C name */ - c_name = py_str_to_c_str(py_name, NULL); + c_name = py_path_to_c_str(py_name); if (c_name == NULL) return NULL; @@ -1324,7 +1332,7 @@ TreeEntry_get_attributes(TreeEntry *self) static PyObject * TreeEntry_get_name(TreeEntry *self) { - return c_str_to_py_str(git_tree_entry_name(self->entry)); + return c_str_to_py_path(git_tree_entry_name(self->entry)); } static PyObject * @@ -1418,7 +1426,7 @@ Tree_contains(Tree *self, PyObject *py_name) { char *name; - name = py_str_to_c_str(py_name, NULL); + name = py_path_to_c_str(py_name); if (name == NULL) return -1; @@ -1513,7 +1521,7 @@ Tree_getitem(Tree *self, PyObject *value) return Tree_getitem_by_index(self, value); /* Case 2: byte or text string */ - name = py_str_to_c_str(value, NULL); + name = py_path_to_c_str(value); if (name == NULL) return NULL; entry = git_tree_entry_byname(self->tree, name); @@ -1904,7 +1912,7 @@ Index_get_position(Index *self, PyObject *value) } /* Case 2: byte or text string */ - path = py_str_to_c_str(value, NULL); + path = py_path_to_c_str(value); if (!path) return -1; idx = git_index_find(self->index, path); @@ -1921,7 +1929,7 @@ Index_contains(Index *self, PyObject *value) char *path; int idx; - path = py_str_to_c_str(value, NULL); + path = py_path_to_c_str(value); if (!path) return -1; idx = git_index_find(self->index, path); @@ -2171,7 +2179,7 @@ IndexEntry_get_mode(IndexEntry *self) static PyObject * IndexEntry_get_path(IndexEntry *self) { - return c_str_to_py_str(self->entry->path); + return c_str_to_py_path(self->entry->path); } static PyObject * @@ -2412,7 +2420,7 @@ Reference_rename(Reference *self, PyObject *py_name) int err; /* 1- Get the C name */ - c_name = py_str_to_c_str(py_name, NULL); + c_name = py_path_to_c_str(py_name); if (c_name == NULL) return NULL; @@ -2453,7 +2461,7 @@ Reference_get_target(Reference *self) } /* 2- Make a PyString and return it */ - return c_str_to_py_str(c_name); + return c_str_to_py_path(c_name); } static int @@ -2463,7 +2471,7 @@ Reference_set_target(Reference *self, PyObject *py_name) int err; /* 1- Get the C name */ - c_name = py_str_to_c_str(py_name, NULL); + c_name = py_path_to_c_str(py_name); if (c_name == NULL) return -1; @@ -2484,7 +2492,7 @@ Reference_get_name(Reference *self) const char *c_name; c_name = git_reference_name(self->reference); - return c_str_to_py_str(c_name); + return c_str_to_py_path(c_name); } static PyObject * From 3851f725f90adf1f1262f8d87fb0e97beee7fdcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 15 Nov 2011 00:45:20 +0100 Subject: [PATCH 0094/2237] Fix memory leak in 'Repository.create_commit' Free the signatures. --- pygit2.c | 71 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/pygit2.c b/pygit2.c index f27001309..334c293c0 100644 --- a/pygit2.c +++ b/pygit2.c @@ -645,29 +645,19 @@ py_signature_to_git_signature(PyObject *value, const char* encoding) return signature; } -static PyObject * -free_parents(git_commit **parents, int n) -{ - int i; - - for (i = 0; i < n; i++) - git_commit_close(parents[i]); - free(parents); - return NULL; -} - static PyObject * Repository_create_commit(Repository *self, PyObject *args) { PyObject *py_author, *py_committer; PyObject *py_oid, *py_message, *py_parents, *py_parent; - git_signature *author, *committer; + PyObject *py_result = NULL; + git_signature *author = NULL, *committer = NULL; char *message, *update_ref, *encoding; git_oid oid; - git_tree *tree; + git_tree *tree = NULL; int parent_count; - git_commit **parents; - int err, i; + git_commit **parents = NULL; + int err = 0, i = 0; size_t len; if (!PyArg_ParseTuple(args, "zO!O!zOOO!", @@ -685,47 +675,56 @@ Repository_create_commit(Repository *self, PyObject *args) return NULL; committer = py_signature_to_git_signature(py_committer, encoding); if (committer == NULL) - return NULL; + goto out; len = py_str_to_git_oid(py_oid, &oid); if (len == 0) - return NULL; + goto out; message = py_str_to_c_str(py_message, encoding); err = git_tree_lookup_prefix(&tree, self->repo, &oid, (unsigned int)len); - if (err < 0) - return Error_set(err); + if (err < 0) { + Error_set(err); + goto out; + } parent_count = (int)PyList_Size(py_parents); parents = malloc(parent_count * sizeof(git_commit*)); if (parents == NULL) { - git_tree_close(tree); PyErr_SetNone(PyExc_MemoryError); - return NULL; + goto out; } - for (i = 0; i < parent_count; i++) { + for (; i < parent_count; i++) { py_parent = PyList_GET_ITEM(py_parents, i); len = py_str_to_git_oid(py_parent, &oid); - if (len == 0) { - git_tree_close(tree); - return free_parents(parents, i); - } + if (len == 0) + goto out; if (git_commit_lookup_prefix(&parents[i], self->repo, &oid, - (unsigned int)len)) { - git_tree_close(tree); - return free_parents(parents, i); - } + (unsigned int)len)) + goto out; } err = git_commit_create(&oid, self->repo, update_ref, author, committer, - encoding, message, tree, parent_count, (const git_commit**)parents); - git_tree_close(tree); - free_parents(parents, parent_count); - if (err < 0) - return Error_set(err); + encoding, message, tree, parent_count, + (const git_commit**)parents); + if (err < 0) { + Error_set(err); + goto out; + } - return git_oid_to_python(oid.id); + py_result = git_oid_to_python(oid.id); + +out: + git_signature_free(author); + git_signature_free(committer); + git_tree_close(tree); + while (i > 0) { + i--; + git_commit_close(parents[i]); + } + free(parents); + return py_result; } static PyObject * From 1eb0c6a9f02c06a2d33c597ccc17529f67d8c54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 15 Nov 2011 23:18:04 +0100 Subject: [PATCH 0095/2237] Fix memory leak in 'Repository.create_tag' Free the signature. --- pygit2.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/pygit2.c b/pygit2.c index 334c293c0..54862a312 100644 --- a/pygit2.c +++ b/pygit2.c @@ -730,11 +730,11 @@ Repository_create_commit(Repository *self, PyObject *args) static PyObject * Repository_create_tag(Repository *self, PyObject *args) { - PyObject *py_oid, *py_tagger; + PyObject *py_oid, *py_tagger, *py_result = NULL; char *tag_name, *message; - git_signature *tagger; + git_signature *tagger = NULL; git_oid oid; - git_object *target; + git_object *target = NULL; int err, target_type; char hex[GIT_OID_HEXSZ + 1]; size_t len; @@ -753,23 +753,26 @@ Repository_create_tag(Repository *self, PyObject *args) len = py_str_to_git_oid(py_oid, &oid); if (len == 0) - return NULL; + goto out; err = git_object_lookup_prefix(&target, self->repo, &oid, (unsigned int)len, target_type); if (err < 0) { git_oid_fmt(hex, &oid); hex[len] = '\0'; - return Error_set_str(err, hex); + Error_set_str(err, hex); + goto out; } err = git_tag_create(&oid, self->repo, tag_name, target, tagger, message, 0); - git_object_close(target); - if (err < 0) - return NULL; + if (err == 0) + py_result = git_oid_to_python(oid.id); - return git_oid_to_python(oid.id); +out: + git_signature_free(tagger); + git_object_close(target); + return py_result; } static PyObject * From ce41de9417e68b2a9ae718bec8d622ec77883a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 19 Nov 2011 09:37:45 +0100 Subject: [PATCH 0096/2237] Repository.create_commit, make encoding optional --- pygit2.c | 8 ++++---- test/test_commit.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pygit2.c b/pygit2.c index 54862a312..47e875b50 100644 --- a/pygit2.c +++ b/pygit2.c @@ -652,7 +652,7 @@ Repository_create_commit(Repository *self, PyObject *args) PyObject *py_oid, *py_message, *py_parents, *py_parent; PyObject *py_result = NULL; git_signature *author = NULL, *committer = NULL; - char *message, *update_ref, *encoding; + char *message, *update_ref, *encoding = NULL; git_oid oid; git_tree *tree = NULL; int parent_count; @@ -660,14 +660,14 @@ Repository_create_commit(Repository *self, PyObject *args) int err = 0, i = 0; size_t len; - if (!PyArg_ParseTuple(args, "zO!O!zOOO!", + if (!PyArg_ParseTuple(args, "zO!O!OOO!|s", &update_ref, &PyTuple_Type, &py_author, &PyTuple_Type, &py_committer, - &encoding, &py_message, &py_oid, - &PyList_Type, &py_parents)) + &PyList_Type, &py_parents, + &encoding)) return NULL; author = py_signature_to_git_signature(py_author, encoding); diff --git a/test/test_commit.py b/test/test_commit.py index 322ac01f1..71716a2bd 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -75,9 +75,9 @@ def test_new_commit(self): parents = [COMMIT_SHA[:5]] self.assertRaises(ValueError, repo.create_commit, None, author, - committer, None, message, too_short_prefix, parents) + committer, message, too_short_prefix, parents) - sha = repo.create_commit(None, author, committer, None, message, + sha = repo.create_commit(None, author, committer, message, tree_prefix, parents) commit = repo[sha] @@ -102,8 +102,8 @@ def test_new_commit_encoding(self): tree_prefix = tree[:5] parents = [COMMIT_SHA[:5]] - sha = repo.create_commit(None, author, committer, 'iso-8859-1', - message, tree_prefix, parents) + sha = repo.create_commit(None, author, committer, message, + tree_prefix, parents, 'iso-8859-1') commit = repo[sha] self.assertEqual(GIT_OBJ_COMMIT, commit.type) From fb1ca247f89039148470d704cf1afb8656ede064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 19 Nov 2011 17:43:52 +0100 Subject: [PATCH 0097/2237] Commit.tree, check for no memory error --- pygit2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pygit2.c b/pygit2.c index 47e875b50..dbd300c16 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1222,6 +1222,9 @@ Commit_get_tree(Commit *commit) return Error_set(err); py_tree = PyObject_New(Tree, &TreeType); + if (!py_tree) + return NULL; + Py_INCREF(commit->repo); py_tree->repo = commit->repo; py_tree->tree = (git_tree*)tree; From 38f1884b29d0a5413b5d636c4212025d4096d6cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 19 Nov 2011 19:04:31 +0100 Subject: [PATCH 0098/2237] tp_alloc already sets no memory error --- pygit2.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pygit2.c b/pygit2.c index dbd300c16..61d2afeda 100644 --- a/pygit2.c +++ b/pygit2.c @@ -242,12 +242,13 @@ lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, default: assert(0); } - if (!py_obj) - return PyErr_NoMemory(); - py_obj->obj = obj; - py_obj->repo = repo; - Py_INCREF(repo); + if (py_obj) { + py_obj->obj = obj; + py_obj->repo = repo; + Py_INCREF(repo); + } + return (PyObject*)py_obj; } @@ -275,9 +276,9 @@ wrap_reference(git_reference * c_reference) Reference *py_reference=NULL; py_reference = (Reference *)ReferenceType.tp_alloc(&ReferenceType, 0); - if (py_reference == NULL) - return NULL; - py_reference->reference = c_reference; + if (py_reference) + py_reference->reference = c_reference; + return (PyObject *)py_reference; } @@ -521,7 +522,8 @@ Repository_get_index(Repository *self, void *closure) if (err == GIT_SUCCESS) { py_index = (Index*)IndexType.tp_alloc(&IndexType, 0); if (!py_index) - return PyErr_NoMemory(); + return NULL; + Py_INCREF(self); py_index->repo = self; py_index->index = index; @@ -1443,12 +1445,12 @@ wrap_tree_entry(const git_tree_entry *entry, Tree *tree) { TreeEntry *py_entry = NULL; py_entry = (TreeEntry*)TreeEntryType.tp_alloc(&TreeEntryType, 0); - if (!py_entry) - return NULL; + if (py_entry) { + py_entry->entry = entry; + py_entry->tree = tree; + Py_INCREF(tree); + } - py_entry->entry = entry; - py_entry->tree = tree; - Py_INCREF(tree); return py_entry; } @@ -1975,10 +1977,8 @@ wrap_index_entry(git_index_entry *entry, Index *index) IndexEntry *py_entry; py_entry = (IndexEntry*)IndexEntryType.tp_alloc(&IndexEntryType, 0); - if (!py_entry) - return PyErr_NoMemory(); - - py_entry->entry = entry; + if (py_entry) + py_entry->entry = entry; return (PyObject*)py_entry; } From 46ab8106367e7b3f7b5234029959ce260390a092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 20 Nov 2011 12:43:28 +0100 Subject: [PATCH 0099/2237] Use PyObject_New/PyObject_Del --- pygit2.c | 123 +++++++++++++++++++++---------------------------------- 1 file changed, 47 insertions(+), 76 deletions(-) diff --git a/pygit2.c b/pygit2.c index 61d2afeda..66bdd0c11 100644 --- a/pygit2.c +++ b/pygit2.c @@ -72,15 +72,9 @@ OBJECT_STRUCT(Object, git_object, obj) OBJECT_STRUCT(Commit, git_commit, commit) OBJECT_STRUCT(Tree, git_tree, tree) OBJECT_STRUCT(Blob, git_blob, blob) +OBJECT_STRUCT(Tag, git_tag, tag) OBJECT_STRUCT(Walker, git_revwalk, walk) -typedef struct { - PyObject_HEAD - Repository *repo; - git_tag *tag; - PyObject *target; -} Tag; - typedef struct { PyObject_HEAD const git_tree_entry *entry; @@ -228,16 +222,16 @@ lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, switch (git_object_type(obj)) { case GIT_OBJ_COMMIT: - py_obj = (Object*)CommitType.tp_alloc(&CommitType, 0); + py_obj = PyObject_New(Object, &CommitType); break; case GIT_OBJ_TREE: - py_obj = (Object*)TreeType.tp_alloc(&TreeType, 0); + py_obj = PyObject_New(Object, &TreeType); break; case GIT_OBJ_BLOB: - py_obj = (Object*)BlobType.tp_alloc(&BlobType, 0); + py_obj = PyObject_New(Object, &BlobType); break; case GIT_OBJ_TAG: - py_obj = (Object*)TagType.tp_alloc(&TagType, 0); + py_obj = PyObject_New(Object, &TagType); break; default: assert(0); @@ -248,7 +242,6 @@ lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, py_obj->repo = repo; Py_INCREF(repo); } - return (PyObject*)py_obj; } @@ -275,7 +268,7 @@ wrap_reference(git_reference * c_reference) { Reference *py_reference=NULL; - py_reference = (Reference *)ReferenceType.tp_alloc(&ReferenceType, 0); + py_reference = PyObject_New(Reference, &ReferenceType); if (py_reference) py_reference->reference = c_reference; @@ -412,7 +405,7 @@ Repository_dealloc(Repository *self) if (self->repo) git_repository_free(self->repo); Py_XDECREF(self->index); - Py_TYPE(self)->tp_free((PyObject*)self); + PyObject_Del(self); } static int @@ -520,7 +513,7 @@ Repository_get_index(Repository *self, void *closure) if (self->index == NULL) { err = git_repository_index(&index, self->repo); if (err == GIT_SUCCESS) { - py_index = (Index*)IndexType.tp_alloc(&IndexType, 0); + py_index = PyObject_New(Index, &IndexType); if (!py_index) return NULL; @@ -1035,7 +1028,7 @@ Object_dealloc(Object* self) { git_object_close(self->obj); Py_XDECREF(self->repo); - Py_TYPE(self)->tp_free((PyObject*)self); + PyObject_Del(self); } static PyObject * @@ -1224,13 +1217,11 @@ Commit_get_tree(Commit *commit) return Error_set(err); py_tree = PyObject_New(Tree, &TreeType); - if (!py_tree) - return NULL; - - Py_INCREF(commit->repo); - py_tree->repo = commit->repo; - py_tree->tree = (git_tree*)tree; - + if (py_tree) { + Py_INCREF(commit->repo); + py_tree->repo = commit->repo; + py_tree->tree = (git_tree*)tree; + } return (PyObject*)py_tree; } @@ -1327,7 +1318,7 @@ static void TreeEntry_dealloc(TreeEntry *self) { Py_XDECREF(self->tree); - Py_TYPE(self)->tp_free((PyObject *)self); + PyObject_Del(self); } static PyObject * @@ -1443,14 +1434,14 @@ Tree_contains(Tree *self, PyObject *py_name) static TreeEntry * wrap_tree_entry(const git_tree_entry *entry, Tree *tree) { - TreeEntry *py_entry = NULL; - py_entry = (TreeEntry*)TreeEntryType.tp_alloc(&TreeEntryType, 0); + TreeEntry *py_entry; + + py_entry = PyObject_New(TreeEntry, &TreeEntryType); if (py_entry) { py_entry->entry = entry; py_entry->tree = tree; Py_INCREF(tree); } - return py_entry; } @@ -1486,17 +1477,15 @@ Tree_fix_index(Tree *self, PyObject *py_index) static PyObject * Tree_iter(Tree *self) { - TreeIter *iter; - - iter = PyObject_New(TreeIter, &TreeIterType); - if (!iter) - return NULL; + TreeIter *iter; - Py_INCREF(self); - iter->owner = self; - iter->i = 0; - - return (PyObject*)iter; + iter = PyObject_New(TreeIter, &TreeIterType); + if (iter) { + Py_INCREF(self); + iter->owner = self; + iter->i = 0; + } + return (PyObject*)iter; } static TreeEntry * @@ -1694,31 +1683,15 @@ static PyTypeObject BlobType = { 0, /* tp_new */ }; -static void -Tag_dealloc(Tag *self) -{ - git_tag_close(self->tag); - Py_XDECREF(self->target); - Py_XDECREF(self->repo); - Py_TYPE(self)->tp_free((PyObject*)self); -} - static PyObject * Tag_get_target(Tag *self) { const git_oid *target_oid; git_otype target_type; - if (self->target == NULL) { - target_oid = git_tag_target_oid(self->tag); - target_type = git_tag_type(self->tag); - self->target = lookup_object(self->repo, target_oid, target_type); - if (self->target == NULL) - return NULL; - } - - Py_INCREF(self->target); - return self->target; + target_oid = git_tag_target_oid(self->tag); + target_type = git_tag_type(self->tag); + return lookup_object(self->repo, target_oid, target_type); } static PyObject * @@ -1764,7 +1737,7 @@ static PyTypeObject TagType = { "pygit2.Tag", /* tp_name */ sizeof(Tag), /* tp_basicsize */ 0, /* tp_itemsize */ - (destructor)Tag_dealloc, /* tp_dealloc */ + 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -1831,7 +1804,7 @@ Index_dealloc(Index* self) if (self->own_obj) git_index_free(self->index); Py_XDECREF(self->repo); - Py_TYPE(self)->tp_free((PyObject*)self); + PyObject_Del(self); } static PyObject * @@ -1953,16 +1926,15 @@ Index_contains(Index *self, PyObject *value) static PyObject * Index_iter(Index *self) { - IndexIter *iter; - - iter = PyObject_New(IndexIter, &IndexIterType); - if (!iter) - return NULL; + IndexIter *iter; - Py_INCREF(self); - iter->owner = self; - iter->i = 0; - return (PyObject*)iter; + iter = PyObject_New(IndexIter, &IndexIterType); + if (iter) { + Py_INCREF(self); + iter->owner = self; + iter->i = 0; + } + return (PyObject*)iter; } static Py_ssize_t @@ -1976,7 +1948,7 @@ wrap_index_entry(git_index_entry *entry, Index *index) { IndexEntry *py_entry; - py_entry = (IndexEntry*)IndexEntryType.tp_alloc(&IndexEntryType, 0); + py_entry = PyObject_New(IndexEntry, &IndexEntryType); if (py_entry) py_entry->entry = entry; @@ -2172,7 +2144,7 @@ static PyTypeObject IndexIterType = { static void IndexEntry_dealloc(IndexEntry *self) { - Py_TYPE(self)->tp_free((PyObject*)self); + PyObject_Del(self); } static PyObject * @@ -2253,7 +2225,7 @@ Walker_dealloc(Walker *self) { git_revwalk_free(self->walk); Py_DECREF(self->repo); - Py_TYPE(self)->tp_free((PyObject*)self); + PyObject_Del(self); } static PyObject * @@ -2339,12 +2311,11 @@ Walker_iternext(Walker *self) return Error_set(err); py_commit = PyObject_New(Commit, &CommitType); - if (!py_commit) - return NULL; - py_commit->commit = commit; - Py_INCREF(self->repo); - py_commit->repo = self->repo; - + if (py_commit) { + py_commit->commit = commit; + Py_INCREF(self->repo); + py_commit->repo = self->repo; + } return (PyObject*)py_commit; } From cb00e7a6b92e8949989d3ce4df5655d911e15b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 22 Nov 2011 00:23:54 +0100 Subject: [PATCH 0100/2237] Break reference cycle with repositoy/index --- pygit2.c | 75 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/pygit2.c b/pygit2.c index 66bdd0c11..1667eb6de 100644 --- a/pygit2.c +++ b/pygit2.c @@ -73,6 +73,7 @@ OBJECT_STRUCT(Commit, git_commit, commit) OBJECT_STRUCT(Tree, git_tree, tree) OBJECT_STRUCT(Blob, git_blob, blob) OBJECT_STRUCT(Tag, git_tag, tag) +OBJECT_STRUCT(Index, git_index, index) OBJECT_STRUCT(Walker, git_revwalk, walk) typedef struct { @@ -81,13 +82,6 @@ typedef struct { Tree *tree; } TreeEntry; -typedef struct { - PyObject_HEAD - Repository *repo; - git_index *index; - int own_obj:1; -} Index; - typedef struct { PyObject_HEAD Index *owner; @@ -402,10 +396,24 @@ Repository_init(Repository *self, PyObject *args, PyObject *kwds) static void Repository_dealloc(Repository *self) { - if (self->repo) - git_repository_free(self->repo); + PyObject_GC_UnTrack(self); Py_XDECREF(self->index); - PyObject_Del(self); + git_repository_free(self->repo); + PyObject_GC_Del(self); +} + +static int +Repository_traverse(Repository *self, visitproc visit, void *arg) +{ + Py_VISIT(self->index); + return 0; +} + +static int +Repository_clear(Repository *self) +{ + Py_CLEAR(self->index); + return 0; } static int @@ -513,14 +521,14 @@ Repository_get_index(Repository *self, void *closure) if (self->index == NULL) { err = git_repository_index(&index, self->repo); if (err == GIT_SUCCESS) { - py_index = PyObject_New(Index, &IndexType); + py_index = PyObject_GC_New(Index, &IndexType); if (!py_index) return NULL; Py_INCREF(self); py_index->repo = self; py_index->index = index; - py_index->own_obj = 0; + PyObject_GC_Track(py_index); self->index = (PyObject*)py_index; } else if (err == GIT_EBAREINDEX) { @@ -1002,10 +1010,12 @@ static PyTypeObject RepositoryType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC, /* tp_flags */ "Git repository", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ + (traverseproc)Repository_traverse, /* tp_traverse */ + (inquiry)Repository_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ @@ -1794,17 +1804,23 @@ Index_init(Index *self, PyObject *args, PyObject *kwds) return -1; } - self->own_obj = 1; return 0; } static void Index_dealloc(Index* self) { - if (self->own_obj) - git_index_free(self->index); + PyObject_GC_UnTrack(self); Py_XDECREF(self->repo); - PyObject_Del(self); + git_index_free(self->index); + PyObject_GC_Del(self); +} + +static int +Index_traverse(Index *self, visitproc visit, void *arg) +{ + Py_VISIT(self->repo); + return 0; } static PyObject * @@ -2068,9 +2084,11 @@ static PyTypeObject IndexType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC, /* tp_flags */ "Index file", /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)Index_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -2624,15 +2642,16 @@ init_repository(PyObject *self, PyObject *args) return NULL; } - py_repo = PyObject_New(Repository, &RepositoryType); - if (!py_repo) { - git_repository_free(repo); - return NULL; + py_repo = PyObject_GC_New(Repository, &RepositoryType); + if (py_repo) { + py_repo->repo = repo; + py_repo->index = NULL; + PyObject_GC_Track(py_repo); + return (PyObject*)py_repo; } - py_repo->repo = repo; - py_repo->index = NULL; - return (PyObject*)py_repo; + git_repository_free(repo); + return NULL; }; static PyMethodDef module_methods[] = { From 63ab0f3b2618ca9cdf508e0d9e06f27abdc89211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 23 Nov 2011 23:13:19 +0100 Subject: [PATCH 0101/2237] Minor coding style fixes --- pygit2.c | 134 +++++++++++++++++++++++++++---------------------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/pygit2.c b/pygit2.c index 1667eb6de..86ed7fc50 100644 --- a/pygit2.c +++ b/pygit2.c @@ -83,15 +83,15 @@ typedef struct { } TreeEntry; typedef struct { - PyObject_HEAD - Index *owner; - int i; + PyObject_HEAD + Index *owner; + int i; } IndexIter; typedef struct { - PyObject_HEAD - Tree *owner; - int i; + PyObject_HEAD + Tree *owner; + int i; } TreeIter; typedef struct { @@ -905,15 +905,15 @@ Repository_packall_references(Repository *self, PyObject *args) Py_RETURN_NONE; } -static int read_status_cb(const char *path, unsigned int status_flags, - void *payload_dict) +static int +read_status_cb(const char *path, unsigned int status_flags, void *payload) { /* This is the callback that will be called in git_status_foreach. It * will be called for every path.*/ PyObject *flags; flags = PyInt_FromLong((long) status_flags); - PyDict_SetItemString(payload_dict, path, flags); + PyDict_SetItemString(payload, path, flags); return GIT_SUCCESS; } @@ -1617,35 +1617,35 @@ TreeIter_iternext(TreeIter *self) } static PyTypeObject TreeIterType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.TreeIter", /* tp_name */ - sizeof(TreeIter), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)TreeIter_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)TreeIter_iternext, /* tp_iternext */ - }; + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.TreeIter", /* tp_name */ + sizeof(TreeIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)TreeIter_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)TreeIter_iternext, /* tp_iternext */ +}; static PyGetSetDef Blob_getseters[] = { {"data", (getter)Object_read_raw, NULL, "raw data", NULL}, @@ -2129,35 +2129,35 @@ IndexIter_iternext(IndexIter *self) } static PyTypeObject IndexIterType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.IndexIter", /* tp_name */ - sizeof(IndexIter), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)IndexIter_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)IndexIter_iternext, /* tp_iternext */ - }; + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.IndexIter", /* tp_name */ + sizeof(IndexIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)IndexIter_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)IndexIter_iternext, /* tp_iternext */ +}; static void IndexEntry_dealloc(IndexEntry *self) From 9064b8e038d34b47ade70fd924434c59d905975d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 23 Nov 2011 23:48:41 +0100 Subject: [PATCH 0102/2237] Introduce the Signature object --- TODO.txt | 6 ++ pygit2.c | 252 ++++++++++++++++++++++++++++++++------------ test/test_commit.py | 37 ++++--- test/test_tag.py | 15 ++- test/utils.py | 7 ++ 5 files changed, 225 insertions(+), 92 deletions(-) create mode 100644 TODO.txt diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 000000000..99d24f45a --- /dev/null +++ b/TODO.txt @@ -0,0 +1,6 @@ +Signature +========= +- Implement equality interface +- Return unicode for the email +- Implement interface to access the name/email as bytes +- In Repository's create_commit/create_tag check signatures encoding is right diff --git a/pygit2.c b/pygit2.c index 86ed7fc50..6c3d32f6a 100644 --- a/pygit2.c +++ b/pygit2.c @@ -104,6 +104,13 @@ typedef struct { git_reference *reference; } Reference; +typedef struct { + PyObject_HEAD + Object *obj; + const git_signature *signature; + const char *encoding; +} Signature; + static PyTypeObject RepositoryType; static PyTypeObject ObjectType; static PyTypeObject CommitType; @@ -117,6 +124,7 @@ static PyTypeObject IndexIterType; static PyTypeObject IndexEntryType; static PyTypeObject WalkerType; static PyTypeObject ReferenceType; +static PyTypeObject SignatureType; static PyObject *GitError; @@ -614,47 +622,27 @@ Repository_walk(Repository *self, PyObject *args) } static PyObject * -build_person(const git_signature *signature, const char *encoding) +build_signature(Object *obj, const git_signature *signature, + const char *encoding) { - PyObject *name; - - name = PyUnicode_Decode(signature->name, strlen(signature->name), - encoding, "strict"); - return Py_BuildValue("(NsLi)", name, signature->email, - signature->when.time, signature->when.offset); -} - -static git_signature * -py_signature_to_git_signature(PyObject *value, const char* encoding) -{ - PyObject *py_name; - char *name, *email; - long long time; - int offset; - int err; - git_signature *signature; - - if (!PyArg_ParseTuple(value, "OsLi", &py_name, &email, &time, &offset)) - return NULL; + Signature *py_signature; - name = py_str_to_c_str(py_name, encoding); - - err = git_signature_new(&signature, name, email, time, offset); - if (err < 0) { - Error_set(err); - return NULL; + py_signature = PyObject_New(Signature, &SignatureType); + if (py_signature) { + Py_INCREF(obj); + py_signature->obj = obj; + py_signature->signature = signature; + py_signature->encoding = encoding; } - - return signature; + return (PyObject*)py_signature; } static PyObject * Repository_create_commit(Repository *self, PyObject *args) { - PyObject *py_author, *py_committer; + Signature *py_author, *py_committer; PyObject *py_oid, *py_message, *py_parents, *py_parent; PyObject *py_result = NULL; - git_signature *author = NULL, *committer = NULL; char *message, *update_ref, *encoding = NULL; git_oid oid; git_tree *tree = NULL; @@ -665,21 +653,14 @@ Repository_create_commit(Repository *self, PyObject *args) if (!PyArg_ParseTuple(args, "zO!O!OOO!|s", &update_ref, - &PyTuple_Type, &py_author, - &PyTuple_Type, &py_committer, + &SignatureType, &py_author, + &SignatureType, &py_committer, &py_message, &py_oid, &PyList_Type, &py_parents, &encoding)) return NULL; - author = py_signature_to_git_signature(py_author, encoding); - if (author == NULL) - return NULL; - committer = py_signature_to_git_signature(py_committer, encoding); - if (committer == NULL) - goto out; - len = py_str_to_git_oid(py_oid, &oid); if (len == 0) goto out; @@ -708,7 +689,8 @@ Repository_create_commit(Repository *self, PyObject *args) goto out; } - err = git_commit_create(&oid, self->repo, update_ref, author, committer, + err = git_commit_create(&oid, self->repo, update_ref, + py_author->signature, py_committer->signature, encoding, message, tree, parent_count, (const git_commit**)parents); if (err < 0) { @@ -719,8 +701,6 @@ Repository_create_commit(Repository *self, PyObject *args) py_result = git_oid_to_python(oid.id); out: - git_signature_free(author); - git_signature_free(committer); git_tree_close(tree); while (i > 0) { i--; @@ -733,9 +713,9 @@ Repository_create_commit(Repository *self, PyObject *args) static PyObject * Repository_create_tag(Repository *self, PyObject *args) { - PyObject *py_oid, *py_tagger, *py_result = NULL; + PyObject *py_oid, *py_result = NULL; + Signature *py_tagger; char *tag_name, *message; - git_signature *tagger = NULL; git_oid oid; git_object *target = NULL; int err, target_type; @@ -746,14 +726,10 @@ Repository_create_tag(Repository *self, PyObject *args) &tag_name, &py_oid, &target_type, - &PyTuple_Type, &py_tagger, + &SignatureType, &py_tagger, &message)) return NULL; - tagger = py_signature_to_git_signature(py_tagger, NULL); - if (tagger == NULL) - return NULL; - len = py_str_to_git_oid(py_oid, &oid); if (len == 0) goto out; @@ -767,13 +743,12 @@ Repository_create_tag(Repository *self, PyObject *args) goto out; } - err = git_tag_create(&oid, self->repo, tag_name, target, tagger, message, - 0); + err = git_tag_create(&oid, self->repo, tag_name, target, + py_tagger->signature, message, 0); if (err == 0) py_result = git_oid_to_python(oid.id); out: - git_signature_free(tagger); git_object_close(target); return py_result; } @@ -1187,29 +1162,27 @@ Commit_get_commit_time_offset(Commit *commit) } static PyObject * -Commit_get_committer(Commit *commit) +Commit_get_committer(Commit *self) { const git_signature *signature; const char *encoding; - signature = git_commit_committer(commit->commit); - encoding = git_commit_message_encoding(commit->commit); - if (encoding == NULL) - encoding = "utf-8"; - return build_person(signature, encoding); + signature = git_commit_committer(self->commit); + encoding = git_commit_message_encoding(self->commit); + + return build_signature((Object*)self, signature, encoding); } static PyObject * -Commit_get_author(Commit *commit) +Commit_get_author(Commit *self) { const git_signature *signature; const char *encoding; - signature = git_commit_author(commit->commit); - encoding = git_commit_message_encoding(commit->commit); - if (encoding == NULL) - encoding = "utf-8"; - return build_person(signature, encoding); + signature = git_commit_author(self->commit); + encoding = git_commit_message_encoding(self->commit); + + return build_signature((Object*)self, signature, encoding); } static PyObject * @@ -1715,12 +1688,13 @@ Tag_get_name(Tag *self) } static PyObject * -Tag_get_tagger(Tag *tag) +Tag_get_tagger(Tag *self) { - const git_signature *signature = git_tag_tagger(tag->tag); + const git_signature *signature = git_tag_tagger(self->tag); if (!signature) Py_RETURN_NONE; - return build_person(signature, "utf-8"); + + return build_signature((Object*)self, signature, "utf-8"); } static PyObject * @@ -2624,6 +2598,142 @@ static PyTypeObject ReferenceType = { 0, /* tp_new */ }; +static int +Signature_init(Signature *self, PyObject *args, PyObject *kwds) +{ + PyObject *py_name; + char *name, *email, *encoding = NULL; + long long time; + int offset; + int err; + git_signature *signature; + + if (kwds) { + PyErr_SetString(PyExc_TypeError, + "Signature takes no keyword arguments"); + return -1; + } + + if (!PyArg_ParseTuple(args, "OsLi|s", + &py_name, &email, &time, &offset, &encoding)) + return -1; + + name = py_str_to_c_str(py_name, encoding); + + err = git_signature_new(&signature, name, email, time, offset); + if (err < 0) { + Error_set(err); + return -1; + } + + self->obj = NULL; + self->signature = signature; + + if (encoding) { + self->encoding = strdup(encoding); + if (self->encoding == NULL) { + PyErr_NoMemory(); + return -1; + } + } + + return 0; +} + +static void +Signature_dealloc(Signature *self) +{ + if (self->obj) + Py_DECREF(self->obj); + else { + git_signature_free((git_signature*)self->signature); + free((void*)self->encoding); + } + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static PyObject * +Signature_get_name(Signature *self) +{ + const char *encoding; + git_object *object; + + encoding = self->encoding; + if (encoding == NULL) + encoding = "utf-8"; + + return PyUnicode_Decode(self->signature->name, + strlen(self->signature->name), + encoding, "strict"); +} + +static PyObject * +Signature_get_email(Signature *self) +{ + return PyString_FromString(self->signature->email); +} + +static PyObject * +Signature_get_time(Signature *self) +{ + return PyInt_FromLong(self->signature->when.time); +} + +static PyObject * +Signature_get_offset(Signature *self) +{ + return PyInt_FromLong(self->signature->when.offset); +} + +static PyGetSetDef Signature_getseters[] = { + {"name", (getter)Signature_get_name, NULL, "Name", NULL}, + {"email", (getter)Signature_get_email, NULL, "Email", NULL}, + {"time", (getter)Signature_get_time, NULL, "Time", NULL}, + {"offset", (getter)Signature_get_offset, NULL, "Offset", NULL}, + {NULL} +}; + +static PyTypeObject SignatureType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Signature", /* tp_name */ + sizeof(Signature), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Signature_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Signature", /* 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 */ + Signature_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Signature_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + static PyObject * init_repository(PyObject *self, PyObject *args) { @@ -2704,6 +2814,9 @@ moduleinit(PyObject* m) ReferenceType.tp_new = PyType_GenericNew; if (PyType_Ready(&ReferenceType) < 0) return NULL; + SignatureType.tp_new = PyType_GenericNew; + if (PyType_Ready(&SignatureType) < 0) + return NULL; Py_INCREF(GitError); PyModule_AddObject(m, "GitError", GitError); @@ -2738,6 +2851,9 @@ moduleinit(PyObject* m) Py_INCREF(&ReferenceType); PyModule_AddObject(m, "Reference", (PyObject *)&ReferenceType); + Py_INCREF(&SignatureType); + PyModule_AddObject(m, "Signature", (PyObject *)&SignatureType); + 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/test/test_commit.py b/test/test_commit.py index 71716a2bd..526d7d1a8 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -31,7 +31,7 @@ from __future__ import unicode_literals import unittest -from pygit2 import GIT_OBJ_COMMIT +from pygit2 import GIT_OBJ_COMMIT, Signature from . import utils @@ -55,20 +55,22 @@ def test_read_commit(self): commit.message) commit_time = 1288481576 self.assertEqual(commit_time, commit.commit_time) - self.assertEqual( - ('Dave Borowitz', 'dborowitz@google.com', commit_time, -420), - commit.committer) - self.assertEqual( - ('Dave Borowitz', 'dborowitz@google.com', 1288477363, -420), - commit.author) + self.assertEqualSignature( + commit.committer, + Signature('Dave Borowitz', 'dborowitz@google.com', + commit_time, -420)) + self.assertEqualSignature( + commit.author, + Signature('Dave Borowitz', 'dborowitz@google.com', 1288477363, + -420)) self.assertEqual( '967fce8df97cc71722d3c2a5930ef3e6f1d27b12', commit.tree.hex) def test_new_commit(self): repo = self.repo message = 'New commit.\n\nMessage with non-ascii chars: ééé.\n' - committer = ('John Doe', 'jdoe@example.com', 12346, 0) - author = ('J. David Ibáñez', 'jdavid@example.com', 12345, 0) + committer = Signature('John Doe', 'jdoe@example.com', 12346, 0) + author = Signature('J. David Ibáñez', 'jdavid@example.com', 12345, 0) tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' tree_prefix = tree[:5] too_short_prefix = tree[:3] @@ -87,31 +89,34 @@ def test_new_commit(self): self.assertEqual(None, commit.message_encoding) self.assertEqual(message, commit.message) self.assertEqual(12346, commit.commit_time) - self.assertEqual(committer, commit.committer) - self.assertEqual(author, commit.author) + self.assertEqualSignature(committer, commit.committer) + self.assertEqualSignature(author, commit.author) self.assertEqual(tree, commit.tree.hex) self.assertEqual(1, len(commit.parents)) self.assertEqual(COMMIT_SHA, commit.parents[0].hex) def test_new_commit_encoding(self): repo = self.repo + encoding = 'iso-8859-1' message = 'New commit.\n\nMessage with non-ascii chars: ééé.\n' - committer = ('John Doe', 'jdoe@example.com', 12346, 0) - author = ('J. David Ibáñez', 'jdavid@example.com', 12345, 0) + committer = Signature('John Doe', 'jdoe@example.com', 12346, 0, + encoding) + author = Signature('J. David Ibáñez', 'jdavid@example.com', 12345, 0, + encoding) tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' tree_prefix = tree[:5] parents = [COMMIT_SHA[:5]] sha = repo.create_commit(None, author, committer, message, - tree_prefix, parents, 'iso-8859-1') + tree_prefix, parents, encoding) commit = repo[sha] self.assertEqual(GIT_OBJ_COMMIT, commit.type) self.assertEqual('iso-8859-1', commit.message_encoding) self.assertEqual(message, commit.message) self.assertEqual(12346, commit.commit_time) - self.assertEqual(committer, commit.committer) - self.assertEqual(author, commit.author) + self.assertEqualSignature(committer, commit.committer) + self.assertEqualSignature(author, commit.author) self.assertEqual(tree, commit.tree.hex) self.assertEqual(1, len(commit.parents)) self.assertEqual(COMMIT_SHA, commit.parents[0].hex) diff --git a/test/test_tag.py b/test/test_tag.py index a138882cf..5c11e0189 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -48,19 +48,18 @@ def test_read_tag(self): self.assertEqual(pygit2.GIT_OBJ_TAG, tag.type) self.assertEqual(pygit2.GIT_OBJ_COMMIT, tag.target.type) self.assertEqual('root', tag.name) - self.assertEqual( - ('Dave Borowitz', 'dborowitz@google.com', 1288724692, -420), - tag.tagger) self.assertEqual('Tagged root commit.\n', tag.message) - - commit = tag.target - self.assertEqual('Initial test data commit.\n', commit.message) + self.assertEqual('Initial test data commit.\n', tag.target.message) + self.assertEqualSignature( + tag.tagger, + pygit2.Signature('Dave Borowitz', 'dborowitz@google.com', + 1288724692, -420)) def test_new_tag(self): name = 'thetag' target = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' message = 'Tag a blob.\n' - tagger = ('John Doe', 'jdoe@example.com', 12347, 0) + tagger = pygit2.Signature('John Doe', 'jdoe@example.com', 12347, 0) target_prefix = target[:5] too_short_prefix = target[:3] @@ -74,7 +73,7 @@ def test_new_tag(self): self.assertEqual('3ee44658fd11660e828dfc96b9b5c5f38d5b49bb', tag.hex) self.assertEqual(name, tag.name) self.assertEqual(target, tag.target.hex) - self.assertEqual(tagger, tag.tagger) + self.assertEqualSignature(tagger, tag.tagger) self.assertEqual(message, tag.message) self.assertEqual(name, self.repo[tag.hex].name) diff --git a/test/utils.py b/test/utils.py index b9c75493b..f56d29d63 100644 --- a/test/utils.py +++ b/test/utils.py @@ -74,6 +74,13 @@ def assertRaisesWithArg(self, exc_class, arg, func, *args, **kwargs): else: self.fail('%s(%r) not raised' % (exc_class.__name__, arg)) + def assertEqualSignature(self, a, b): + # XXX Remove this once equality test is supported by Signature + self.assertEqual(a.name, b.name) + self.assertEqual(a.email, b.email) + self.assertEqual(a.time, b.time) + self.assertEqual(a.offset, b.offset) + class BareRepoTestCase(NoRepoTestCase): From eafcef38f6424c690db96241d9a44cd4360ef80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 24 Nov 2011 22:26:29 +0100 Subject: [PATCH 0103/2237] Do not pretend Python 2.5 is supported --- pygit2.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pygit2.c b/pygit2.c index 6c3d32f6a..874e66783 100644 --- a/pygit2.c +++ b/pygit2.c @@ -30,14 +30,6 @@ #include #include -/* Python 2.5 support */ -#ifndef Py_TYPE - #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) -#endif -#ifndef PyVarObject_HEAD_INIT - #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, -#endif - /* Python 3 support */ #if PY_MAJOR_VERSION >= 3 #define PyInt_AsLong PyLong_AsLong From a50c886cbb0a65ea2c336d626a933ab04332565f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 25 Nov 2011 00:03:32 +0100 Subject: [PATCH 0104/2237] signature: add bytes accessors for name/email Now Signature.email returns unicode. The name and email are available as byte strings through the Signature._name and Signature._email accessors respectively. Signature._encoding returns the encoding used in the signature. --- TODO.txt | 2 -- pygit2.c | 56 +++++++++++++++++++++++++++++------------ test/__init__.py | 2 +- test/test_signature.py | 57 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 19 deletions(-) create mode 100644 test/test_signature.py diff --git a/TODO.txt b/TODO.txt index 99d24f45a..d151325e0 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,6 +1,4 @@ Signature ========= - Implement equality interface -- Return unicode for the email -- Implement interface to access the name/email as bytes - In Repository's create_commit/create_tag check signatures encoding is right diff --git a/pygit2.c b/pygit2.c index 874e66783..cda16b6da 100644 --- a/pygit2.c +++ b/pygit2.c @@ -44,7 +44,19 @@ #define PyString_Size PyBytes_Size #endif +/* Utilities */ +static PyObject * +to_str(const char *value, const char *encoding, const char *errors) +{ + if (encoding == NULL) + encoding = "utf-8"; + return PyUnicode_Decode(value, strlen(value), encoding, errors); +} + +#define to_bytes(value) PyString_FromString(value) + +/* Python objects */ typedef struct { PyObject_HEAD git_repository *repo; @@ -358,15 +370,11 @@ py_str_to_c_str(PyObject *value, const char *encoding) return NULL; } -#define c_str_to_py_str(c_str) \ - PyUnicode_DecodeUTF8(c_str, strlen(c_str), "strict") - #define py_path_to_c_str(py_path) \ py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding) #define c_str_to_py_path(c_str) \ - PyUnicode_Decode(c_str, strlen(c_str), \ - Py_FileSystemDefaultEncoding, "strict") + to_str(c_str, Py_FileSystemDefaultEncoding, "strict") static int @@ -1136,9 +1144,7 @@ Commit_get_message(Commit *commit) message = git_commit_message(commit->commit); encoding = git_commit_message_encoding(commit->commit); - if (encoding == NULL) - encoding = "utf-8"; - return PyUnicode_Decode(message, strlen(message), encoding, "strict"); + return to_str(message, encoding, "strict"); } static PyObject * @@ -1676,7 +1682,7 @@ Tag_get_name(Tag *self) name = git_tag_name(self->tag); if (!name) Py_RETURN_NONE; - return c_str_to_py_str(name); + return to_str(name, "utf-8", "strict"); } static PyObject * @@ -1696,7 +1702,7 @@ Tag_get_message(Tag *self) message = git_tag_message(self->tag); if (!message) Py_RETURN_NONE; - return c_str_to_py_str(message); + return to_str(message, "utf-8", "strict"); } static PyGetSetDef Tag_getseters[] = { @@ -2645,24 +2651,39 @@ Signature_dealloc(Signature *self) } static PyObject * -Signature_get_name(Signature *self) +Signature_get_encoding(Signature *self) { const char *encoding; - git_object *object; encoding = self->encoding; if (encoding == NULL) encoding = "utf-8"; - return PyUnicode_Decode(self->signature->name, - strlen(self->signature->name), - encoding, "strict"); + return PyUnicode_DecodeASCII(encoding, strlen(encoding), "strict"); +} + +static PyObject * +Signature_get_raw_name(Signature *self) +{ + return to_bytes(self->signature->name); +} + +static PyObject * +Signature_get_raw_email(Signature *self) +{ + return to_bytes(self->signature->email); +} + +static PyObject * +Signature_get_name(Signature *self) +{ + return to_str(self->signature->name, self->encoding, "strict"); } static PyObject * Signature_get_email(Signature *self) { - return PyString_FromString(self->signature->email); + return to_str(self->signature->email, self->encoding, "strict"); } static PyObject * @@ -2678,6 +2699,9 @@ Signature_get_offset(Signature *self) } static PyGetSetDef Signature_getseters[] = { + {"_encoding", (getter)Signature_get_encoding, NULL, "encoding", NULL}, + {"_name", (getter)Signature_get_raw_name, NULL, "Name (bytes)", NULL}, + {"_email", (getter)Signature_get_raw_email, NULL, "Email (bytes)", NULL}, {"name", (getter)Signature_get_name, NULL, "Name", NULL}, {"email", (getter)Signature_get_email, NULL, "Email", NULL}, {"time", (getter)Signature_get_time, NULL, "Time", NULL}, diff --git a/test/__init__.py b/test/__init__.py index 237975745..1864663a6 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -36,7 +36,7 @@ names = ['blob', 'commit', 'index', 'refs', 'repository', 'revwalk', 'tag', - 'tree', 'status'] + 'tree', 'signature', 'status'] def test_suite(): modules = ['test.test_%s' % n for n in names] return unittest.defaultTestLoader.loadTestsFromNames(modules) diff --git a/test/test_signature.py b/test/test_signature.py new file mode 100644 index 000000000..cebbf1b38 --- /dev/null +++ b/test/test_signature.py @@ -0,0 +1,57 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2011 J. David Ibáñez +# +# 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. + +from __future__ import absolute_import +from __future__ import unicode_literals +import unittest + +from pygit2 import Signature +from .utils import NoRepoTestCase + + +__author__ = 'jdavid.ibp@gmail.com (J. David Ibáñez)' + + + +class SignatureTest(NoRepoTestCase): + + def test_default(self): + signature = Signature('Foo Ibáñez', 'foo@example.com', 1322174594, 60) + encoding = signature._encoding + self.assertEqual(signature.name, signature._name.decode(encoding)) + self.assertEqual(signature.name.encode(encoding), signature._name) + + def test_latin1(self): + encoding = 'iso-8859-1' + signature = Signature('Foo Ibáñez', 'foo@example.com', 1322174594, 60, + encoding) + self.assertEqual(signature.name, signature._name.decode(encoding)) + self.assertEqual(signature.name.encode(encoding), signature._name) + + +if __name__ == '__main__': + unittest.main() From dd33369fba7393cbe405e9b1905207e1450e3890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 26 Nov 2011 13:21:40 +0100 Subject: [PATCH 0105/2237] signature: minor test addition --- test/test_signature.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_signature.py b/test/test_signature.py index cebbf1b38..fb9d89185 100644 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -42,6 +42,7 @@ class SignatureTest(NoRepoTestCase): def test_default(self): signature = Signature('Foo Ibáñez', 'foo@example.com', 1322174594, 60) encoding = signature._encoding + self.assertEqual(encoding, 'utf-8') self.assertEqual(signature.name, signature._name.decode(encoding)) self.assertEqual(signature.name.encode(encoding), signature._name) @@ -49,6 +50,7 @@ def test_latin1(self): encoding = 'iso-8859-1' signature = Signature('Foo Ibáñez', 'foo@example.com', 1322174594, 60, encoding) + self.assertEqual(encoding, signature._encoding) self.assertEqual(signature.name, signature._name.decode(encoding)) self.assertEqual(signature.name.encode(encoding), signature._name) From ee9429217b96e2a1135724a3f75a4cda30a68ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 26 Nov 2011 23:49:36 +0100 Subject: [PATCH 0106/2237] Start using inline functions Actually, the Py_LOCAL_INLINE macro only works with Python 3.2, we should fix this. --- TODO.txt | 4 ++++ pygit2.c | 37 +++++++++++++++++++------------------ 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/TODO.txt b/TODO.txt index d151325e0..2b0467f81 100644 --- a/TODO.txt +++ b/TODO.txt @@ -2,3 +2,7 @@ Signature ========= - Implement equality interface - In Repository's create_commit/create_tag check signatures encoding is right + +Other +========= +- Make the Py_LOCAL_INLINE macro to work with Python 2.6, 2.7 and 3.1 diff --git a/pygit2.c b/pygit2.c index cda16b6da..9ad8ee2c1 100644 --- a/pygit2.c +++ b/pygit2.c @@ -45,7 +45,7 @@ #endif /* Utilities */ -static PyObject * +Py_LOCAL_INLINE(PyObject*) to_str(const char *value, const char *encoding, const char *errors) { if (encoding == NULL) @@ -53,7 +53,17 @@ to_str(const char *value, const char *encoding, const char *errors) return PyUnicode_Decode(value, strlen(value), encoding, errors); } -#define to_bytes(value) PyString_FromString(value) +Py_LOCAL_INLINE(PyObject*) +to_bytes(const char * value) +{ + return PyString_FromString(value); +} + +Py_LOCAL_INLINE(PyObject*) +to_path(const char *value) +{ + return to_str(value, Py_FileSystemDefaultEncoding, "strict"); +} /* Python objects */ @@ -373,9 +383,6 @@ 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 c_str_to_py_path(c_str) \ - to_str(c_str, Py_FileSystemDefaultEncoding, "strict") - static int Repository_init(Repository *self, PyObject *args, PyObject *kwds) @@ -555,10 +562,7 @@ Repository_get_index(Repository *self, void *closure) static PyObject * Repository_get_path(Repository *self, void *closure) { - const char *c_path; - - c_path = git_repository_path(self->repo, GIT_REPO_PATH); - return c_str_to_py_path(c_path); + return to_path(git_repository_path(self->repo, GIT_REPO_PATH)); } static PyObject * @@ -570,7 +574,7 @@ Repository_get_workdir(Repository *self, void *closure) if (c_path == NULL) Py_RETURN_NONE; - return c_str_to_py_path(c_path); + return to_path(c_path); } static PyObject * @@ -780,7 +784,7 @@ Repository_listall_references(Repository *self, PyObject *args) /* 4- Fill it */ for (index=0; index < c_result.count; index++) { - py_string = c_str_to_py_path((c_result.strings)[index]); + py_string = to_path((c_result.strings)[index]); if (py_string == NULL) { Py_XDECREF(py_result); git_strarray_free(&c_result); @@ -1311,7 +1315,7 @@ TreeEntry_get_attributes(TreeEntry *self) static PyObject * TreeEntry_get_name(TreeEntry *self) { - return c_str_to_py_path(git_tree_entry_name(self->entry)); + return to_path(git_tree_entry_name(self->entry)); } static PyObject * @@ -2146,7 +2150,7 @@ IndexEntry_get_mode(IndexEntry *self) static PyObject * IndexEntry_get_path(IndexEntry *self) { - return c_str_to_py_path(self->entry->path); + return to_path(self->entry->path); } static PyObject * @@ -2427,7 +2431,7 @@ Reference_get_target(Reference *self) } /* 2- Make a PyString and return it */ - return c_str_to_py_path(c_name); + return to_path(c_name); } static int @@ -2455,10 +2459,7 @@ Reference_set_target(Reference *self, PyObject *py_name) static PyObject * Reference_get_name(Reference *self) { - const char *c_name; - - c_name = git_reference_name(self->reference); - return c_str_to_py_path(c_name); + return to_path(git_reference_name(self->reference)); } static PyObject * From 3037d26c9d34fc620fe8dc76363041f885fe3644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 27 Nov 2011 10:05:58 +0100 Subject: [PATCH 0107/2237] Fix unit tests with Python 3.2 --- test/test_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_repository.py b/test/test_repository.py index c62537f66..66d1a5a41 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -42,7 +42,7 @@ __author__ = 'dborowitz@google.com (Dave Borowitz)' A_HEX_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' -A_BIN_SHA = binascii.unhexlify(A_HEX_SHA) +A_BIN_SHA = binascii.unhexlify(A_HEX_SHA.encode('ascii')) class RepositoryTest(utils.BareRepoTestCase): From 1a34bc3b60f70666a6f27a76b50689ef1e8086e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 28 Nov 2011 22:47:35 +0100 Subject: [PATCH 0108/2237] Update .gitignore, add test/__pycache__ The __pycache__ folder is genarated by Python 3.2 --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7151ca7cd..b0d892a21 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ build dist pygit2.so test/*.pyc +test/__pycache__ *.egg-info *.swp From 3073a7c3daa07419c79e6895250009cde6b85c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 28 Nov 2011 23:33:16 +0100 Subject: [PATCH 0109/2237] Return bytes for paths (and encodings) on Python 2 --- pygit2.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/pygit2.c b/pygit2.c index 9ad8ee2c1..b5520c3d0 100644 --- a/pygit2.c +++ b/pygit2.c @@ -46,7 +46,7 @@ /* Utilities */ Py_LOCAL_INLINE(PyObject*) -to_str(const char *value, const char *encoding, const char *errors) +to_unicode(const char *value, const char *encoding, const char *errors) { if (encoding == NULL) encoding = "utf-8"; @@ -59,11 +59,13 @@ to_bytes(const char * value) return PyString_FromString(value); } -Py_LOCAL_INLINE(PyObject*) -to_path(const char *value) -{ - return to_str(value, Py_FileSystemDefaultEncoding, "strict"); -} +#if PY_MAJOR_VERSION == 2 + #define to_path(x) to_bytes(x) + #define to_encoding(x) to_bytes(x) +#else + #define to_path(x) to_unicode(x, Py_FileSystemDefaultEncoding, "strict") + #define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict") +#endif /* Python objects */ @@ -1138,7 +1140,7 @@ Commit_get_message_encoding(Commit *commit) if (encoding == NULL) Py_RETURN_NONE; - return PyUnicode_DecodeASCII(encoding, strlen(encoding), "strict"); + return to_encoding(encoding); } static PyObject * @@ -1148,7 +1150,7 @@ Commit_get_message(Commit *commit) message = git_commit_message(commit->commit); encoding = git_commit_message_encoding(commit->commit); - return to_str(message, encoding, "strict"); + return to_unicode(message, encoding, "strict"); } static PyObject * @@ -1686,7 +1688,7 @@ Tag_get_name(Tag *self) name = git_tag_name(self->tag); if (!name) Py_RETURN_NONE; - return to_str(name, "utf-8", "strict"); + return to_unicode(name, "utf-8", "strict"); } static PyObject * @@ -1706,7 +1708,7 @@ Tag_get_message(Tag *self) message = git_tag_message(self->tag); if (!message) Py_RETURN_NONE; - return to_str(message, "utf-8", "strict"); + return to_unicode(message, "utf-8", "strict"); } static PyGetSetDef Tag_getseters[] = { @@ -2660,7 +2662,7 @@ Signature_get_encoding(Signature *self) if (encoding == NULL) encoding = "utf-8"; - return PyUnicode_DecodeASCII(encoding, strlen(encoding), "strict"); + return to_encoding(encoding); } static PyObject * @@ -2678,13 +2680,13 @@ Signature_get_raw_email(Signature *self) static PyObject * Signature_get_name(Signature *self) { - return to_str(self->signature->name, self->encoding, "strict"); + return to_unicode(self->signature->name, self->encoding, "strict"); } static PyObject * Signature_get_email(Signature *self) { - return to_str(self->signature->email, self->encoding, "strict"); + return to_unicode(self->signature->email, self->encoding, "strict"); } static PyObject * From 2dee35d1be5c1335b409756985f41077336855f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 4 Dec 2011 23:26:38 +0100 Subject: [PATCH 0110/2237] Update to latest changes in libgit2 --- TODO.txt | 1 + pygit2.c | 88 +++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/TODO.txt b/TODO.txt index 2b0467f81..d878858d5 100644 --- a/TODO.txt +++ b/TODO.txt @@ -6,3 +6,4 @@ Signature Other ========= - Make the Py_LOCAL_INLINE macro to work with Python 2.6, 2.7 and 3.1 +- Expose the ODB (Repository.odb) diff --git a/pygit2.c b/pygit2.c index b5520c3d0..d32751826 100644 --- a/pygit2.c +++ b/pygit2.c @@ -437,14 +437,24 @@ static int Repository_contains(Repository *self, PyObject *value) { git_oid oid; + git_odb *odb; size_t len; + int err, exists; len = py_str_to_git_oid(value, &oid); TODO_SUPPORT_SHORT_HEXS(len) if (len == 0) return -1; - return git_odb_exists(git_repository_database(self->repo), &oid); + err = git_repository_odb(&odb, self->repo); + if (err < 0) { + Error_set_str(err, "failed to open object database"); + return -1; + } + + exists = git_odb_exists(odb, &oid); + git_odb_free(odb); + return exists; } static PyObject * @@ -460,12 +470,30 @@ Repository_getitem(Repository *self, PyObject *value) return lookup_object_prefix(self, &oid, len, GIT_OBJ_ANY); } -static int -Repository_read_raw(git_odb_object **obj, git_repository *repo, - const git_oid *oid, size_t len) +static git_odb_object * +Repository_read_raw(git_repository *repo, const git_oid *oid, size_t len) { - return git_odb_read_prefix(obj, git_repository_database(repo), - oid, (unsigned int)len); + git_odb *odb; + git_odb_object *obj; + int err; + PyObject *aux; + + err = git_repository_odb(&odb, repo); + if (err < 0) { + Error_set_str(err, "failed to open object database"); + return NULL; + } + + err = git_odb_read_prefix(&obj, odb, oid, (unsigned int)len); + git_odb_free(odb); + if (err < 0) { + aux = git_oid_to_py_str(oid); + Error_set_py_obj(err, aux); + Py_XDECREF(aux); + return NULL; + } + + return obj; } static PyObject * @@ -480,9 +508,9 @@ Repository_read(Repository *self, PyObject *py_hex) if (len == 0) return NULL; - err = Repository_read_raw(&obj, self->repo, &oid, len); - if (err < 0) - return Error_set_py_obj(err, py_hex); + obj = Repository_read_raw(self->repo, &oid, len); + if (obj == NULL) + return NULL; PyObject* tuple = Py_BuildValue( "(ns#)", @@ -490,7 +518,7 @@ Repository_read(Repository *self, PyObject *py_hex) git_odb_object_data(obj), git_odb_object_size(obj)); - git_odb_object_close(obj); + git_odb_object_free(obj); return tuple; } @@ -499,6 +527,7 @@ Repository_write(Repository *self, PyObject *args) { int err; git_oid oid; + git_odb *odb; git_odb_stream* stream; int type_id; @@ -512,17 +541,20 @@ Repository_write(Repository *self, PyObject *args) if (type == GIT_OBJ_BAD) return Error_set_str(-100, "Invalid object type"); - git_odb* odb = git_repository_database(self->repo); + err = git_repository_odb(&odb, self->repo); + if (err < 0) { + Error_set_str(err, "failed to open object database"); + return NULL; + } err = git_odb_open_wstream(&stream, odb, buflen, type); - if (err == GIT_SUCCESS) { - stream->write(stream, buffer, buflen); - err = stream->finalize_write(&oid, stream); - stream->free(stream); - } + git_odb_free(odb); if (err < 0) return Error_set_str(err, "failed to write data"); + stream->write(stream, buffer, buflen); + err = stream->finalize_write(&oid, stream); + stream->free(stream); return git_oid_to_python(oid.id); } @@ -564,7 +596,7 @@ Repository_get_index(Repository *self, void *closure) static PyObject * Repository_get_path(Repository *self, void *closure) { - return to_path(git_repository_path(self->repo, GIT_REPO_PATH)); + return to_path(git_repository_path(self->repo)); } static PyObject * @@ -572,7 +604,7 @@ Repository_get_workdir(Repository *self, void *closure) { const char *c_path; - c_path = git_repository_path(self->repo, GIT_REPO_PATH_WORKDIR); + c_path = git_repository_workdir(self->repo); if (c_path == NULL) Py_RETURN_NONE; @@ -707,10 +739,10 @@ Repository_create_commit(Repository *self, PyObject *args) py_result = git_oid_to_python(oid.id); out: - git_tree_close(tree); + git_tree_free(tree); while (i > 0) { i--; - git_commit_close(parents[i]); + git_commit_free(parents[i]); } free(parents); return py_result; @@ -755,7 +787,7 @@ Repository_create_tag(Repository *self, PyObject *args) py_result = git_oid_to_python(oid.id); out: - git_object_close(target); + git_object_free(target); return py_result; } @@ -1017,7 +1049,7 @@ static PyTypeObject RepositoryType = { static void Object_dealloc(Object* self) { - git_object_close(self->obj); + git_object_free(self->obj); Py_XDECREF(self->repo); PyObject_Del(self); } @@ -1056,24 +1088,20 @@ Object_read_raw(Object *self) const git_oid *oid; git_odb_object *obj; int err; - PyObject *aux = NULL; + PyObject *aux; oid = git_object_id(self->obj); assert(oid); - err = Repository_read_raw(&obj, self->repo->repo, oid, GIT_OID_HEXSZ); - if (err < 0) { - aux = git_oid_to_py_str(oid); - Error_set_py_obj(err, aux); - Py_XDECREF(aux); + obj = Repository_read_raw(self->repo->repo, oid, GIT_OID_HEXSZ); + if (obj == NULL) return NULL; - } aux = PyString_FromStringAndSize( git_odb_object_data(obj), git_odb_object_size(obj)); - git_odb_object_close(obj); + git_odb_object_free(obj); return aux; } From 01cb80b4c89718e15bdcba751d5cc9bb4dbed2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 6 Dec 2011 23:36:20 +0100 Subject: [PATCH 0111/2237] errors: pass through git_lasterror in several places And clean up related code a little bit. --- pygit2.c | 118 +++++++++++++--------------------------- test/test_repository.py | 5 +- 2 files changed, 40 insertions(+), 83 deletions(-) diff --git a/pygit2.c b/pygit2.c index d32751826..a5c537f17 100644 --- a/pygit2.c +++ b/pygit2.c @@ -167,17 +167,10 @@ static PyObject * Error_set(int err) { assert(err < 0); - if (err == GIT_ENOTFOUND) { - /* KeyError expects the arg to be the missing key. If the caller - * called this instead of Error_set_py_obj, it means we don't - * know the key, but nor should we use git_lasterror. */ - PyErr_SetNone(PyExc_KeyError); - return NULL; - } - else if (err == GIT_EOSERR) { - PyErr_SetFromErrno(GitError); - return NULL; - } + + if (err == GIT_EOSERR) + return PyErr_SetFromErrno(GitError); + PyErr_SetString(Error_type(err), git_lasterror()); return NULL; } @@ -187,38 +180,21 @@ Error_set_str(int err, const char *str) { if (err == GIT_ENOTFOUND) { /* KeyError expects the arg to be the missing key. */ - PyErr_Format(PyExc_KeyError, "%s", str); + PyErr_SetString(PyExc_KeyError, str); return NULL; } - PyErr_Format(Error_type(err), "%s: %s", str, git_lasterror()); - return NULL; + + return PyErr_Format(Error_type(err), "%s: %s", str, git_lasterror()); } static PyObject * -Error_set_py_obj(int err, PyObject *py_obj) +Error_set_oid(int err, const git_oid *oid, size_t len) { - PyObject *py_str; - char *str; - - assert(err < 0); + char hex[GIT_OID_HEXSZ + 1]; - if (err == GIT_ENOTOID && !PyString_Check(py_obj) - && !PyUnicode_Check(py_obj)) { - PyErr_Format(PyExc_TypeError, - "Git object id must be byte or a text string, not: %.200s", - Py_TYPE(py_obj)->tp_name); - return NULL; - } - else if (err == GIT_ENOTFOUND) { - /* KeyError expects the arg to be the missing key. */ - PyErr_SetObject(PyExc_KeyError, py_obj); - return NULL; - } - py_str = PyObject_Bytes(py_obj); - str = py_str ? PyString_AS_STRING(py_str) : ""; - PyErr_Format(Error_type(err), "%s: %s", str, git_lasterror()); - Py_XDECREF(py_str); - return NULL; + git_oid_fmt(hex, oid); + hex[len] = '\0'; + return Error_set_str(err, hex); } static PyObject * @@ -226,17 +202,13 @@ lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, git_otype type) { int err; - char hex[GIT_OID_HEXSZ + 1]; git_object *obj; Object *py_obj = NULL; err = git_object_lookup_prefix(&obj, repo->repo, oid, (unsigned int)len, type); - if (err < 0) { - git_oid_fmt(hex, oid); - hex[len] = '\0'; - return Error_set_str(err, hex); - } + if (err < 0) + return Error_set_oid(err, oid, len); switch (git_object_type(obj)) { case GIT_OBJ_COMMIT: @@ -321,7 +293,7 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) return 0; err = git_oid_fromstrn(oid, hex_or_bin, len); if (err < 0) { - Error_set_py_obj(err, py_str); + PyErr_SetObject(Error_type(err), py_str); return 0; } return len; @@ -448,7 +420,7 @@ Repository_contains(Repository *self, PyObject *value) err = git_repository_odb(&odb, self->repo); if (err < 0) { - Error_set_str(err, "failed to open object database"); + Error_set(err); return -1; } @@ -480,16 +452,14 @@ Repository_read_raw(git_repository *repo, const git_oid *oid, size_t len) err = git_repository_odb(&odb, repo); if (err < 0) { - Error_set_str(err, "failed to open object database"); + Error_set(err); return NULL; } err = git_odb_read_prefix(&obj, odb, oid, (unsigned int)len); git_odb_free(odb); if (err < 0) { - aux = git_oid_to_py_str(oid); - Error_set_py_obj(err, aux); - Py_XDECREF(aux); + Error_set_oid(err, oid, len); return NULL; } @@ -529,28 +499,26 @@ Repository_write(Repository *self, PyObject *args) git_oid oid; git_odb *odb; git_odb_stream* stream; - int type_id; const char* buffer; Py_ssize_t buflen; + git_otype type; if (!PyArg_ParseTuple(args, "Is#", &type_id, &buffer, &buflen)) return NULL; - git_otype type = int_to_loose_object_type(type_id); + type = int_to_loose_object_type(type_id); if (type == GIT_OBJ_BAD) - return Error_set_str(-100, "Invalid object type"); + return PyErr_Format(PyExc_ValueError, "%d", type_id); err = git_repository_odb(&odb, self->repo); - if (err < 0) { - Error_set_str(err, "failed to open object database"); - return NULL; - } + if (err < 0) + return Error_set(err); err = git_odb_open_wstream(&stream, odb, buflen, type); git_odb_free(odb); if (err < 0) - return Error_set_str(err, "failed to write data"); + return Error_set(err); stream->write(stream, buffer, buflen); err = stream->finalize_write(&oid, stream); @@ -757,7 +725,6 @@ Repository_create_tag(Repository *self, PyObject *args) git_oid oid; git_object *target = NULL; int err, target_type; - char hex[GIT_OID_HEXSZ + 1]; size_t len; if (!PyArg_ParseTuple(args, "sOiO!s", @@ -770,23 +737,18 @@ Repository_create_tag(Repository *self, PyObject *args) len = py_str_to_git_oid(py_oid, &oid); if (len == 0) - goto out; + return NULL; err = git_object_lookup_prefix(&target, self->repo, &oid, (unsigned int)len, target_type); - if (err < 0) { - git_oid_fmt(hex, &oid); - hex[len] = '\0'; - Error_set_str(err, hex); - goto out; - } + if (err < 0) + return Error_set_oid(err, &oid, len); err = git_tag_create(&oid, self->repo, tag_name, target, py_tagger->signature, message, 0); if (err == 0) py_result = git_oid_to_python(oid.id); -out: git_object_free(target); return py_result; } @@ -2456,7 +2418,7 @@ Reference_get_target(Reference *self) /* 1- Get the target */ c_name = git_reference_target(self->reference); if (c_name == NULL) { - PyErr_Format(PyExc_ValueError, "Not target available"); + PyErr_SetString(PyExc_ValueError, "Not target available"); return NULL; } @@ -2499,11 +2461,10 @@ Reference_get_oid(Reference *self) /* 1- Get the oid (only for "direct" references) */ oid = git_reference_oid(self->reference); - if (oid == NULL) - { - PyErr_Format(PyExc_ValueError, - "oid is only available if the reference is direct " - "(i.e. not symbolic)"); + if (oid == NULL) { + PyErr_SetString(PyExc_ValueError, + "oid is only available if the reference is direct " + "(i.e. not symbolic)"); return NULL; } @@ -2542,11 +2503,10 @@ Reference_get_hex(Reference *self) /* 1- Get the oid (only for "direct" references) */ oid = git_reference_oid(self->reference); - if (oid == NULL) - { - PyErr_Format(PyExc_ValueError, - "oid is only available if the reference is direct " - "(i.e. not symbolic)"); + if (oid == NULL) { + PyErr_SetString(PyExc_ValueError, + "oid is only available if the reference is direct " + "(i.e. not symbolic)"); return NULL; } @@ -2794,10 +2754,8 @@ init_repository(PyObject *self, PyObject *args) return NULL; err = git_repository_init(&repo, path, bare); - if (err < 0) { - Error_set_str(err, path); - return NULL; - } + if (err < 0) + return Error_set_str(err, path); py_repo = PyObject_GC_New(Repository, &RepositoryType); if (py_repo) { diff --git a/test/test_repository.py b/test/test_repository.py index 66d1a5a41..d07babca1 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -34,8 +34,7 @@ import os from os.path import join, realpath -from pygit2 import (GitError, GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, - init_repository) +from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, init_repository from . import utils @@ -66,7 +65,7 @@ def test_read(self): def test_write(self): data = b"hello world" # invalid object type - self.assertRaises(GitError, self.repo.write, GIT_OBJ_ANY, data) + self.assertRaises(ValueError, self.repo.write, GIT_OBJ_ANY, data) oid = self.repo.write(GIT_OBJ_BLOB, data) self.assertEqual(type(oid), bytes) From 59866881977802c43981b2ca9bbb26945373d02b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 19 Dec 2011 23:40:27 +0100 Subject: [PATCH 0112/2237] refs: fix segfault when using a deleted reference Before this patch the code below produced a segfault: >>> reference.delete() >>> reference.name Now an exception is raised. --- TODO.txt | 6 +++ pygit2.c | 98 ++++++++++++++++++++++++++++------------------- test/test_refs.py | 14 ++++++- 3 files changed, 77 insertions(+), 41 deletions(-) diff --git a/TODO.txt b/TODO.txt index d878858d5..7b33ba41f 100644 --- a/TODO.txt +++ b/TODO.txt @@ -3,6 +3,12 @@ Signature - Implement equality interface - In Repository's create_commit/create_tag check signatures encoding is right +References +========== +- Free the git_reference struct. +- Wrap missing functions: git_reference_foreach, git_reference_is_packed, + git_reference_reload + Other ========= - Make the Py_LOCAL_INLINE macro to work with Python 2.6, 2.7 and 3.1 diff --git a/pygit2.c b/pygit2.c index a5c537f17..c9b41d047 100644 --- a/pygit2.c +++ b/pygit2.c @@ -67,6 +67,17 @@ to_bytes(const char * value) #define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict") #endif +#define CHECK_REFERENCE(self)\ + if (self->reference == NULL) {\ + PyErr_SetString(GitError, "deleted reference");\ + return NULL;\ + } + +#define CHECK_REFERENCE_INT(self)\ + if (self->reference == NULL) {\ + PyErr_SetString(GitError, "deleted reference");\ + return -1;\ + } /* Python objects */ typedef struct { @@ -773,26 +784,21 @@ Repository_listall_references(Repository *self, PyObject *args) /* 3- Create a new PyTuple */ py_result = PyTuple_New(c_result.count); - if (py_result == NULL) { - git_strarray_free(&c_result); - return NULL; - } + if (py_result == NULL) + goto out; /* 4- Fill it */ for (index=0; index < c_result.count; index++) { py_string = to_path((c_result.strings)[index]); if (py_string == NULL) { - Py_XDECREF(py_result); - git_strarray_free(&c_result); - return NULL; + Py_CLEAR(py_result); + goto out; } PyTuple_SET_ITEM(py_result, index, py_string); } - /* 5- Destroy the c_result */ +out: git_strarray_free(&c_result); - - /* 6- And return the py_result */ return py_result; } @@ -811,7 +817,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) /* 2- Lookup */ err = git_reference_lookup(&c_reference, self->repo, c_name); if (err < 0) - return Error_set(err); + return Error_set_str(err, c_name); /* 3- Make an instance of Reference and return it */ return wrap_reference(c_reference); @@ -860,7 +866,7 @@ Repository_create_symbolic_reference(Repository *self, PyObject *args) err = git_reference_create_symbolic(&c_reference, self->repo, c_name, c_target, 0); if (err < 0) - return Error_set(err); + return Error_set(err); /* 3- Make an instance of Reference and return it */ return wrap_reference(c_reference); @@ -2363,16 +2369,15 @@ Reference_delete(Reference *self, PyObject *args) { int err; - /* 1- Delete the reference */ + CHECK_REFERENCE(self); + + /* Delete the reference */ err = git_reference_delete(self->reference); if (err < 0) - return Error_set(err); - - /* 2- Invalidate the pointer */ - self->reference = NULL; + return Error_set(err); - /* 3- Return None */ - Py_RETURN_NONE; + self->reference = NULL; /* Invalidate the pointer */ + Py_RETURN_NONE; /* Return None */ } static PyObject * @@ -2381,18 +2386,19 @@ Reference_rename(Reference *self, PyObject *py_name) char *c_name; int err; - /* 1- Get the C name */ + CHECK_REFERENCE(self); + + /* Get the C name */ c_name = py_path_to_c_str(py_name); if (c_name == NULL) return NULL; - /* 2- Rename */ + /* Rename */ err = git_reference_rename(self->reference, c_name, 0); if (err < 0) - return Error_set(err); + return Error_set(err); - /* 3- Return None */ - Py_RETURN_NONE; + Py_RETURN_NONE; /* Return None */ } static PyObject * @@ -2401,12 +2407,14 @@ Reference_resolve(Reference *self, PyObject *args) git_reference *c_reference; int err; - /* 1- Resolve */ + CHECK_REFERENCE(self); + + /* Resolve */ err = git_reference_resolve(&c_reference, self->reference); if (err < 0) - return Error_set(err); + return Error_set(err); - /* 2- Make an instance of Reference and return it */ + /* Make an instance of Reference and return it */ return wrap_reference(c_reference); } @@ -2415,14 +2423,16 @@ Reference_get_target(Reference *self) { const char * c_name; - /* 1- Get the target */ + CHECK_REFERENCE(self); + + /* Get the target */ c_name = git_reference_target(self->reference); if (c_name == NULL) { - PyErr_SetString(PyExc_ValueError, "Not target available"); + PyErr_SetString(PyExc_ValueError, "no target available"); return NULL; } - /* 2- Make a PyString and return it */ + /* Make a PyString and return it */ return to_path(c_name); } @@ -2432,25 +2442,27 @@ Reference_set_target(Reference *self, PyObject *py_name) char *c_name; int err; - /* 1- Get the C name */ + CHECK_REFERENCE_INT(self); + + /* Get the C name */ c_name = py_path_to_c_str(py_name); if (c_name == NULL) return -1; - /* 2- Set the new target */ + /* Set the new target */ err = git_reference_set_target(self->reference, c_name); if (err < 0) { Error_set(err); return -1; } - /* 3- All OK */ return 0; } static PyObject * Reference_get_name(Reference *self) { + CHECK_REFERENCE(self); return to_path(git_reference_name(self->reference)); } @@ -2459,7 +2471,9 @@ Reference_get_oid(Reference *self) { const git_oid *oid; - /* 1- Get the oid (only for "direct" references) */ + CHECK_REFERENCE(self); + + /* Get the oid (only for "direct" references) */ oid = git_reference_oid(self->reference); if (oid == NULL) { PyErr_SetString(PyExc_ValueError, @@ -2468,7 +2482,7 @@ Reference_get_oid(Reference *self) return NULL; } - /* 2- Convert and return it */ + /* Convert and return it */ return git_oid_to_python(oid->id); } @@ -2479,20 +2493,21 @@ Reference_set_oid(Reference *self, PyObject *py_hex) int err; size_t len; - /* 1- Get the oid */ + CHECK_REFERENCE_INT(self); + + /* Get the oid */ len = py_str_to_git_oid(py_hex, &oid); TODO_SUPPORT_SHORT_HEXS(len) if (len == 0) return -1; - /* 2- Set the oid */ + /* Set the oid */ err = git_reference_set_oid(self->reference, &oid); if (err < 0) { Error_set(err); return -1; } - /* 3- All OK */ return 0; } @@ -2501,7 +2516,9 @@ Reference_get_hex(Reference *self) { const git_oid *oid; - /* 1- Get the oid (only for "direct" references) */ + CHECK_REFERENCE(self); + + /* Get the oid (only for "direct" references) */ oid = git_reference_oid(self->reference); if (oid == NULL) { PyErr_SetString(PyExc_ValueError, @@ -2510,7 +2527,7 @@ Reference_get_hex(Reference *self) return NULL; } - /* 2- Convert and return it */ + /* Convert and return it */ return git_oid_to_py_str(oid); } @@ -2519,6 +2536,7 @@ Reference_get_type(Reference *self) { git_rtype c_type; + CHECK_REFERENCE(self); c_type = git_reference_type(self->reference); return PyInt_FromLong(c_type); } diff --git a/test/test_refs.py b/test/test_refs.py index e457d8266..de4c05460 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -31,7 +31,7 @@ from __future__ import unicode_literals import unittest -from pygit2 import GIT_REF_OID, GIT_REF_SYMBOLIC +from pygit2 import GitError, GIT_REF_OID, GIT_REF_SYMBOLIC from . import utils @@ -113,6 +113,18 @@ def test_delete(self): reference.delete() self.assertFalse('refs/tags/version1' in repo.listall_references()) + # Access the deleted reference + self.assertRaises(GitError, getattr, reference, 'name') + self.assertRaises(GitError, getattr, reference, 'type') + self.assertRaises(GitError, getattr, reference, 'oid') + self.assertRaises(GitError, setattr, reference, 'oid', LAST_COMMIT) + self.assertRaises(GitError, getattr, reference, 'hex') + self.assertRaises(GitError, getattr, reference, 'target') + self.assertRaises(GitError, setattr, reference, 'target', "a/b/c") + self.assertRaises(GitError, reference.delete) + self.assertRaises(GitError, reference.resolve) + self.assertRaises(GitError, reference.rename, "refs/tags/version2") + def test_rename(self): # We add a tag as a new reference that points to "origin/master" From 584a8b147389c4922387dd05ca81218e29e06a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 20 Dec 2011 22:19:15 +0100 Subject: [PATCH 0113/2237] refs: implement reference dealloc --- TODO.txt | 1 - pygit2.c | 11 +++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/TODO.txt b/TODO.txt index 7b33ba41f..cc74b66dc 100644 --- a/TODO.txt +++ b/TODO.txt @@ -5,7 +5,6 @@ Signature References ========== -- Free the git_reference struct. - Wrap missing functions: git_reference_foreach, git_reference_is_packed, git_reference_reload diff --git a/pygit2.c b/pygit2.c index c9b41d047..ae1fcb1cf 100644 --- a/pygit2.c +++ b/pygit2.c @@ -845,7 +845,7 @@ Repository_create_reference(Repository *self, PyObject *args) /* 2- Create the reference */ err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid, 0); if (err < 0) - return Error_set(err); + return Error_set(err); /* 3- Make an instance of Reference and return it */ return wrap_reference(c_reference); @@ -2364,6 +2364,13 @@ static PyTypeObject WalkerType = { 0, /* tp_new */ }; +static void +Reference_dealloc(Reference *self) +{ + git_reference_free(self->reference); + PyObject_Del(self); +} + static PyObject * Reference_delete(Reference *self, PyObject *args) { @@ -2569,7 +2576,7 @@ static PyTypeObject ReferenceType = { "pygit2.Reference", /* tp_name */ sizeof(Reference), /* tp_basicsize */ 0, /* tp_itemsize */ - 0, /* tp_dealloc */ + (destructor)Reference_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ From 73a4a3a649661ed63b4eec9f1cd4796788cb03d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 24 Dec 2011 17:04:24 +0100 Subject: [PATCH 0114/2237] refs: resolving a direct ref returns the same object When resolving a direct reference the same Python object is returned, with the refcount incremented by one. The reference is reloaded. --- TODO.txt | 2 ++ pygit2.c | 14 ++++++++++++-- test/test_refs.py | 6 ++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/TODO.txt b/TODO.txt index cc74b66dc..d4b8ab2de 100644 --- a/TODO.txt +++ b/TODO.txt @@ -7,6 +7,8 @@ References ========== - Wrap missing functions: git_reference_foreach, git_reference_is_packed, git_reference_reload +- Write more twisted tests. Like accessing a reference deleted by someone + else. Other ========= diff --git a/pygit2.c b/pygit2.c index ae1fcb1cf..e3c332784 100644 --- a/pygit2.c +++ b/pygit2.c @@ -2416,12 +2416,22 @@ Reference_resolve(Reference *self, PyObject *args) CHECK_REFERENCE(self); - /* Resolve */ + /* Direct: reload */ + if (git_reference_type(self->reference) == GIT_REF_OID) { + err = git_reference_reload(self->reference); + if (err < 0) { + self->reference = NULL; + return Error_set(err); + } + Py_INCREF(self); + return (PyObject *)self; + } + + /* Symbolic: resolve */ err = git_reference_resolve(&c_reference, self->reference); if (err < 0) return Error_set(err); - /* Make an instance of Reference and return it */ return wrap_reference(c_reference); } diff --git a/test/test_refs.py b/test/test_refs.py index de4c05460..46a6ac9ef 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -143,6 +143,12 @@ def test_reference_resolve(self): self.assertEqual(reference.hex, LAST_COMMIT) + def test_reference_resolve_identity(self): + head = self.repo.lookup_reference('HEAD') + ref = head.resolve() + self.assertTrue(ref.resolve() is ref) + + def test_create_reference(self): # We add a tag as a new reference that points to "origin/master" reference = self.repo.create_reference('refs/tags/version1', From 4a33c7f8069db227c37b0431be36fdfc271d500f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 25 Dec 2011 10:46:38 +0100 Subject: [PATCH 0115/2237] refs: implement reference reload --- TODO.txt | 3 +-- pygit2.c | 19 +++++++++++++++++++ test/test_refs.py | 11 +++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/TODO.txt b/TODO.txt index d4b8ab2de..d9e656e16 100644 --- a/TODO.txt +++ b/TODO.txt @@ -5,8 +5,7 @@ Signature References ========== -- Wrap missing functions: git_reference_foreach, git_reference_is_packed, - git_reference_reload +- Wrap missing functions: git_reference_foreach, git_reference_is_packed. - Write more twisted tests. Like accessing a reference deleted by someone else. diff --git a/pygit2.c b/pygit2.c index e3c332784..a4cd80d69 100644 --- a/pygit2.c +++ b/pygit2.c @@ -2408,6 +2408,23 @@ Reference_rename(Reference *self, PyObject *py_name) Py_RETURN_NONE; /* Return None */ } +static PyObject * +Reference_reload(Reference *self) +{ + int err; + + CHECK_REFERENCE(self); + + err = git_reference_reload(self->reference); + if (err < 0) { + self->reference = NULL; + return Error_set(err); + } + + Py_RETURN_NONE; +} + + static PyObject * Reference_resolve(Reference *self, PyObject *args) { @@ -2563,6 +2580,8 @@ static PyMethodDef Reference_methods[] = { "Delete this reference. It will no longer be valid!"}, {"rename", (PyCFunction)Reference_rename, METH_O, "Rename the reference."}, + {"reload", (PyCFunction)Reference_reload, METH_NOARGS, + "Reload the reference from the file-system."}, {"resolve", (PyCFunction)Reference_resolve, METH_NOARGS, "Resolve a symbolic reference and return a direct reference."}, {NULL} diff --git a/test/test_refs.py b/test/test_refs.py index 46a6ac9ef..c96aa9bb2 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -135,6 +135,17 @@ def test_rename(self): self.assertEqual(reference.name, 'refs/tags/version2') + def test_reload(self): + name = 'refs/tags/version1' + + ref = self.repo.create_symbolic_reference(name, "refs/heads/master") + ref2 = self.repo.lookup_reference(name) + ref.delete() + self.assertEqual(ref2.name, name) + self.assertRaises(KeyError, ref2.reload) + self.assertRaises(GitError, getattr, ref2, 'name') + + def test_reference_resolve(self): reference = self.repo.lookup_reference('HEAD') self.assertEqual(reference.type, GIT_REF_SYMBOLIC) From 35ed84cf444f644dc777d218fe416c06576c4de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 2 Jan 2012 19:42:49 +0100 Subject: [PATCH 0116/2237] Update to latest changes in libgit2 Now Repository.index also works on bare repositories. Unit tests pass again. And fix a memory leak (add a call to git_index_free). --- pygit2.c | 28 ++++++++++++---------------- test/test_index.py | 3 ++- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/pygit2.c b/pygit2.c index a4cd80d69..270a966f4 100644 --- a/pygit2.c +++ b/pygit2.c @@ -548,24 +548,20 @@ Repository_get_index(Repository *self, void *closure) if (self->index == NULL) { err = git_repository_index(&index, self->repo); - if (err == GIT_SUCCESS) { - py_index = PyObject_GC_New(Index, &IndexType); - if (!py_index) - return NULL; - - Py_INCREF(self); - py_index->repo = self; - py_index->index = index; - PyObject_GC_Track(py_index); - self->index = (PyObject*)py_index; - } - else if (err == GIT_EBAREINDEX) { - Py_INCREF(Py_None); - self->index = Py_None; - } - else { + if (err < 0) return Error_set(err); + + py_index = PyObject_GC_New(Index, &IndexType); + if (!py_index) { + git_index_free(index); + return NULL; } + + Py_INCREF(self); + py_index->repo = self; + py_index->index = index; + PyObject_GC_Track(py_index); + self->index = (PyObject*)py_index; } Py_INCREF(self->index); diff --git a/test/test_index.py b/test/test_index.py index 5df397b1e..ae3768f4f 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -43,7 +43,8 @@ class IndexBareTest(utils.BareRepoTestCase): def test_bare(self): - self.assertEqual(None, self.repo.index) + index = self.repo.index + self.assertEqual(len(index), 0) class IndexTest(utils.RepoTestCase): From 550c966111cc490210342304eedb32290a7c5e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 2 Jan 2012 20:10:28 +0100 Subject: [PATCH 0117/2237] Rename Index.create_tree to Index.write_tree Like Git command "git write-tree" --- pygit2.c | 6 +++--- test/test_index.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pygit2.c b/pygit2.c index 270a966f4..7a79b3db2 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1986,7 +1986,7 @@ Index_setitem(Index *self, PyObject *key, PyObject *value) } static PyObject * -Index_create_tree(Index *self) +Index_write_tree(Index *self) { git_oid oid; int err; @@ -2012,8 +2012,8 @@ static PyMethodDef Index_methods[] = { {"write", (PyCFunction)Index_write, METH_NOARGS, "Write an existing index object from memory back to disk using an" " atomic file lock."}, - {"create_tree", (PyCFunction)Index_create_tree, METH_NOARGS, - "Create a tree from the index file, return its SHA."}, + {"write_tree", (PyCFunction)Index_write_tree, METH_NOARGS, + "Create a tree object from the index file, return its oid."}, {NULL} }; diff --git a/test/test_index.py b/test/test_index.py index ae3768f4f..7c566a36b 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -92,8 +92,8 @@ def test_write(self): index.read() self.assertTrue('bye.txt' in index) - def test_create_tree(self): - oid = self.repo.index.create_tree() + def test_write_tree(self): + oid = self.repo.index.write_tree() sha = b2a_hex(oid).decode('ascii') self.assertEqual(sha, 'fd937514cb799514d4b81bb24c5fcfeb6472b245') From 9d8d2ace6430298db91531dee825315f43232feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 2 Jan 2012 23:11:30 +0100 Subject: [PATCH 0118/2237] index: implement Index.read_tree --- pygit2.c | 27 +++++++++++++++++++++++++++ test/test_index.py | 17 +++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/pygit2.c b/pygit2.c index 7a79b3db2..d2c972881 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1985,6 +1985,31 @@ Index_setitem(Index *self, PyObject *key, PyObject *value) return 0; } +static PyObject * +Index_read_tree(Index *self, PyObject *value) +{ + git_oid oid; + git_tree *tree; + size_t len; + int err; + + len = py_str_to_git_oid(value, &oid); + TODO_SUPPORT_SHORT_HEXS(len) + if (len == 0) + return NULL; + + err = git_tree_lookup_prefix(&tree, self->repo->repo, &oid, + (unsigned int)len); + if (err < 0) + return Error_set(err); + + err = git_index_read_tree(self->index, tree); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + static PyObject * Index_write_tree(Index *self) { @@ -2012,6 +2037,8 @@ static PyMethodDef Index_methods[] = { {"write", (PyCFunction)Index_write, METH_NOARGS, "Write an existing index object from memory back to disk using an" " atomic file lock."}, + {"read_tree", (PyCFunction)Index_read_tree, METH_O, + "Update the index file from the given tree object."}, {"write_tree", (PyCFunction)Index_write_tree, METH_NOARGS, "Create a tree object from the index file, return its oid."}, {NULL} diff --git a/test/test_index.py b/test/test_index.py index 7c566a36b..a8c47770a 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -92,6 +92,23 @@ def test_write(self): index.read() self.assertTrue('bye.txt' in index) + + def test_read_tree(self): + tree_oid = '68aba62e560c0ebc3396e8ae9335232cd93a3f60' + # Test reading first tree + index = self.repo.index + self.assertEqual(len(index), 2) + index.read_tree(tree_oid) + self.assertEqual(len(index), 1) + # Test read-write returns the same oid + oid = index.write_tree() + oid = b2a_hex(oid).decode('ascii') + self.assertEqual(oid, tree_oid) + # Test the index is only modified in memory + index.read() + self.assertEqual(len(index), 2) + + def test_write_tree(self): oid = self.repo.index.write_tree() sha = b2a_hex(oid).decode('ascii') From 39bb5de7c560448c2ed9b70f54b56cf01597a4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 25 Jan 2012 19:04:16 +0100 Subject: [PATCH 0119/2237] Fixing compilation with MSVC (issue #53) --- pygit2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index d2c972881..a1fab76bc 100644 --- a/pygit2.c +++ b/pygit2.c @@ -484,6 +484,7 @@ Repository_read(Repository *self, PyObject *py_hex) int err; git_odb_object *obj; size_t len; + PyObject* tuple; len = py_str_to_git_oid(py_hex, &oid); if (len == 0) @@ -493,7 +494,7 @@ Repository_read(Repository *self, PyObject *py_hex) if (obj == NULL) return NULL; - PyObject* tuple = Py_BuildValue( + tuple = Py_BuildValue( "(ns#)", git_odb_object_type(obj), git_odb_object_data(obj), From bc7db4ba8a766085e1c71224f9d2ee69aa45d395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 3 Feb 2012 18:31:19 +0100 Subject: [PATCH 0120/2237] errors: don't segfault if the library didn't set an error git_lasterror() can return NULL. Passing that to the error formatting functions leads to a segmentation fault. Add a wrapper with a generic "no more information" string for the cases when the library didn't set one. --- pygit2.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/pygit2.c b/pygit2.c index a1fab76bc..0b74ff585 100644 --- a/pygit2.c +++ b/pygit2.c @@ -174,6 +174,20 @@ Error_type(int err) } } +/* + * Python doesn't like it when the error string is NULL. Not giving + * back an error string could be a bug in the library + */ +static const char * +git_last_error(void) +{ + const char *ret; + + ret = git_lasterror(); + + return ret != NULL ? ret : "(No error information given)"; +} + static PyObject * Error_set(int err) { @@ -182,7 +196,7 @@ Error_set(int err) if (err == GIT_EOSERR) return PyErr_SetFromErrno(GitError); - PyErr_SetString(Error_type(err), git_lasterror()); + PyErr_SetString(Error_type(err), git_last_error()); return NULL; } @@ -195,7 +209,7 @@ Error_set_str(int err, const char *str) return NULL; } - return PyErr_Format(Error_type(err), "%s: %s", str, git_lasterror()); + return PyErr_Format(Error_type(err), "%s: %s", str, git_last_error()); } static PyObject * From 42006d715345839a7cdbeb07e20dfe26b4d84269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 6 Feb 2012 23:05:19 +0100 Subject: [PATCH 0121/2237] docs: update talk about signatures --- README.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cc6c850f5..469e59965 100644 --- a/README.md +++ b/README.md @@ -106,12 +106,21 @@ Commits Commit.tree -- the tree object attached to the commit Commit.parents -- the list of parent commits -The author and committer attributes of commit objects are tuples with four -elements, the author name and email, the unix time and time offset in -minutes: +### Signatures + +The author and committer attributes of commit objects are ``Signature`` +objects: >>> commit.author - ('J. David Ibáñez', 'jdavid.ibp@gmail.com', 1315005198, 120) + + +This is their interface: + + Signature.name -- person's name + Signature.email -- person's email address + Signature.time -- unix time + Signature.offset -- offset from utc in minutes + Trees ----------------- From 0fa8503f919e3a619ec13ba4aa33e1eb4cb1e4de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 6 Feb 2012 23:30:00 +0100 Subject: [PATCH 0122/2237] Release v0.16.0 Changes since v0.15.1: - Update to libgit2 v0.16.0 - Improve support for references - New method Index.read_tree - Rename Index.create_tree to Index.write_tree - Fix compilation error with MSVC (issue #53) - Fix a few segfaults (including issue #55) --- setup.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 2f03ae906..1640c9377 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,6 @@ from distutils.core import setup, Extension, Command SETUPTOOLS = False -import sys # Replace with your libgit2 configuration. include_dirs = ['/usr/local/include'] @@ -67,11 +66,18 @@ def run(self): kwargs = {'cmdclass': {'test': TestCommand}} +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Topic :: Software Development :: Version Control"] + + setup(name='pygit2', description='Python bindings for libgit2.', keywords='git', - version='0.15.0', + version='0.16.0', url='http://github.com/libgit2/pygit2', + classifiers=classifiers, license='GPLv2', maintainer='J. David Ibáñez', maintainer_email='jdavid.ibp@gmail.com', From a11f3c52a9770112fc0ff64cf4fc101c66803641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 8 Feb 2012 23:21:37 +0100 Subject: [PATCH 0123/2237] docs: use readme file for both github and pypi The readme file is now written in reStructuredText (instead of markdown). This is because reST is the only thing pypi understands. So now the same readme file will be used for pygit2's homepage at github and pypi: - https://github.com/libgit2/pygit2 - http://pypi.python.org/pypi/pygit2 --- README.md => README.rst | 43 ++++++++++++++++++++++------------------- setup.py | 11 +++++------ 2 files changed, 28 insertions(+), 26 deletions(-) rename README.md => README.rst (90%) diff --git a/README.md b/README.rst similarity index 90% rename from README.md rename to README.rst index 469e59965..b55492712 100644 --- a/README.md +++ b/README.rst @@ -14,16 +14,16 @@ INSTALLING AND RUNNING First you need to install the latest version of libgit2. You can find platform-specific instructions to build the library in the libgit2 website: - + http://libgit2.github.com Next, make sure you have the required library dependencies for pygit2: OpenSSL and ZLib. -For instance, in Debian-based systems run: +For instance, in Debian-based systems run:: $ sudo apt-get install zlib1g-dev libssl-dev Also, make sure you have Python 2.6+ installed together with the Python development headers. -When those are installed, you can install pygit2: +When those are installed, you can install pygit2:: $ git clone git://github.com/libgit2/pygit2.git $ cd pygit2 @@ -34,18 +34,18 @@ When those are installed, you can install pygit2: The repository ================= -Everything starts by opening an existing repository: +Everything starts by opening an existing repository:: >>> from pygit2 import Repository >>> repo = Repository('pygit2/.git') -Or by creating a new one: +Or by creating a new one:: >>> from pygit2 import init_repository >>> bare = False >>> repo = init_repository('test', bare) -These are the basic attributes of a repository: +These are the basic attributes of a repository:: Repository.path -- path to the Git repository Repository.workdir -- path to the working directory, None in the case of @@ -57,7 +57,7 @@ Git objects In the first place Git is a key-value storage system. The values stored are called *objects*, there are four types (commits, trees, blobs and tags), -for each type pygit2 has a Python class: +for each type pygit2 has a Python class:: # Get the last commit >>> head = repo.lookup_reference('HEAD') @@ -73,7 +73,7 @@ for each type pygit2 has a Python class: These four classes (``Commit``, ``Tree``, ``Blob`` and ``Tag``) inherit from the ``Object`` base class, which provides shared behaviour. A Git object is identified by a unique *object id*, which is a binary byte string; this is -often represented as an hexadecimal text string: +often represented as an hexadecimal text string:: >>> commit.oid b'x\xde\xb5W\x8d\x01<\xdb\xdf\x08o\xa1\xd1\xa3\xe7\xd9\x82\xe8\x88\x8f' @@ -84,7 +84,7 @@ The API of pygit2 accepts both the raw object id and its hexadecimal representation, the difference is done based on its type (a byte or a text string). -This is the common interface for all Git objects: +This is the common interface for all Git objects:: Object.type -- one of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_TAG constants @@ -106,15 +106,16 @@ Commits Commit.tree -- the tree object attached to the commit Commit.parents -- the list of parent commits -### Signatures +Signatures +............. The author and committer attributes of commit objects are ``Signature`` -objects: +objects:: >>> commit.author -This is their interface: +This is their interface:: Signature.name -- person's name Signature.email -- person's email address @@ -128,7 +129,7 @@ Trees A tree is a sorted collection of tree entries. It is similar to a folder or directory in a file system. Each entry points to another tree or a blob. A tree can be iterated, and partially implements the sequence and mapping -interfaces: +interfaces:: # Number of entries >>> tree = commit.tree @@ -156,7 +157,7 @@ interfaces: >>> blob -This is the interface of a tree entry: +This is the interface of a tree entry:: TreeEntry.name -- name of the tree entry TreeEntry.oid -- the id of the git object @@ -167,7 +168,7 @@ This is the interface of a tree entry: Blobs ----------------- -A blob is equivalent to a file in a file system. +A blob is equivalent to a file in a file system:: Blob.data -- the contents of the blob, a byte string @@ -180,7 +181,7 @@ XXX References ================= -Reference lookup: +Reference lookup:: >>> master_ref = repo.lookup_reference("refs/heads/master") >>> commit = repo[master_ref.oid] @@ -189,6 +190,8 @@ Reference lookup: Revision walking ================= +:: + >>> from pygit2 import GIT_SORT_TIME >>> for commit in repo.walk(oid, GIT_SORT_TIME): ... print commit.hex @@ -196,19 +199,19 @@ Revision walking The index file ================= -Index read: +Index read:: >>> index = repo.index >>> index.read() >>> oid = index['path/to/file'].oid # from path to object id >>> blob = repo[oid] # from object id to object -Iterate over all entries of the index: +Iterate over all entries of the index:: >>> for entry in index: ... print entry.path, entry.hex -Index write: +Index write:: >>> index.add('path/to/file') # git add >>> del index['path/to/file'] # git rm @@ -217,7 +220,7 @@ Index write: Status ================= -Inspect the status of the repository: +Inspect the status of the repository:: >>> from pygit2 import GIT_STATUS_CURRENT >>> status = repo.status() diff --git a/setup.py b/setup.py index 1640c9377..3d72293bb 100644 --- a/setup.py +++ b/setup.py @@ -72,6 +72,9 @@ def run(self): "Topic :: Software Development :: Version Control"] +with open('README.rst') as readme: + long_description = readme.read() + setup(name='pygit2', description='Python bindings for libgit2.', keywords='git', @@ -81,15 +84,11 @@ def run(self): license='GPLv2', maintainer='J. David Ibáñez', maintainer_email='jdavid.ibp@gmail.com', - long_description=""" - Bindings for libgit2, a linkable C library for the Git version-control - system. - """, + long_description=long_description, ext_modules = [ Extension('pygit2', ['pygit2.c'], include_dirs=include_dirs, library_dirs=library_dirs, libraries=libraries), ], - **kwargs - ) + **kwargs) From f11a05b610d5bd896f54ed9fdaf6e9fc4c083aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 8 Feb 2012 23:29:27 +0100 Subject: [PATCH 0124/2237] docs: fix for the previous commit --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index b55492712..9e72c5e03 100644 --- a/README.rst +++ b/README.rst @@ -100,6 +100,8 @@ Objects can not be modified once they have been created. Commits ----------------- +:: + Commit.author -- the author of the commit Commit.committer -- the committer of the commit Commit.message -- the message, a text string From 3bf5b4c79459f64fcad9d82a75aa61d1ba4a8ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 10 Feb 2012 19:38:25 +0100 Subject: [PATCH 0125/2237] status: new method 'Repository.status_file' Exposes libgit2 function 'git_status_file'. --- pygit2.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/pygit2.c b/pygit2.c index 0b74ff585..ea06c4c2b 100644 --- a/pygit2.c +++ b/pygit2.c @@ -921,6 +921,23 @@ Repository_status(Repository *self, PyObject *args) return payload_dict; } +static PyObject * +Repository_status_file(Repository *self, PyObject *value) +{ + char *path; + int status, err; + + path = py_path_to_c_str(value); + if (!path) + return NULL; + + err = git_status_file(&status, self->repo, path); + if (err < 0) + return Error_set_str(err, path); + + return PyInt_FromLong(status); +} + static PyMethodDef Repository_methods[] = { {"create_commit", (PyCFunction)Repository_create_commit, METH_VARARGS, "Create a new commit object, return its SHA."}, @@ -952,6 +969,8 @@ static PyMethodDef Repository_methods[] = { {"status", (PyCFunction)Repository_status, METH_NOARGS, "Reads the " "status of the repository and returns a dictionnary with file paths " "as keys and status flags as values.\nSee pygit2.GIT_STATUS_*."}, + {"status_file", (PyCFunction)Repository_status_file, METH_O, + "Returns the status of the given file path."}, {NULL} }; @@ -2964,17 +2983,13 @@ moduleinit(PyObject* m) PyModule_AddIntConstant(m, "GIT_REF_SYMBOLIC", GIT_REF_SYMBOLIC); PyModule_AddIntConstant(m, "GIT_REF_PACKED", GIT_REF_PACKED); - /** Git status flags **/ + /* Git status flags */ PyModule_AddIntConstant(m, "GIT_STATUS_CURRENT", GIT_STATUS_CURRENT); - - /* Flags for index status */ PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_NEW", GIT_STATUS_INDEX_NEW); PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_MODIFIED", GIT_STATUS_INDEX_MODIFIED); PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_DELETED" , GIT_STATUS_INDEX_DELETED); - - /* Flags for worktree status */ PyModule_AddIntConstant(m, "GIT_STATUS_WT_NEW", GIT_STATUS_WT_NEW); PyModule_AddIntConstant(m, "GIT_STATUS_WT_MODIFIED" , GIT_STATUS_WT_MODIFIED); From 2b4e5504af235b2c3d4f8a23f1b7a909a0e4b449 Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Thu, 16 Feb 2012 01:00:49 +0800 Subject: [PATCH 0126/2237] Get pygit2 can be build under Windows. --- setup.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/setup.py b/setup.py index 3d72293bb..287da932d 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,7 @@ """Setup file for pygit2.""" +import os try: from setuptools import setup, Extension, Command SETUPTOOLS = True @@ -35,12 +36,23 @@ from distutils.core import setup, Extension, Command SETUPTOOLS = False - -# Replace with your libgit2 configuration. -include_dirs = ['/usr/local/include'] -library_dirs = ['/usr/local/lib'] +# Use environment variable LIBGIT2 to set your own libgit2 configuration. libraries = ['git2', 'z', 'crypto'] - +if os.name == 'nt': + libraries = ['git2'] + +libgit2_path = os.getenv("LIBGIT2") +if libgit2_path is None: + if os.name == 'nt': + program_files = os.getenv("ProgramFiles") + print(program_files) + libgit2_path = '%s\libgit2' % program_files + else: + libgit2_path = '/usr/local' + +libgit2_bin = os.path.join(libgit2_path, 'bin') +libgit2_include = os.path.join(libgit2_path, 'include') +libgit2_lib = os.path.join(libgit2_path, 'lib') class TestCommand(Command): """Command for running pygit2 tests.""" @@ -87,8 +99,8 @@ def run(self): long_description=long_description, ext_modules = [ Extension('pygit2', ['pygit2.c'], - include_dirs=include_dirs, - library_dirs=library_dirs, + include_dirs=[libgit2_include], + library_dirs=[libgit2_lib], libraries=libraries), ], **kwargs) From 8394dfb81433c0ea9d125e70147c5ae30d7a38ec Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Thu, 16 Feb 2012 01:02:55 +0800 Subject: [PATCH 0127/2237] Remove extra print. --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 287da932d..6a5ecee67 100644 --- a/setup.py +++ b/setup.py @@ -45,7 +45,6 @@ if libgit2_path is None: if os.name == 'nt': program_files = os.getenv("ProgramFiles") - print(program_files) libgit2_path = '%s\libgit2' % program_files else: libgit2_path = '/usr/local' From 5ece88300a75a5f3c308b9be959e2a255b946b6b Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Sat, 18 Feb 2012 16:28:35 +0800 Subject: [PATCH 0128/2237] Repository_create_tag return error when necessary. --- pygit2.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/pygit2.c b/pygit2.c index ea06c4c2b..153739ed5 100644 --- a/pygit2.c +++ b/pygit2.c @@ -741,7 +741,7 @@ Repository_create_commit(Repository *self, PyObject *args) static PyObject * Repository_create_tag(Repository *self, PyObject *args) { - PyObject *py_oid, *py_result = NULL; + PyObject *py_oid; Signature *py_tagger; char *tag_name, *message; git_oid oid; @@ -763,16 +763,12 @@ Repository_create_tag(Repository *self, PyObject *args) err = git_object_lookup_prefix(&target, self->repo, &oid, (unsigned int)len, target_type); - if (err < 0) - return Error_set_oid(err, &oid, len); - - err = git_tag_create(&oid, self->repo, tag_name, target, + err = err < 0 ? err : git_tag_create(&oid, self->repo, tag_name, target, py_tagger->signature, message, 0); - if (err == 0) - py_result = git_oid_to_python(oid.id); - git_object_free(target); - return py_result; + if (err < 0) + return Error_set_oid(err, &oid, len); + return git_oid_to_python(oid.id); } static PyObject * From 5da739270af1cd8623d876fb72d1e8722e7c60dd Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Sat, 18 Feb 2012 17:08:11 +0800 Subject: [PATCH 0129/2237] Arrange Tree and Index definition. --- pygit2.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pygit2.c b/pygit2.c index 153739ed5..ae584a7bd 100644 --- a/pygit2.c +++ b/pygit2.c @@ -109,12 +109,6 @@ typedef struct { Tree *tree; } TreeEntry; -typedef struct { - PyObject_HEAD - Index *owner; - int i; -} IndexIter; - typedef struct { PyObject_HEAD Tree *owner; @@ -126,6 +120,12 @@ typedef struct { git_index_entry *entry; } IndexEntry; +typedef struct { + PyObject_HEAD + Index *owner; + int i; +} IndexIter; + typedef struct { PyObject_HEAD git_reference *reference; @@ -141,14 +141,14 @@ typedef struct { static PyTypeObject RepositoryType; static PyTypeObject ObjectType; static PyTypeObject CommitType; -static PyTypeObject TreeEntryType; static PyTypeObject TreeType; +static PyTypeObject TreeEntryType; +static PyTypeObject TreeIterType; static PyTypeObject BlobType; static PyTypeObject TagType; static PyTypeObject IndexType; -static PyTypeObject TreeIterType; -static PyTypeObject IndexIterType; static PyTypeObject IndexEntryType; +static PyTypeObject IndexIterType; static PyTypeObject WalkerType; static PyTypeObject ReferenceType; static PyTypeObject SignatureType; From b143c0ccde6e38a67306b14169d343ab60587743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 19 Feb 2012 09:20:58 +0100 Subject: [PATCH 0130/2237] Fix compilation warnings --- pygit2.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pygit2.c b/pygit2.c index ae584a7bd..f8e1a5a23 100644 --- a/pygit2.c +++ b/pygit2.c @@ -473,7 +473,6 @@ Repository_read_raw(git_repository *repo, const git_oid *oid, size_t len) git_odb *odb; git_odb_object *obj; int err; - PyObject *aux; err = git_repository_odb(&odb, repo); if (err < 0) { @@ -495,7 +494,6 @@ static PyObject * Repository_read(Repository *self, PyObject *py_hex) { git_oid oid; - int err; git_odb_object *obj; size_t len; PyObject* tuple; @@ -921,7 +919,8 @@ static PyObject * Repository_status_file(Repository *self, PyObject *value) { char *path; - int status, err; + unsigned int status; + int err; path = py_path_to_c_str(value); if (!path) @@ -1081,7 +1080,6 @@ Object_read_raw(Object *self) { const git_oid *oid; git_odb_object *obj; - int err; PyObject *aux; oid = git_object_id(self->obj); From 7e1e8435aa9f8dcb289472c67f6433239aea1c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 19 Feb 2012 17:13:33 +0100 Subject: [PATCH 0131/2237] todo: add note about surrogateescape --- TODO.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.txt b/TODO.txt index d9e656e16..a648a06e8 100644 --- a/TODO.txt +++ b/TODO.txt @@ -12,4 +12,5 @@ References Other ========= - Make the Py_LOCAL_INLINE macro to work with Python 2.6, 2.7 and 3.1 +- Use surrogateescape in Python 3, see PEP-383 - Expose the ODB (Repository.odb) From d43577a46482544ab5b925ca1be29ff41c6cc123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 13 Feb 2012 12:22:39 +0100 Subject: [PATCH 0132/2237] Create the TreeBuilder type --- pygit2.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/pygit2.c b/pygit2.c index f8e1a5a23..c2f4bb5ec 100644 --- a/pygit2.c +++ b/pygit2.c @@ -98,6 +98,7 @@ typedef struct { OBJECT_STRUCT(Object, git_object, obj) OBJECT_STRUCT(Commit, git_commit, commit) OBJECT_STRUCT(Tree, git_tree, tree) +OBJECT_STRUCT(TreeBuilder, git_treebuilder, bld) OBJECT_STRUCT(Blob, git_blob, blob) OBJECT_STRUCT(Tag, git_tag, tag) OBJECT_STRUCT(Index, git_index, index) @@ -142,6 +143,7 @@ static PyTypeObject RepositoryType; static PyTypeObject ObjectType; static PyTypeObject CommitType; static PyTypeObject TreeType; +static PyTypeObject TreeBuilderType; static PyTypeObject TreeEntryType; static PyTypeObject TreeIterType; static PyTypeObject BlobType; @@ -1593,6 +1595,68 @@ static PyTypeObject TreeType = { 0, /* tp_new */ }; +static int +TreeBuilder_init(TreeBuilder *self, PyObject *args, PyObject *kwds) +{ + PyObject *py_name; + int err; + + err = git_treebuilder_create(&self->bld, NULL); + if (err < 0) { + Error_set(err); + return -1; + } + + return 0; +} + +static void +TreeBuilder_dealloc(TreeBuilder* self) +{ + git_treebuilder_free(self->bld); +} + +static PyTypeObject TreeBuilderType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.TreeBuilder", /* tp_name */ + sizeof(TreeBuilder), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)TreeBuilder_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "TreeBuilder objects", /* 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 */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)TreeBuilder_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + static void TreeIter_dealloc(TreeIter *self) { @@ -2918,6 +2982,9 @@ moduleinit(PyObject* m) IndexEntryType.tp_new = PyType_GenericNew; if (PyType_Ready(&IndexEntryType) < 0) return NULL; + TreeBuilderType.tp_new = PyType_GenericNew; + if (PyType_Ready(&TreeBuilderType) < 0) + return NULL; WalkerType.tp_new = PyType_GenericNew; if (PyType_Ready(&WalkerType) < 0) return NULL; @@ -2946,6 +3013,9 @@ moduleinit(PyObject* m) Py_INCREF(&TreeType); PyModule_AddObject(m, "Tree", (PyObject *)&TreeType); + Py_INCREF(&TreeBuilderType); + PyModule_AddObject(m, "TreeBuilder", (PyObject *)&TreeBuilderType); + Py_INCREF(&BlobType); PyModule_AddObject(m, "Blob", (PyObject *)&BlobType); From 5d327c7205e4ce71815dcf545fa05324552b0cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 13 Feb 2012 14:21:09 +0100 Subject: [PATCH 0133/2237] Add TreeBuilder_insert --- pygit2.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index c2f4bb5ec..e3109754f 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1616,6 +1616,32 @@ TreeBuilder_dealloc(TreeBuilder* self) git_treebuilder_free(self->bld); } +static PyObject * +TreeBuilder_insert(TreeBuilder *self, TreeEntry *py_tentry) +{ + int err, attr; + const git_oid *oid; + const char *fname; + const git_tree_entry *tentry; + + tentry = py_tentry->entry; + fname = git_tree_entry_name(tentry); + oid = git_tree_entry_id(tentry); + attr = git_tree_entry_attributes(tentry); + + err = git_treebuilder_insert(NULL, self->bld, fname, oid, attr); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + +static PyMethodDef TreeBuilder_methods[] = { + {"insert", (PyCFunction)TreeBuilder_insert, METH_O, + "Insert or replace an entry in the treebuilder"}, + {NULL, NULL, 0, NULL} +}; + static PyTypeObject TreeBuilderType = { PyVarObject_HEAD_INIT(NULL, 0) "pygit2.TreeBuilder", /* tp_name */ @@ -1644,7 +1670,7 @@ static PyTypeObject TreeBuilderType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + TreeBuilder_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ From 2044699a53bf5902623e4d6a7cb9bca70d455931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 13 Feb 2012 14:30:32 +0100 Subject: [PATCH 0134/2237] Add TreeBuilder_write --- pygit2.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pygit2.c b/pygit2.c index e3109754f..0d2796a31 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1636,9 +1636,25 @@ TreeBuilder_insert(TreeBuilder *self, TreeEntry *py_tentry) Py_RETURN_NONE; } +static PyObject * +TreeBuilder_write(TreeBuilder *self, Repository *py_repo) +{ + int err; + git_oid oid; + git_repository *repo = py_repo->repo; + + err = git_treebuilder_write(&oid, repo, self->bld); + if (err < 0) + return Error_set(err); + + return git_oid_to_python(&oid); +} + static PyMethodDef TreeBuilder_methods[] = { {"insert", (PyCFunction)TreeBuilder_insert, METH_O, "Insert or replace an entry in the treebuilder"}, + {"write", (PyCFunction)TreeBuilder_write, METH_O, + "Write the tree to the given repository"}, {NULL, NULL, 0, NULL} }; From 8b71a8045be73ff6a1abcc7ac68bbbc036ecf91b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 13 Feb 2012 15:00:39 +0100 Subject: [PATCH 0135/2237] Allow a 'source' arg to TreeBuilder() --- pygit2.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pygit2.c b/pygit2.c index 0d2796a31..63b49b491 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1598,10 +1598,22 @@ static PyTypeObject TreeType = { static int TreeBuilder_init(TreeBuilder *self, PyObject *args, PyObject *kwds) { - PyObject *py_name; + Tree *py_tree = NULL; + git_tree *tree; int err; - err = git_treebuilder_create(&self->bld, NULL); + if (kwds) { + PyErr_SetString(PyExc_TypeError, + "TreeBuilder takes no keyword arguments"); + return -1; + } + + if (!PyArg_ParseTuple(args, "|O", &py_tree)) + return -1; + + tree = py_tree == NULL ? NULL : py_tree->tree; + + err = git_treebuilder_create(&self->bld, tree); if (err < 0) { Error_set(err); return -1; From cf380cea2a841e2ce233aec186af0dedd425290d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 13 Feb 2012 15:36:16 +0100 Subject: [PATCH 0136/2237] Add tests for TreeBuilder --- test/__init__.py | 2 +- test/test_treebuilder.py | 64 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 test/test_treebuilder.py diff --git a/test/__init__.py b/test/__init__.py index 1864663a6..62823aea6 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -36,7 +36,7 @@ names = ['blob', 'commit', 'index', 'refs', 'repository', 'revwalk', 'tag', - 'tree', 'signature', 'status'] + 'tree', 'signature', 'status', 'treebuilder'] def test_suite(): modules = ['test.test_%s' % n for n in names] return unittest.defaultTestLoader.loadTestsFromNames(modules) diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py new file mode 100644 index 000000000..0ef6de3af --- /dev/null +++ b/test/test_treebuilder.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2012 Carlos Martín Nieto +# +# 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. + +"""Tests for Index files.""" + +from __future__ import absolute_import +from __future__ import unicode_literals +from binascii import b2a_hex +import os +import unittest + +import pygit2 +from . import utils + + +__author__ = 'carlos@cmartin.tk (Carlos Martín Nieto)' + +TREE_SHA = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' + +class TreeBuilderTest(utils.BareRepoTestCase): + def test_new_empty_treebuilder(self): + bld = pygit2.TreeBuilder() + + def test_noop_treebuilder(self): + tree = self.repo[TREE_SHA] + bld = pygit2.TreeBuilder(tree) + result = bld.write(self.repo) + self.assertEqual(tree.oid, result) + + def test_rebuild_treebuilder(self): + tree = self.repo[TREE_SHA] + bld = pygit2.TreeBuilder(tree) + for e in tree: + bld.insert(e) + + result = bld.write(self.repo) + self.assertEqual(tree.oid, result) + +if __name__ == '__main__': + unittest.main() From 7ad8f0a8f03d7ad13561b342a7469dba032502f7 Mon Sep 17 00:00:00 2001 From: Amit Bakshi Date: Sat, 25 Feb 2012 17:33:59 -0800 Subject: [PATCH 0137/2237] Add discover_repository. pygit2.discover_repository(path, across_fs = False, ceiling_paths = None) Add support for git_discover_repository which searches the given path for the git repository. --- pygit2.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/pygit2.c b/pygit2.c index 63b49b491..9e1025667 100644 --- a/pygit2.c +++ b/pygit2.c @@ -28,6 +28,7 @@ #define PY_SSIZE_T_CLEAN #include +#include #include /* Python 3 support */ @@ -2992,9 +2993,31 @@ init_repository(PyObject *self, PyObject *args) return NULL; }; +static PyObject * +discover_repository(PyObject *self, PyObject *args) +{ + const char *path; + int across_fs = 0; + const char *ceiling_dirs = NULL; + char repo_path[MAXPATHLEN]; + int err; + + if (!PyArg_ParseTuple(args, "s|Is", &path, &across_fs, &ceiling_dirs)) + return NULL; + + err = git_repository_discover(repo_path, sizeof(repo_path), + path, across_fs, ceiling_dirs); + if (err < 0) + return Error_set_str(err, path); + + return to_path(repo_path); +}; + static PyMethodDef module_methods[] = { {"init_repository", init_repository, METH_VARARGS, "Creates a new Git repository in the given folder."}, + {"discover_repository", discover_repository, METH_VARARGS, + "Look for a git repository and return its path."}, {NULL} }; From b1cb51c9d192e2ca2ba1bf1ed2b7d68e9f2cb6be Mon Sep 17 00:00:00 2001 From: Amit Bakshi Date: Sat, 25 Feb 2012 18:15:45 -0800 Subject: [PATCH 0138/2237] Test for pygit2.discover_repository --- test/test_repository.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/test_repository.py b/test/test_repository.py index d07babca1..f8f5764e8 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -34,7 +34,8 @@ import os from os.path import join, realpath -from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, init_repository +from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, init_repository, \ + discover_repository from . import utils @@ -57,7 +58,7 @@ def test_read(self): a2 = self.repo.read('7f129fd57e31e935c6d60a0c794efe4e6927664b') self.assertEqual((GIT_OBJ_BLOB, 'a contents 2\n'), a2) - + a_hex_prefix = A_HEX_SHA[:4] a3 = self.repo.read(a_hex_prefix) self.assertEqual((GIT_OBJ_BLOB, 'a contents\n'), a3) @@ -145,5 +146,12 @@ def test_new_repo(self): assert os.path.exists(os.path.join(self._temp_dir, '.git')) +class DiscoverRepositoryTest(utils.NoRepoTestCase): + def test_discover_repo(self): + repo = init_repository(self._temp_dir, False) + subdir = os.path.join(self._temp_dir, "test1","test2") + os.makedirs(subdir) + self.assertEqual(repo.path, discover_repository(subdir)) + if __name__ == '__main__': unittest.main() From 4cdb1a83b430fa27e2b0800ce17751631b036a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 26 Feb 2012 19:36:04 +0100 Subject: [PATCH 0139/2237] (issue #56) Remove TreeEntry.to_object This is one way to enable implementing 'TreeBuilder.get'. It also makes pygit2 a little lower-level. And makes TreeEntry consistent with IndexEntry, which lacks such a function. --- README.rst | 1 - pygit2.c | 24 ++---------------------- test/test_tree.py | 16 ++++++++-------- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/README.rst b/README.rst index 9e72c5e03..4154545e5 100644 --- a/README.rst +++ b/README.rst @@ -165,7 +165,6 @@ This is the interface of a tree entry:: TreeEntry.oid -- the id of the git object TreeEntry.hex -- hexadecimal representation of the oid TreeEntry.attributes -- the Unix file attributes - TreeEntry.to_object() -- returns the git object (equivalent to repo[entry.oid]) Blobs ----------------- diff --git a/pygit2.c b/pygit2.c index 9e1025667..e81c05ea7 100644 --- a/pygit2.c +++ b/pygit2.c @@ -108,7 +108,6 @@ OBJECT_STRUCT(Walker, git_revwalk, walk) typedef struct { PyObject_HEAD const git_tree_entry *entry; - Tree *tree; } TreeEntry; typedef struct { @@ -1327,7 +1326,6 @@ static PyTypeObject CommitType = { static void TreeEntry_dealloc(TreeEntry *self) { - Py_XDECREF(self->tree); PyObject_Del(self); } @@ -1358,15 +1356,6 @@ TreeEntry_get_hex(TreeEntry *self) return git_oid_to_py_str(git_tree_entry_id(self->entry)); } -static PyObject * -TreeEntry_to_object(TreeEntry *self) -{ - const git_oid *entry_oid; - - entry_oid = git_tree_entry_id(self->entry); - return lookup_object(self->tree->repo, entry_oid, GIT_OBJ_ANY); -} - static PyGetSetDef TreeEntry_getseters[] = { {"attributes", (getter)TreeEntry_get_attributes, NULL, "attributes", NULL}, {"name", (getter)TreeEntry_get_name, NULL, "name", NULL}, @@ -1375,12 +1364,6 @@ static PyGetSetDef TreeEntry_getseters[] = { {NULL} }; -static PyMethodDef TreeEntry_methods[] = { - {"to_object", (PyCFunction)TreeEntry_to_object, METH_NOARGS, - "Look up the corresponding object in the repo."}, - {NULL, NULL, 0, NULL} -}; - static PyTypeObject TreeEntryType = { PyVarObject_HEAD_INIT(NULL, 0) "pygit2.TreeEntry", /* tp_name */ @@ -1409,7 +1392,7 @@ static PyTypeObject TreeEntryType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - TreeEntry_methods, /* tp_methods */ + 0, /* tp_methods */ 0, /* tp_members */ TreeEntry_getseters, /* tp_getset */ 0, /* tp_base */ @@ -1447,11 +1430,8 @@ wrap_tree_entry(const git_tree_entry *entry, Tree *tree) TreeEntry *py_entry; py_entry = PyObject_New(TreeEntry, &TreeEntryType); - if (py_entry) { + if (py_entry) py_entry->entry = entry; - py_entry->tree = tree; - Py_INCREF(tree); - } return py_entry; } diff --git a/test/test_tree.py b/test/test_tree.py index 571f702cc..567b5f05e 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -71,20 +71,21 @@ def test_read_tree(self): self.assertTreeEntryEqual(tree['b'], sha, 'b', 0o0100644) def test_read_subtree(self): - tree = self.repo[TREE_SHA] + repo = self.repo + tree = repo[TREE_SHA] subtree_entry = tree['c'] self.assertTreeEntryEqual(subtree_entry, SUBTREE_SHA, 'c', 0o0040000) - subtree = subtree_entry.to_object() + subtree = repo[subtree_entry.oid] self.assertEqual(1, len(subtree)) sha = '297efb891a47de80be0cfe9c639e4b8c9b450989' self.assertTreeEntryEqual(subtree[0], sha, 'd', 0o0100644) - # XXX Creating new trees was removed from libgit2 by v0.11.0, we - # deactivate this test temporarily, since the feature may come back in - # a near feature (if it does not this test will be removed). + # TODO This test worked with libgit2 v0.10.0, update to use the + # tree-builder def xtest_new_tree(self): - tree = pygit2.Tree(self.repo) + repo = self.repo + tree = pygit2.Tree(repo) self.assertEqual(0, len(tree)) tree.add_entry('1' * 40, 'x', 0o0100644) tree.add_entry('2' * 40, 'y', 0o0100755) @@ -103,8 +104,7 @@ def xtest_new_tree(self): self.assertEqual(None, tree.hex) tree.write() contents = '100644 x\0%s100755 y\0%s' % ('\x11' * 20, '\x22' * 20) - self.assertEqual((pygit2.GIT_OBJ_TREE, contents), - self.repo.read(tree.hex)) + self.assertEqual((pygit2.GIT_OBJ_TREE, contents), repo.read(tree.hex)) def test_modify_tree(self): tree = self.repo[TREE_SHA] From 8b84f90a81ddb7c611f71ca35957ecc4bddb9747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 26 Feb 2012 19:57:33 +0100 Subject: [PATCH 0140/2237] (issue #56) Fix TreeBuilder deallocation Also, add a comment to the TODO file about a memory management issue found throught pygit2. --- TODO.txt | 4 ++++ pygit2.c | 1 + 2 files changed, 5 insertions(+) diff --git a/TODO.txt b/TODO.txt index a648a06e8..4a48a284c 100644 --- a/TODO.txt +++ b/TODO.txt @@ -14,3 +14,7 @@ Other - Make the Py_LOCAL_INLINE macro to work with Python 2.6, 2.7 and 3.1 - Use surrogateescape in Python 3, see PEP-383 - Expose the ODB (Repository.odb) +- According to Python documentation, tp_dealloc must call tp_free (instead of + PyObject_Del or similar) if the type is subclassable. So, go through the + code and switch to tp_free, or make the type not subclassable, on a case by + case basis. diff --git a/pygit2.c b/pygit2.c index e81c05ea7..7769ff41c 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1607,6 +1607,7 @@ static void TreeBuilder_dealloc(TreeBuilder* self) { git_treebuilder_free(self->bld); + PyObject_Del(self); } static PyObject * From e9e902e024bdb890d6b0a4b411055440acc46de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 27 Feb 2012 00:12:33 +0100 Subject: [PATCH 0141/2237] tests: add utility function 'oid_to_hex' --- test/test_blob.py | 3 +-- test/test_index.py | 5 ++--- test/test_treebuilder.py | 1 - test/utils.py | 5 +++++ 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/test/test_blob.py b/test/test_blob.py index a8658724b..3c8d8e531 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -29,7 +29,6 @@ from __future__ import absolute_import from __future__ import unicode_literals -from binascii import b2a_hex import unittest import pygit2 @@ -46,7 +45,7 @@ class BlobTest(utils.BareRepoTestCase): def test_read_blob(self): blob = self.repo[BLOB_SHA] self.assertEqual(blob.hex, BLOB_SHA) - sha = b2a_hex(blob.oid).decode('ascii') + sha = utils.oid_to_hex(blob.oid) self.assertEqual(sha, BLOB_SHA) self.assertTrue(isinstance(blob, pygit2.Blob)) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) diff --git a/test/test_index.py b/test/test_index.py index a8c47770a..39fb01538 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -29,7 +29,6 @@ from __future__ import absolute_import from __future__ import unicode_literals -from binascii import b2a_hex import os import unittest @@ -102,7 +101,7 @@ def test_read_tree(self): self.assertEqual(len(index), 1) # Test read-write returns the same oid oid = index.write_tree() - oid = b2a_hex(oid).decode('ascii') + oid = utils.oid_to_hex(oid) self.assertEqual(oid, tree_oid) # Test the index is only modified in memory index.read() @@ -111,7 +110,7 @@ def test_read_tree(self): def test_write_tree(self): oid = self.repo.index.write_tree() - sha = b2a_hex(oid).decode('ascii') + sha = utils.oid_to_hex(oid) self.assertEqual(sha, 'fd937514cb799514d4b81bb24c5fcfeb6472b245') def test_iter(self): diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index 0ef6de3af..7fbbc40e4 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -29,7 +29,6 @@ from __future__ import absolute_import from __future__ import unicode_literals -from binascii import b2a_hex import os import unittest diff --git a/test/utils.py b/test/utils.py index f56d29d63..8dbaf6339 100644 --- a/test/utils.py +++ b/test/utils.py @@ -27,6 +27,7 @@ """Test utilities for libgit2.""" +from binascii import b2a_hex import os import shutil import stat @@ -40,6 +41,10 @@ __author__ = 'dborowitz@google.com (Dave Borowitz)' +def oid_to_hex(oid): + return b2a_hex(oid).decode('ascii') + + def rmtree(path): """In Windows a read-only file cannot be removed, and shutil.rmtree fails. So we implement our own version of rmtree to address this issue. From 4b0f759cc051b1df5f7b7f5f6143ee70e2213419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 27 Feb 2012 00:13:43 +0100 Subject: [PATCH 0142/2237] Tag.target now returns the oid (not the object) --- pygit2.c | 8 +++----- test/test_tag.py | 10 ++++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pygit2.c b/pygit2.c index 7769ff41c..d8d65d896 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1793,12 +1793,10 @@ static PyTypeObject BlobType = { static PyObject * Tag_get_target(Tag *self) { - const git_oid *target_oid; - git_otype target_type; + const git_oid *oid; - target_oid = git_tag_target_oid(self->tag); - target_type = git_tag_type(self->tag); - return lookup_object(self->repo, target_oid, target_type); + oid = git_tag_target_oid(self->tag); + return git_oid_to_python(oid->id); } static PyObject * diff --git a/test/test_tag.py b/test/test_tag.py index 5c11e0189..ca115693e 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -43,13 +43,15 @@ class TagTest(utils.BareRepoTestCase): def test_read_tag(self): - tag = self.repo[TAG_SHA] + repo = self.repo + tag = repo[TAG_SHA] + target = repo[tag.target] self.assertTrue(isinstance(tag, pygit2.Tag)) self.assertEqual(pygit2.GIT_OBJ_TAG, tag.type) - self.assertEqual(pygit2.GIT_OBJ_COMMIT, tag.target.type) + self.assertEqual(pygit2.GIT_OBJ_COMMIT, target.type) self.assertEqual('root', tag.name) self.assertEqual('Tagged root commit.\n', tag.message) - self.assertEqual('Initial test data commit.\n', tag.target.message) + self.assertEqual('Initial test data commit.\n', target.message) self.assertEqualSignature( tag.tagger, pygit2.Signature('Dave Borowitz', 'dborowitz@google.com', @@ -72,7 +74,7 @@ def test_new_tag(self): self.assertEqual('3ee44658fd11660e828dfc96b9b5c5f38d5b49bb', tag.hex) self.assertEqual(name, tag.name) - self.assertEqual(target, tag.target.hex) + self.assertEqual(target, utils.oid_to_hex(tag.target)) self.assertEqualSignature(tagger, tag.tagger) self.assertEqual(message, tag.message) self.assertEqual(name, self.repo[tag.hex].name) From 1fb34e7b96000d29c4633b66fabc0699ac74ce15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 27 Feb 2012 00:25:57 +0100 Subject: [PATCH 0143/2237] Commit.parents now returns a list of oids Instead of returning the commit objects. --- pygit2.c | 8 +------- test/test_commit.py | 6 +++--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/pygit2.c b/pygit2.c index d8d65d896..1ec238b8e 100644 --- a/pygit2.c +++ b/pygit2.c @@ -262,12 +262,6 @@ lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, return (PyObject*)py_obj; } -static PyObject * -lookup_object(Repository *repo, const git_oid *oid, git_otype type) -{ - return lookup_object_prefix(repo, oid, GIT_OID_HEXSZ, type); -} - static git_otype int_to_loose_object_type(int type_id) { @@ -1254,7 +1248,7 @@ Commit_get_parents(Commit *commit) Error_set(GIT_ENOTFOUND); return NULL; } - obj = lookup_object(commit->repo, parent_oid, GIT_OBJ_COMMIT); + obj = git_oid_to_python(parent_oid); if (obj == NULL) { Py_DECREF(list); return NULL; diff --git a/test/test_commit.py b/test/test_commit.py index 526d7d1a8..51dff737d 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -48,7 +48,7 @@ def test_read_commit(self): parents = commit.parents self.assertEqual(1, len(parents)) self.assertEqual('c2792cfa289ae6321ecf2cd5806c2194b0fd070c', - parents[0].hex) + utils.oid_to_hex(parents[0])) self.assertEqual(None, commit.message_encoding) self.assertEqual(('Second test data commit.\n\n' 'This commit has some additional text.\n'), @@ -93,7 +93,7 @@ def test_new_commit(self): self.assertEqualSignature(author, commit.author) self.assertEqual(tree, commit.tree.hex) self.assertEqual(1, len(commit.parents)) - self.assertEqual(COMMIT_SHA, commit.parents[0].hex) + self.assertEqual(COMMIT_SHA, utils.oid_to_hex(commit.parents[0])) def test_new_commit_encoding(self): repo = self.repo @@ -119,7 +119,7 @@ def test_new_commit_encoding(self): self.assertEqualSignature(author, commit.author) self.assertEqual(tree, commit.tree.hex) self.assertEqual(1, len(commit.parents)) - self.assertEqual(COMMIT_SHA, commit.parents[0].hex) + self.assertEqual(COMMIT_SHA, utils.oid_to_hex(commit.parents[0])) def test_modify_commit(self): message = 'New commit.\n\nMessage.\n' From c6ebc98930cc337ce03cb5d715a1add4b3663743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 28 Feb 2012 13:02:37 +0100 Subject: [PATCH 0144/2237] Revert "Commit.parents now returns a list of oids" This reverts commit 1fb34e7b96000d29c4633b66fabc0699ac74ce15. --- pygit2.c | 8 +++++++- test/test_commit.py | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pygit2.c b/pygit2.c index 1ec238b8e..d8d65d896 100644 --- a/pygit2.c +++ b/pygit2.c @@ -262,6 +262,12 @@ lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, return (PyObject*)py_obj; } +static PyObject * +lookup_object(Repository *repo, const git_oid *oid, git_otype type) +{ + return lookup_object_prefix(repo, oid, GIT_OID_HEXSZ, type); +} + static git_otype int_to_loose_object_type(int type_id) { @@ -1248,7 +1254,7 @@ Commit_get_parents(Commit *commit) Error_set(GIT_ENOTFOUND); return NULL; } - obj = git_oid_to_python(parent_oid); + obj = lookup_object(commit->repo, parent_oid, GIT_OBJ_COMMIT); if (obj == NULL) { Py_DECREF(list); return NULL; diff --git a/test/test_commit.py b/test/test_commit.py index 51dff737d..526d7d1a8 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -48,7 +48,7 @@ def test_read_commit(self): parents = commit.parents self.assertEqual(1, len(parents)) self.assertEqual('c2792cfa289ae6321ecf2cd5806c2194b0fd070c', - utils.oid_to_hex(parents[0])) + parents[0].hex) self.assertEqual(None, commit.message_encoding) self.assertEqual(('Second test data commit.\n\n' 'This commit has some additional text.\n'), @@ -93,7 +93,7 @@ def test_new_commit(self): self.assertEqualSignature(author, commit.author) self.assertEqual(tree, commit.tree.hex) self.assertEqual(1, len(commit.parents)) - self.assertEqual(COMMIT_SHA, utils.oid_to_hex(commit.parents[0])) + self.assertEqual(COMMIT_SHA, commit.parents[0].hex) def test_new_commit_encoding(self): repo = self.repo @@ -119,7 +119,7 @@ def test_new_commit_encoding(self): self.assertEqualSignature(author, commit.author) self.assertEqual(tree, commit.tree.hex) self.assertEqual(1, len(commit.parents)) - self.assertEqual(COMMIT_SHA, utils.oid_to_hex(commit.parents[0])) + self.assertEqual(COMMIT_SHA, commit.parents[0].hex) def test_modify_commit(self): message = 'New commit.\n\nMessage.\n' From 8380c625c4fa027f666d95dc65b58cc38cd275c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 3 Mar 2012 17:33:57 +0100 Subject: [PATCH 0145/2237] Check for error after call to py_str_to_c_str Fixes segfaults in 'Repository.create_commit' and 'Signature(...)' Error reported by Han-Wen Nienhuys in the mailing list. --- pygit2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pygit2.c b/pygit2.c index d8d65d896..cb4d82675 100644 --- a/pygit2.c +++ b/pygit2.c @@ -694,6 +694,8 @@ Repository_create_commit(Repository *self, PyObject *args) goto out; message = py_str_to_c_str(py_message, encoding); + if (message == NULL) + goto out; err = git_tree_lookup_prefix(&tree, self->repo, &oid, (unsigned int)len); if (err < 0) { @@ -2811,6 +2813,8 @@ Signature_init(Signature *self, PyObject *args, PyObject *kwds) return -1; name = py_str_to_c_str(py_name, encoding); + if (name == NULL) + return -1; err = git_signature_new(&signature, name, email, time, offset); if (err < 0) { From eb522b0caa7317e4b952ff6754d093701c342a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 17 Feb 2012 17:14:13 +0100 Subject: [PATCH 0146/2237] Make TreeBuilder grow out of the repository A TreeBuilder is a lot more useful (and easy to use) when it belongs to a repository. --- pygit2.c | 81 +++++++++++++++++++++++----------------- test/test_treebuilder.py | 10 ++--- 2 files changed, 51 insertions(+), 40 deletions(-) diff --git a/pygit2.c b/pygit2.c index cb4d82675..f63a11975 100644 --- a/pygit2.c +++ b/pygit2.c @@ -937,6 +937,45 @@ Repository_status_file(Repository *self, PyObject *value) return PyInt_FromLong(status); } +static PyObject * +Repository_TreeBuilder(Repository *self, PyObject *args) +{ + TreeBuilder *builder; + git_treebuilder *bld; + PyObject *py_oid = NULL; + size_t oid_len; + git_oid oid; + git_tree *tree = NULL; + int err; + + if (!PyArg_ParseTuple(args, "|O", &py_oid)) + return NULL; + + if (py_oid) { + oid_len = py_str_to_git_oid(py_oid, &oid); + TODO_SUPPORT_SHORT_HEXS(oid_len) + if (oid_len == 0) + return NULL; + + err = git_tree_lookup(&tree, self->repo, &oid); + if (err < 0) + return Error_set(err); + } + + err = git_treebuilder_create(&bld, tree); + if (err < 0) + return Error_set(err); + + builder = PyObject_New(TreeBuilder, &TreeBuilderType); + if (builder) { + builder->repo = self; + builder->bld = bld; + Py_INCREF(self); + } + + return (PyObject*)builder; +} + static PyMethodDef Repository_methods[] = { {"create_commit", (PyCFunction)Repository_create_commit, METH_VARARGS, "Create a new commit object, return its SHA."}, @@ -970,6 +1009,8 @@ static PyMethodDef Repository_methods[] = { "as keys and status flags as values.\nSee pygit2.GIT_STATUS_*."}, {"status_file", (PyCFunction)Repository_status_file, METH_O, "Returns the status of the given file path."}, + {"TreeBuilder", (PyCFunction)Repository_TreeBuilder, METH_VARARGS, + "Create a TreeBuilder object for this repository."}, {NULL} }; @@ -1578,36 +1619,10 @@ static PyTypeObject TreeType = { 0, /* tp_new */ }; -static int -TreeBuilder_init(TreeBuilder *self, PyObject *args, PyObject *kwds) -{ - Tree *py_tree = NULL; - git_tree *tree; - int err; - - if (kwds) { - PyErr_SetString(PyExc_TypeError, - "TreeBuilder takes no keyword arguments"); - return -1; - } - - if (!PyArg_ParseTuple(args, "|O", &py_tree)) - return -1; - - tree = py_tree == NULL ? NULL : py_tree->tree; - - err = git_treebuilder_create(&self->bld, tree); - if (err < 0) { - Error_set(err); - return -1; - } - - return 0; -} - static void TreeBuilder_dealloc(TreeBuilder* self) { + Py_XDECREF(self->repo); git_treebuilder_free(self->bld); PyObject_Del(self); } @@ -1633,13 +1648,12 @@ TreeBuilder_insert(TreeBuilder *self, TreeEntry *py_tentry) } static PyObject * -TreeBuilder_write(TreeBuilder *self, Repository *py_repo) +TreeBuilder_write(TreeBuilder *self) { int err; git_oid oid; - git_repository *repo = py_repo->repo; - err = git_treebuilder_write(&oid, repo, self->bld); + err = git_treebuilder_write(&oid, self->repo->repo, self->bld); if (err < 0) return Error_set(err); @@ -1649,7 +1663,7 @@ TreeBuilder_write(TreeBuilder *self, Repository *py_repo) static PyMethodDef TreeBuilder_methods[] = { {"insert", (PyCFunction)TreeBuilder_insert, METH_O, "Insert or replace an entry in the treebuilder"}, - {"write", (PyCFunction)TreeBuilder_write, METH_O, + {"write", (PyCFunction)TreeBuilder_write, METH_NOARGS, "Write the tree to the given repository"}, {NULL, NULL, 0, NULL} }; @@ -1690,7 +1704,7 @@ static PyTypeObject TreeBuilderType = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)TreeBuilder_init, /* tp_init */ + 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; @@ -3073,9 +3087,6 @@ moduleinit(PyObject* m) Py_INCREF(&TreeType); PyModule_AddObject(m, "Tree", (PyObject *)&TreeType); - Py_INCREF(&TreeBuilderType); - PyModule_AddObject(m, "TreeBuilder", (PyObject *)&TreeBuilderType); - Py_INCREF(&BlobType); PyModule_AddObject(m, "Blob", (PyObject *)&BlobType); diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index 7fbbc40e4..d1b003ba0 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -42,21 +42,21 @@ class TreeBuilderTest(utils.BareRepoTestCase): def test_new_empty_treebuilder(self): - bld = pygit2.TreeBuilder() + bld = self.repo.TreeBuilder() def test_noop_treebuilder(self): tree = self.repo[TREE_SHA] - bld = pygit2.TreeBuilder(tree) - result = bld.write(self.repo) + bld = self.repo.TreeBuilder(TREE_SHA) + result = bld.write() self.assertEqual(tree.oid, result) def test_rebuild_treebuilder(self): tree = self.repo[TREE_SHA] - bld = pygit2.TreeBuilder(tree) + bld = self.repo.TreeBuilder(TREE_SHA) for e in tree: bld.insert(e) - result = bld.write(self.repo) + result = bld.write() self.assertEqual(tree.oid, result) if __name__ == '__main__': From 3e73d3a2b8173417ba74a3a99e2f12d7a0d4841c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 21 Feb 2012 13:04:13 +0100 Subject: [PATCH 0147/2237] Add remove() and clear() to TreeBuilder --- pygit2.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/pygit2.c b/pygit2.c index f63a11975..0d9fd537c 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1660,11 +1660,39 @@ TreeBuilder_write(TreeBuilder *self) return git_oid_to_python(&oid); } +static PyObject * +TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename) +{ + char *filename; + int err; + + filename = py_path_to_c_str(py_filename); + if (filename == NULL) + return NULL; + + err = git_treebuilder_remove(self->bld, filename); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + +static PyObject * +TreeBuilder_clear(TreeBuilder *self) +{ + git_treebuilder_clear(self->bld); + Py_RETURN_NONE; +} + static PyMethodDef TreeBuilder_methods[] = { {"insert", (PyCFunction)TreeBuilder_insert, METH_O, "Insert or replace an entry in the treebuilder"}, {"write", (PyCFunction)TreeBuilder_write, METH_NOARGS, "Write the tree to the given repository"}, + {"remove", (PyCFunction)TreeBuilder_remove, METH_O, + "Remove an entry from the builder"}, + {"clear", (PyCFunction)TreeBuilder_clear, METH_NOARGS, + "Clear all the entries in the builder"}, {NULL, NULL, 0, NULL} }; From 242f3c2ffa267006f0dbba098bbec8fe3e92d620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 9 Mar 2012 12:12:05 +0100 Subject: [PATCH 0148/2237] TreeBuilder: allow the source to be a Tree If the user passes a tree, use it as the source for the TreeBuilder. On the way, make sure we free the tree we looked up, and fix a test to make sure the TreeBuilder starts empty. --- pygit2.c | 31 ++++++++++++++++++++----------- test/test_treebuilder.py | 8 +++++++- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/pygit2.c b/pygit2.c index 0d9fd537c..2a7c3178c 100644 --- a/pygit2.c +++ b/pygit2.c @@ -942,27 +942,36 @@ Repository_TreeBuilder(Repository *self, PyObject *args) { TreeBuilder *builder; git_treebuilder *bld; - PyObject *py_oid = NULL; + PyObject *py_src = NULL; size_t oid_len; git_oid oid; git_tree *tree = NULL; int err; - if (!PyArg_ParseTuple(args, "|O", &py_oid)) + if (!PyArg_ParseTuple(args, "|O", &py_src)) return NULL; - if (py_oid) { - oid_len = py_str_to_git_oid(py_oid, &oid); - TODO_SUPPORT_SHORT_HEXS(oid_len) - if (oid_len == 0) - return NULL; - - err = git_tree_lookup(&tree, self->repo, &oid); - if (err < 0) - return Error_set(err); + if (py_src) { + if (PyObject_TypeCheck(py_src, &TreeType)) { + Tree *py_tree = (Tree *)py_src; + if (py_tree->repo->repo != self->repo) { + return Error_set(GIT_EINVALIDARGS); + } + tree = py_tree->tree; + } else { + oid_len = py_str_to_git_oid(py_src, &oid); + TODO_SUPPORT_SHORT_HEXS(oid_len) + if (oid_len == 0) + return NULL; + + err = git_tree_lookup(&tree, self->repo, &oid); + if (err < 0) + return Error_set(err); + } } err = git_treebuilder_create(&bld, tree); + git_tree_free(tree); if (err < 0) return Error_set(err); diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index d1b003ba0..f9628b164 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -50,9 +50,15 @@ def test_noop_treebuilder(self): result = bld.write() self.assertEqual(tree.oid, result) + def test_noop_treebuilder_from_tree(self): + tree = self.repo[TREE_SHA] + bld = self.repo.TreeBuilder(tree) + result = bld.write() + self.assertEqual(tree.oid, result) + def test_rebuild_treebuilder(self): tree = self.repo[TREE_SHA] - bld = self.repo.TreeBuilder(TREE_SHA) + bld = self.repo.TreeBuilder() for e in tree: bld.insert(e) From 6dd8ec65a9525a31a1707d370e442f849f2a50de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 9 Mar 2012 23:15:08 +0100 Subject: [PATCH 0149/2237] (issue #75) Include readme file in source dist Patch submitted by rl-0x0 --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 000000000..bb37a2723 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include *.rst From f11533a65a181ff71b224e1633c25f6c21a57c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 11 Mar 2012 22:40:37 +0100 Subject: [PATCH 0150/2237] Revert "(issue #56) Remove TreeEntry.to_object" This reverts commit 4cdb1a83b430fa27e2b0800ce17751631b036a78. --- README.rst | 1 + pygit2.c | 24 ++++++++++++++++++++++-- test/test_tree.py | 16 ++++++++-------- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/README.rst b/README.rst index 4154545e5..9e72c5e03 100644 --- a/README.rst +++ b/README.rst @@ -165,6 +165,7 @@ This is the interface of a tree entry:: TreeEntry.oid -- the id of the git object TreeEntry.hex -- hexadecimal representation of the oid TreeEntry.attributes -- the Unix file attributes + TreeEntry.to_object() -- returns the git object (equivalent to repo[entry.oid]) Blobs ----------------- diff --git a/pygit2.c b/pygit2.c index cb4d82675..fbc7226c7 100644 --- a/pygit2.c +++ b/pygit2.c @@ -108,6 +108,7 @@ OBJECT_STRUCT(Walker, git_revwalk, walk) typedef struct { PyObject_HEAD const git_tree_entry *entry; + Tree *tree; } TreeEntry; typedef struct { @@ -1328,6 +1329,7 @@ static PyTypeObject CommitType = { static void TreeEntry_dealloc(TreeEntry *self) { + Py_XDECREF(self->tree); PyObject_Del(self); } @@ -1358,6 +1360,15 @@ TreeEntry_get_hex(TreeEntry *self) return git_oid_to_py_str(git_tree_entry_id(self->entry)); } +static PyObject * +TreeEntry_to_object(TreeEntry *self) +{ + const git_oid *entry_oid; + + entry_oid = git_tree_entry_id(self->entry); + return lookup_object(self->tree->repo, entry_oid, GIT_OBJ_ANY); +} + static PyGetSetDef TreeEntry_getseters[] = { {"attributes", (getter)TreeEntry_get_attributes, NULL, "attributes", NULL}, {"name", (getter)TreeEntry_get_name, NULL, "name", NULL}, @@ -1366,6 +1377,12 @@ static PyGetSetDef TreeEntry_getseters[] = { {NULL} }; +static PyMethodDef TreeEntry_methods[] = { + {"to_object", (PyCFunction)TreeEntry_to_object, METH_NOARGS, + "Look up the corresponding object in the repo."}, + {NULL, NULL, 0, NULL} +}; + static PyTypeObject TreeEntryType = { PyVarObject_HEAD_INIT(NULL, 0) "pygit2.TreeEntry", /* tp_name */ @@ -1394,7 +1411,7 @@ static PyTypeObject TreeEntryType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + TreeEntry_methods, /* tp_methods */ 0, /* tp_members */ TreeEntry_getseters, /* tp_getset */ 0, /* tp_base */ @@ -1432,8 +1449,11 @@ wrap_tree_entry(const git_tree_entry *entry, Tree *tree) TreeEntry *py_entry; py_entry = PyObject_New(TreeEntry, &TreeEntryType); - if (py_entry) + if (py_entry) { py_entry->entry = entry; + py_entry->tree = tree; + Py_INCREF(tree); + } return py_entry; } diff --git a/test/test_tree.py b/test/test_tree.py index 567b5f05e..571f702cc 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -71,21 +71,20 @@ def test_read_tree(self): self.assertTreeEntryEqual(tree['b'], sha, 'b', 0o0100644) def test_read_subtree(self): - repo = self.repo - tree = repo[TREE_SHA] + tree = self.repo[TREE_SHA] subtree_entry = tree['c'] self.assertTreeEntryEqual(subtree_entry, SUBTREE_SHA, 'c', 0o0040000) - subtree = repo[subtree_entry.oid] + subtree = subtree_entry.to_object() self.assertEqual(1, len(subtree)) sha = '297efb891a47de80be0cfe9c639e4b8c9b450989' self.assertTreeEntryEqual(subtree[0], sha, 'd', 0o0100644) - # TODO This test worked with libgit2 v0.10.0, update to use the - # tree-builder + # XXX Creating new trees was removed from libgit2 by v0.11.0, we + # deactivate this test temporarily, since the feature may come back in + # a near feature (if it does not this test will be removed). def xtest_new_tree(self): - repo = self.repo - tree = pygit2.Tree(repo) + tree = pygit2.Tree(self.repo) self.assertEqual(0, len(tree)) tree.add_entry('1' * 40, 'x', 0o0100644) tree.add_entry('2' * 40, 'y', 0o0100755) @@ -104,7 +103,8 @@ def xtest_new_tree(self): self.assertEqual(None, tree.hex) tree.write() contents = '100644 x\0%s100755 y\0%s' % ('\x11' * 20, '\x22' * 20) - self.assertEqual((pygit2.GIT_OBJ_TREE, contents), repo.read(tree.hex)) + self.assertEqual((pygit2.GIT_OBJ_TREE, contents), + self.repo.read(tree.hex)) def test_modify_tree(self): tree = self.repo[TREE_SHA] From a6a36ad80352ed13b87dda59cae4d8b8e1128110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 11 Mar 2012 22:50:55 +0100 Subject: [PATCH 0151/2237] Now TreeEntry points back to the repo Instead of pointing back to the tree. --- pygit2.c | 23 +++++++++-------------- test/test_tree.py | 5 ++--- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/pygit2.c b/pygit2.c index fbc7226c7..6a530125f 100644 --- a/pygit2.c +++ b/pygit2.c @@ -104,12 +104,7 @@ OBJECT_STRUCT(Blob, git_blob, blob) OBJECT_STRUCT(Tag, git_tag, tag) OBJECT_STRUCT(Index, git_index, index) OBJECT_STRUCT(Walker, git_revwalk, walk) - -typedef struct { - PyObject_HEAD - const git_tree_entry *entry; - Tree *tree; -} TreeEntry; +OBJECT_STRUCT(TreeEntry, git_tree_entry, entry) typedef struct { PyObject_HEAD @@ -1329,7 +1324,7 @@ static PyTypeObject CommitType = { static void TreeEntry_dealloc(TreeEntry *self) { - Py_XDECREF(self->tree); + Py_XDECREF(self->repo); PyObject_Del(self); } @@ -1366,7 +1361,7 @@ TreeEntry_to_object(TreeEntry *self) const git_oid *entry_oid; entry_oid = git_tree_entry_id(self->entry); - return lookup_object(self->tree->repo, entry_oid, GIT_OBJ_ANY); + return lookup_object(self->repo, entry_oid, GIT_OBJ_ANY); } static PyGetSetDef TreeEntry_getseters[] = { @@ -1444,15 +1439,15 @@ Tree_contains(Tree *self, PyObject *py_name) } static TreeEntry * -wrap_tree_entry(const git_tree_entry *entry, Tree *tree) +wrap_tree_entry(const git_tree_entry *entry, Repository *repo) { TreeEntry *py_entry; py_entry = PyObject_New(TreeEntry, &TreeEntryType); if (py_entry) { py_entry->entry = entry; - py_entry->tree = tree; - Py_INCREF(tree); + py_entry->repo = repo; + Py_INCREF(repo); } return py_entry; } @@ -1515,7 +1510,7 @@ Tree_getitem_by_index(Tree *self, PyObject *py_index) PyErr_SetObject(PyExc_IndexError, py_index); return NULL; } - return wrap_tree_entry(entry, self); + return wrap_tree_entry(entry, self->repo); } static TreeEntry * @@ -1537,7 +1532,7 @@ Tree_getitem(Tree *self, PyObject *value) PyErr_SetObject(PyExc_KeyError, value); return NULL; } - return wrap_tree_entry(entry, self); + return wrap_tree_entry(entry, self->repo); } static PySequenceMethods Tree_as_sequence = { @@ -1732,7 +1727,7 @@ TreeIter_iternext(TreeIter *self) return NULL; self->i += 1; - return (TreeEntry*)wrap_tree_entry(tree_entry, self->owner); + return (TreeEntry*)wrap_tree_entry(tree_entry, self->owner->repo); } static PyTypeObject TreeIterType = { diff --git a/test/test_tree.py b/test/test_tree.py index 571f702cc..6c1a91640 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -80,9 +80,8 @@ def test_read_subtree(self): sha = '297efb891a47de80be0cfe9c639e4b8c9b450989' self.assertTreeEntryEqual(subtree[0], sha, 'd', 0o0100644) - # XXX Creating new trees was removed from libgit2 by v0.11.0, we - # deactivate this test temporarily, since the feature may come back in - # a near feature (if it does not this test will be removed). + # TODO This test worked with libgit2 v0.10.0, update to use the + # tree-builder def xtest_new_tree(self): tree = pygit2.Tree(self.repo) self.assertEqual(0, len(tree)) From d35944cc7d435e04df0203421eb00768345c33f6 Mon Sep 17 00:00:00 2001 From: Bryan O'Sullivan Date: Tue, 13 Mar 2012 16:21:42 -0700 Subject: [PATCH 0152/2237] Decode messages leniently if encoding is not known This avoids throwing of a UnicodeDecodeError on some really old commits. We also add undecoded attribute accessors, to get at the raw bytes. --- pygit2.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/pygit2.c b/pygit2.c index c4744b46e..cc7e9d2b6 100644 --- a/pygit2.c +++ b/pygit2.c @@ -49,8 +49,14 @@ Py_LOCAL_INLINE(PyObject*) to_unicode(const char *value, const char *encoding, const char *errors) { - if (encoding == NULL) + if (encoding == NULL) { + /* If the encoding is not explicit, it may not be UTF-8, so it + * is not safe to decode it strictly. This is rare in the + * wild, but does occur in old commits to git itself + * (e.g. c31820c2). */ encoding = "utf-8"; + errors = "replace"; + } return PyUnicode_Decode(value, strlen(value), encoding, errors); } @@ -1223,6 +1229,12 @@ Commit_get_message(Commit *commit) return to_unicode(message, encoding, "strict"); } +static PyObject * +Commit_get_raw_message(Commit *commit) +{ + return PyString_FromString(git_commit_message(commit->commit)); +} + static PyObject * Commit_get_commit_time(Commit *commit) { @@ -1318,6 +1330,7 @@ static PyGetSetDef Commit_getseters[] = { {"message_encoding", (getter)Commit_get_message_encoding, NULL, "message encoding", NULL}, {"message", (getter)Commit_get_message, NULL, "message", NULL}, + {"_message", (getter)Commit_get_raw_message, NULL, "message (bytes)", NULL}, {"commit_time", (getter)Commit_get_commit_time, NULL, "commit time", NULL}, {"commit_time_offset", (getter)Commit_get_commit_time_offset, NULL, @@ -1897,12 +1910,18 @@ Tag_get_message(Tag *self) return to_unicode(message, "utf-8", "strict"); } +static PyObject * +Tag_get_raw_message(Tag *self) +{ + return PyString_FromString(git_tag_message(self->tag)); +} + static PyGetSetDef Tag_getseters[] = { {"target", (getter)Tag_get_target, NULL, "tagged object", NULL}, {"name", (getter)Tag_get_name, NULL, "tag name", NULL}, {"tagger", (getter)Tag_get_tagger, NULL, "tagger", NULL}, - {"message", (getter)Tag_get_message, NULL, "tag message", - NULL}, + {"message", (getter)Tag_get_message, NULL, "tag message", NULL}, + {"_message", (getter)Tag_get_raw_message, NULL, "tag message (bytes)", NULL}, {NULL} }; From a444cd1e1cb46caf8ca8be923b3ea405c7f1fc1e Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Wed, 22 Feb 2012 10:53:52 +0800 Subject: [PATCH 0153/2237] utils.py Use onerror deal with rmtree --- test/utils.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/test/utils.py b/test/utils.py index 8dbaf6339..421f928ac 100644 --- a/test/utils.py +++ b/test/utils.py @@ -40,6 +40,12 @@ __author__ = 'dborowitz@google.com (Dave Borowitz)' +def force_rm_handle(remove_path, path, excinfo): + os.chmod( + path, + os.stat(path).st_mode | stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH + ) + remove_path(path) def oid_to_hex(oid): return b2a_hex(oid).decode('ascii') @@ -49,16 +55,8 @@ def rmtree(path): """In Windows a read-only file cannot be removed, and shutil.rmtree fails. So we implement our own version of rmtree to address this issue. """ - for root, dirs, files in os.walk(path, topdown=False): - for name in files: - filename = os.path.join(root, name) - try: - os.remove(filename) - except OSError: - # Try again - os.chmod(filename, stat.S_IWUSR) - os.remove(filename) - os.rmdir(root) + if os.path.exists(path): + shutil.rmtree(path, onerror=lambda func, path, e: force_rm_handle(func, path, e)) class NoRepoTestCase(unittest.TestCase): From 139034f66f3d8cf8201100ed92bf4933df88b92b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 29 Feb 2012 15:52:21 +0100 Subject: [PATCH 0154/2237] Support short OIDs Intoduce and use py_str_to_git_oid_expand() to get a full OID from a short one the user may have passed. Repository_contains() has learnt to do it by itself as expanding an OID means asking the repository's ODB for the object anyway. The return values have been made consistent with other functions and py_str_to_git_oid() now returns an int and marks errors with a negative value. --- pygit2.c | 146 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 86 insertions(+), 60 deletions(-) diff --git a/pygit2.c b/pygit2.c index cc7e9d2b6..1b2585107 100644 --- a/pygit2.c +++ b/pygit2.c @@ -294,7 +294,7 @@ wrap_reference(git_reference * c_reference) return (PyObject *)py_reference; } -static size_t +static int py_str_to_git_oid(PyObject *py_str, git_oid *oid) { PyObject *py_hex; @@ -306,7 +306,7 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) if (PyString_Check(py_str)) { hex_or_bin = PyString_AsString(py_str); if (hex_or_bin == NULL) - return 0; + return -1; git_oid_fromraw(oid, (const unsigned char*)hex_or_bin); return GIT_OID_HEXSZ; } @@ -315,15 +315,15 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) if (PyUnicode_Check(py_str)) { py_hex = PyUnicode_AsASCIIString(py_str); if (py_hex == NULL) - return 0; + return -1; err = PyString_AsStringAndSize(py_hex, &hex_or_bin, &len); Py_DECREF(py_hex); if (err) - return 0; + return -1; err = git_oid_fromstrn(oid, hex_or_bin, len); if (err < 0) { PyErr_SetObject(Error_type(err), py_str); - return 0; + return -1; } return len; } @@ -332,16 +332,42 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) PyErr_Format(PyExc_TypeError, "Git object id must be byte or a text string, not: %.200s", Py_TYPE(py_str)->tp_name); - return 0; + return -1; } -#define TODO_SUPPORT_SHORT_HEXS(len) \ - if (0 < len && len < GIT_OID_HEXSZ) {\ - PyErr_SetString(PyExc_NotImplementedError,\ - "short strings not yet supported");\ - len = 0;\ +static int +py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, git_oid *oid) +{ + int err; + int len; + git_odb *odb; + git_odb_object *obj; + + len = py_str_to_git_oid(py_str, oid); + + if (len == GIT_OID_HEXSZ || len < 0) + return len; + + err = git_repository_odb(&odb, repo); + if (err < 0) { + Error_set(err); + return -1; + } + + err = git_odb_read_prefix(&obj, odb, oid, len); + if (err < 0) { + git_odb_free(odb); + Error_set(err); + return err; } + git_oid_cpy(oid, git_odb_object_id(obj)); + + git_odb_object_free(obj); + git_odb_free(odb); + + return 0; +} #define git_oid_to_python(id) \ PyString_FromStringAndSize((const char*)id, GIT_OID_RAWSZ) @@ -439,12 +465,10 @@ Repository_contains(Repository *self, PyObject *value) { git_oid oid; git_odb *odb; - size_t len; - int err, exists; + int err, len, exists; len = py_str_to_git_oid(value, &oid); - TODO_SUPPORT_SHORT_HEXS(len) - if (len == 0) + if (len < 0) return -1; err = git_repository_odb(&odb, self->repo); @@ -453,7 +477,21 @@ Repository_contains(Repository *self, PyObject *value) return -1; } - exists = git_odb_exists(odb, &oid); + if (len < GIT_OID_HEXSZ) { + git_odb_object *obj = NULL; + err = git_odb_read_prefix(&obj, odb, &oid, len); + if (err < 0 && err != GIT_ENOTFOUND) { + Error_set(err); + exists = -1; + } else { + exists = (err == 0); + if (obj) + git_odb_object_free(obj); + } + } else { + exists = git_odb_exists(odb, &oid); + } + git_odb_free(odb); return exists; } @@ -462,10 +500,10 @@ static PyObject * Repository_getitem(Repository *self, PyObject *value) { git_oid oid; - size_t len; + int len; len = py_str_to_git_oid(value, &oid); - if (len == 0) + if (len < 0) return NULL; return lookup_object_prefix(self, &oid, len, GIT_OBJ_ANY); @@ -499,11 +537,11 @@ Repository_read(Repository *self, PyObject *py_hex) { git_oid oid; git_odb_object *obj; - size_t len; + int len; PyObject* tuple; len = py_str_to_git_oid(py_hex, &oid); - if (len == 0) + if (len < 0) return NULL; obj = Repository_read_raw(self->repo, &oid, len); @@ -612,7 +650,6 @@ Repository_walk(Repository *self, PyObject *args) git_oid oid; git_revwalk *walk; Walker *py_walker; - size_t len; if (!PyArg_ParseTuple(args, "OI", &value, &sort)) return NULL; @@ -626,12 +663,12 @@ Repository_walk(Repository *self, PyObject *args) /* Push */ if (value != Py_None) { - len = py_str_to_git_oid(value, &oid); - TODO_SUPPORT_SHORT_HEXS(len) - if (len == 0) { + err = py_str_to_git_oid_expand(self->repo, value, &oid); + if (err < 0) { git_revwalk_free(walk); - return NULL; + return Error_set(err); } + err = git_revwalk_push(walk, &oid); if (err < 0) { git_revwalk_free(walk); @@ -678,8 +715,7 @@ Repository_create_commit(Repository *self, PyObject *args) git_tree *tree = NULL; int parent_count; git_commit **parents = NULL; - int err = 0, i = 0; - size_t len; + int err = 0, i = 0, len; if (!PyArg_ParseTuple(args, "zO!O!OOO!|s", &update_ref, @@ -692,7 +728,7 @@ Repository_create_commit(Repository *self, PyObject *args) return NULL; len = py_str_to_git_oid(py_oid, &oid); - if (len == 0) + if (len < 0) goto out; message = py_str_to_c_str(py_message, encoding); @@ -714,7 +750,7 @@ Repository_create_commit(Repository *self, PyObject *args) for (; i < parent_count; i++) { py_parent = PyList_GET_ITEM(py_parents, i); len = py_str_to_git_oid(py_parent, &oid); - if (len == 0) + if (len < 0) goto out; if (git_commit_lookup_prefix(&parents[i], self->repo, &oid, (unsigned int)len)) @@ -750,8 +786,7 @@ Repository_create_tag(Repository *self, PyObject *args) char *tag_name, *message; git_oid oid; git_object *target = NULL; - int err, target_type; - size_t len; + int err, target_type, len; if (!PyArg_ParseTuple(args, "sOiO!s", &tag_name, @@ -762,7 +797,7 @@ Repository_create_tag(Repository *self, PyObject *args) return NULL; len = py_str_to_git_oid(py_oid, &oid); - if (len == 0) + if (len < 0) return NULL; err = git_object_lookup_prefix(&target, self->repo, &oid, @@ -842,16 +877,14 @@ Repository_create_reference(Repository *self, PyObject *args) char *c_name; git_oid oid; int err; - size_t len; /* 1- Get the C variables */ if (!PyArg_ParseTuple(args, "sO", &c_name, &py_oid)) return NULL; - len = py_str_to_git_oid(py_oid, &oid); - TODO_SUPPORT_SHORT_HEXS(len) - if (len == 0) - return NULL; + err = py_str_to_git_oid_expand(self->repo, py_oid, &oid); + if (err < 0) + return Error_set(err); /* 2- Create the reference */ err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid, 0); @@ -945,7 +978,6 @@ Repository_TreeBuilder(Repository *self, PyObject *args) TreeBuilder *builder; git_treebuilder *bld; PyObject *py_src = NULL; - size_t oid_len; git_oid oid; git_tree *tree = NULL; int err; @@ -961,10 +993,9 @@ Repository_TreeBuilder(Repository *self, PyObject *args) } tree = py_tree->tree; } else { - oid_len = py_str_to_git_oid(py_src, &oid); - TODO_SUPPORT_SHORT_HEXS(oid_len) - if (oid_len == 0) - return NULL; + err = py_str_to_git_oid_expand(self->repo, py_src, &oid); + if (err < 0) + return NULL; err = git_tree_lookup(&tree, self->repo, &oid); if (err < 0) @@ -2203,12 +2234,10 @@ Index_read_tree(Index *self, PyObject *value) { git_oid oid; git_tree *tree; - size_t len; - int err; + int err, len; len = py_str_to_git_oid(value, &oid); - TODO_SUPPORT_SHORT_HEXS(len) - if (len == 0) + if (len < 0) return NULL; err = git_tree_lookup_prefix(&tree, self->repo->repo, &oid, @@ -2461,12 +2490,11 @@ Walker_hide(Walker *self, PyObject *py_hex) { int err; git_oid oid; - size_t len; - len = py_str_to_git_oid(py_hex, &oid); - TODO_SUPPORT_SHORT_HEXS(len) - if (len == 0) - return NULL; + err = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); + + if (err < 0) + return Error_set(err); err = git_revwalk_hide(self->walk, &oid); if (err < 0) @@ -2480,12 +2508,10 @@ Walker_push(Walker *self, PyObject *py_hex) { int err; git_oid oid; - size_t len; - len = py_str_to_git_oid(py_hex, &oid); - TODO_SUPPORT_SHORT_HEXS(len) - if (len == 0) - return NULL; + err = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); + if (err < 0) + return Error_set(err); err = git_revwalk_push(self->walk, &oid); if (err < 0) @@ -2761,15 +2787,15 @@ Reference_set_oid(Reference *self, PyObject *py_hex) { git_oid oid; int err; - size_t len; CHECK_REFERENCE_INT(self); /* Get the oid */ - len = py_str_to_git_oid(py_hex, &oid); - TODO_SUPPORT_SHORT_HEXS(len) - if (len == 0) + err = py_str_to_git_oid_expand(git_reference_owner(self->reference), py_hex, &oid); + if (err < 0) { + Error_set(err); return -1; + } /* Set the oid */ err = git_reference_set_oid(self->reference, &oid); From 7137f8217e4480e2acee5947c4b63d4c9d9987ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 29 Feb 2012 17:03:31 +0100 Subject: [PATCH 0155/2237] Add tests for the short OID support --- test/test_refs.py | 6 ++++++ test/test_repository.py | 2 ++ test/test_revwalk.py | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/test/test_refs.py b/test/test_refs.py index c96aa9bb2..c28609b3c 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -84,6 +84,12 @@ def test_reference_set_sha(self): reference.oid = NEW_COMMIT self.assertEqual(reference.hex, NEW_COMMIT) + def test_reference_set_sha_prefix(self): + NEW_COMMIT = '5ebeeebb320790caf276b9fc8b24546d63316533' + reference = self.repo.lookup_reference('refs/heads/master') + reference.oid = NEW_COMMIT[0:6] + self.assertEqual(reference.hex, NEW_COMMIT) + def test_reference_get_type(self): reference = self.repo.lookup_reference('refs/heads/master') diff --git a/test/test_repository.py b/test/test_repository.py index f8f5764e8..8fa47755a 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -76,7 +76,9 @@ def test_contains(self): self.assertRaises(TypeError, lambda: 123 in self.repo) self.assertTrue(A_BIN_SHA in self.repo) self.assertTrue(A_HEX_SHA in self.repo) + self.assertTrue(A_HEX_SHA[0:10] in self.repo) self.assertFalse('a' * 40 in self.repo) + self.assertFalse('a' * 20 in self.repo) def test_lookup_blob(self): self.assertRaises(TypeError, lambda: self.repo[123]) diff --git a/test/test_revwalk.py b/test/test_revwalk.py index 34e386648..9cfb0d22b 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -64,6 +64,11 @@ def test_hide(self): walker.hide('4ec4389a8068641da2d6578db0419484972284c8') self.assertEqual(len(list(walker)), 2) + def test_hide_prefix(self): + walker = self.repo.walk(log[0], GIT_SORT_TIME) + walker.hide('4ec4389a') + self.assertEqual(len(list(walker)), 2) + def test_reset(self): walker = self.repo.walk(log[0], GIT_SORT_TIME) walker.reset() From 27585e534434896a404eda728e4ae2cc558c6620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 17 Mar 2012 17:55:41 +0100 Subject: [PATCH 0156/2237] Fixing TreeEntry lifetime issues Now a TreeEntry can be owned by a Tree or a TreeBuilder, this should handle the lifetime issues reported by Han-Wen Nienhuys. Discussion in issue #56 --- pygit2.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/pygit2.c b/pygit2.c index 1b2585107..d0b8877dd 100644 --- a/pygit2.c +++ b/pygit2.c @@ -110,7 +110,12 @@ OBJECT_STRUCT(Blob, git_blob, blob) OBJECT_STRUCT(Tag, git_tag, tag) OBJECT_STRUCT(Index, git_index, index) OBJECT_STRUCT(Walker, git_revwalk, walk) -OBJECT_STRUCT(TreeEntry, git_tree_entry, entry) + +typedef struct { + PyObject_HEAD + PyObject *owner; /* Tree or TreeBuilder */ + const git_tree_entry *entry; +} TreeEntry; typedef struct { PyObject_HEAD @@ -1418,7 +1423,7 @@ static PyTypeObject CommitType = { static void TreeEntry_dealloc(TreeEntry *self) { - Py_XDECREF(self->repo); + Py_XDECREF(self->owner); PyObject_Del(self); } @@ -1453,9 +1458,11 @@ static PyObject * TreeEntry_to_object(TreeEntry *self) { const git_oid *entry_oid; + Repository *repo; + repo = ((Object*)(self->owner))->repo; entry_oid = git_tree_entry_id(self->entry); - return lookup_object(self->repo, entry_oid, GIT_OBJ_ANY); + return lookup_object(repo, entry_oid, GIT_OBJ_ANY); } static PyGetSetDef TreeEntry_getseters[] = { @@ -1533,15 +1540,15 @@ Tree_contains(Tree *self, PyObject *py_name) } static TreeEntry * -wrap_tree_entry(const git_tree_entry *entry, Repository *repo) +wrap_tree_entry(const git_tree_entry *entry, Tree *tree) { TreeEntry *py_entry; py_entry = PyObject_New(TreeEntry, &TreeEntryType); if (py_entry) { py_entry->entry = entry; - py_entry->repo = repo; - Py_INCREF(repo); + py_entry->owner = (PyObject*)tree; + Py_INCREF(tree); } return py_entry; } @@ -1604,7 +1611,7 @@ Tree_getitem_by_index(Tree *self, PyObject *py_index) PyErr_SetObject(PyExc_IndexError, py_index); return NULL; } - return wrap_tree_entry(entry, self->repo); + return wrap_tree_entry(entry, self); } static TreeEntry * @@ -1626,7 +1633,7 @@ Tree_getitem(Tree *self, PyObject *value) PyErr_SetObject(PyExc_KeyError, value); return NULL; } - return wrap_tree_entry(entry, self->repo); + return wrap_tree_entry(entry, self); } static PySequenceMethods Tree_as_sequence = { @@ -1822,7 +1829,7 @@ TreeIter_iternext(TreeIter *self) return NULL; self->i += 1; - return (TreeEntry*)wrap_tree_entry(tree_entry, self->owner->repo); + return (TreeEntry*)wrap_tree_entry(tree_entry, self->owner); } static PyTypeObject TreeIterType = { From bccf3e1e33c558242fd9680beadc538e566fb61d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 19 Mar 2012 14:16:30 +0100 Subject: [PATCH 0157/2237] Release v0.16.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Highlights: - Tag.target now returns the OID, not the object - New method "Repository.status_file(path)" - New function "discover_repository(...)" - Supporting tree-builders - Improving pygit2 on Windows - Various bugs fixed, including a couple of segfaults Thanks to Amit Bakshi, Bryan O'Sullivan, Carlos Martín Nieto, Han-Wen Nienhuys and Yonggang Luo. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6a5ecee67..f604ca0f1 100644 --- a/setup.py +++ b/setup.py @@ -89,7 +89,7 @@ def run(self): setup(name='pygit2', description='Python bindings for libgit2.', keywords='git', - version='0.16.0', + version='0.16.1', url='http://github.com/libgit2/pygit2', classifiers=classifiers, license='GPLv2', From f53091746309620bc7f56f62d883bb3afdf4b539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 29 Mar 2012 16:28:13 +0200 Subject: [PATCH 0158/2237] We don't need zlib/libssl --- README.rst | 5 ----- setup.py | 6 +----- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/README.rst b/README.rst index 9e72c5e03..9333513f1 100644 --- a/README.rst +++ b/README.rst @@ -16,11 +16,6 @@ You can find platform-specific instructions to build the library in the libgit2 http://libgit2.github.com -Next, make sure you have the required library dependencies for pygit2: OpenSSL and ZLib. -For instance, in Debian-based systems run:: - - $ sudo apt-get install zlib1g-dev libssl-dev - Also, make sure you have Python 2.6+ installed together with the Python development headers. When those are installed, you can install pygit2:: diff --git a/setup.py b/setup.py index f604ca0f1..003aeb68f 100644 --- a/setup.py +++ b/setup.py @@ -37,10 +37,6 @@ SETUPTOOLS = False # Use environment variable LIBGIT2 to set your own libgit2 configuration. -libraries = ['git2', 'z', 'crypto'] -if os.name == 'nt': - libraries = ['git2'] - libgit2_path = os.getenv("LIBGIT2") if libgit2_path is None: if os.name == 'nt': @@ -100,6 +96,6 @@ def run(self): Extension('pygit2', ['pygit2.c'], include_dirs=[libgit2_include], library_dirs=[libgit2_lib], - libraries=libraries), + libraries=['git2']), ], **kwargs) From 21a366e049112bbfaac7714c43492d12ac82c80f Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Fri, 30 Mar 2012 00:11:30 -0300 Subject: [PATCH 0159/2237] Change signature of TreeBuilder.insert to take (name, oid, attribute). This is analogous to git_treebuilder_insert(); update tests. --- pygit2.c | 30 ++++++++++++++++++------------ test/test_tree.py | 21 ++++++--------------- test/test_treebuilder.py | 2 +- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/pygit2.c b/pygit2.c index d0b8877dd..046148e05 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1703,21 +1703,27 @@ TreeBuilder_dealloc(TreeBuilder* self) } static PyObject * -TreeBuilder_insert(TreeBuilder *self, TreeEntry *py_tentry) +TreeBuilder_insert(TreeBuilder *self, PyObject *args) { - int err, attr; - const git_oid *oid; + PyObject *py_oid; + int len, err, attr; + git_oid oid; const char *fname; - const git_tree_entry *tentry; - tentry = py_tentry->entry; - fname = git_tree_entry_name(tentry); - oid = git_tree_entry_id(tentry); - attr = git_tree_entry_attributes(tentry); + if (!PyArg_ParseTuple(args, "sOi", &fname, &py_oid, &attr)) { + return NULL; + } - err = git_treebuilder_insert(NULL, self->bld, fname, oid, attr); - if (err < 0) - return Error_set(err); + len = py_str_to_git_oid(py_oid, &oid); + if (len < 0) { + return NULL; + } + + err = git_treebuilder_insert(NULL, self->bld, fname, &oid, attr); + if (err < 0) { + Error_set(err); + return NULL; + } Py_RETURN_NONE; } @@ -1760,7 +1766,7 @@ TreeBuilder_clear(TreeBuilder *self) } static PyMethodDef TreeBuilder_methods[] = { - {"insert", (PyCFunction)TreeBuilder_insert, METH_O, + {"insert", (PyCFunction)TreeBuilder_insert, METH_VARARGS, "Insert or replace an entry in the treebuilder"}, {"write", (PyCFunction)TreeBuilder_write, METH_NOARGS, "Write the tree to the given repository"}, diff --git a/test/test_tree.py b/test/test_tree.py index 6c1a91640..c9f41ccd2 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -1,4 +1,4 @@ -# -*- coding: UTF-8 -*- +# -*- coding: utf-8 -*- # # Copyright 2010 Google, Inc. # @@ -83,24 +83,15 @@ def test_read_subtree(self): # TODO This test worked with libgit2 v0.10.0, update to use the # tree-builder def xtest_new_tree(self): - tree = pygit2.Tree(self.repo) - self.assertEqual(0, len(tree)) - tree.add_entry('1' * 40, 'x', 0o0100644) - tree.add_entry('2' * 40, 'y', 0o0100755) - self.assertEqual(2, len(tree)) + b = self.repo.TreeBuilder() + b.insert('1' * 40, 'x', 0o0100644) + b.insert('2' * 40, 'y', 0o0100755) + tree = self.repo[b.write()] + self.assertTrue('x' in tree) self.assertTrue('y' in tree) self.assertRaisesWithArg(KeyError, '1' * 40, tree['x'].to_object) - tree.add_entry('3' * 40, 'z1', 0o0100644) - tree.add_entry('4' * 40, 'z2', 0o0100644) - self.assertEqual(4, len(tree)) - del tree['z1'] - del tree[2] - self.assertEqual(2, len(tree)) - - self.assertEqual(None, tree.hex) - tree.write() contents = '100644 x\0%s100755 y\0%s' % ('\x11' * 20, '\x22' * 20) self.assertEqual((pygit2.GIT_OBJ_TREE, contents), self.repo.read(tree.hex)) diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index f9628b164..455f4f4ac 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -60,7 +60,7 @@ def test_rebuild_treebuilder(self): tree = self.repo[TREE_SHA] bld = self.repo.TreeBuilder() for e in tree: - bld.insert(e) + bld.insert(e.name, e.hex, e.attributes) result = bld.write() self.assertEqual(tree.oid, result) From 9c74743f2317d0b5dea1aca97f2537af39991502 Mon Sep 17 00:00:00 2001 From: Benjamin Kircher Date: Sat, 31 Mar 2012 20:26:31 +0200 Subject: [PATCH 0160/2237] Fix spelling in docstring --- pygit2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index d0b8877dd..4f3ba3c4f 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1052,7 +1052,7 @@ static PyMethodDef Repository_methods[] = { {"packall_references", (PyCFunction)Repository_packall_references, METH_NOARGS, "Pack all the loose references in the repository."}, {"status", (PyCFunction)Repository_status, METH_NOARGS, "Reads the " - "status of the repository and returns a dictionnary with file paths " + "status of the repository and returns a dictionary with file paths " "as keys and status flags as values.\nSee pygit2.GIT_STATUS_*."}, {"status_file", (PyCFunction)Repository_status_file, METH_O, "Returns the status of the given file path."}, From 907b668fcf40285448dbf7819019dc3ff0d85e37 Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Sat, 7 Apr 2012 11:54:44 +0800 Subject: [PATCH 0161/2237] drop support for setuptools. --- setup.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/setup.py b/setup.py index 003aeb68f..aa5736bca 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,5 @@ # -*- coding: UTF-8 -*- +# coding: utf-8 # # Copyright 2010 Google, Inc. # Copyright 2011 Itaapy @@ -29,12 +30,7 @@ """Setup file for pygit2.""" import os -try: - from setuptools import setup, Extension, Command - SETUPTOOLS = True -except ImportError: - from distutils.core import setup, Extension, Command - SETUPTOOLS = False +from distutils.core import setup, Extension, Command # Use environment variable LIBGIT2 to set your own libgit2 configuration. libgit2_path = os.getenv("LIBGIT2") @@ -66,11 +62,7 @@ def run(self): test.main() -kwargs = {} -if SETUPTOOLS: - kwargs = {'test_suite': 'test.test_suite'} -else: - kwargs = {'cmdclass': {'test': TestCommand}} +kwargs = {'cmdclass': {'test': TestCommand}} classifiers = [ @@ -92,7 +84,7 @@ def run(self): maintainer='J. David Ibáñez', maintainer_email='jdavid.ibp@gmail.com', long_description=long_description, - ext_modules = [ + ext_modules=[ Extension('pygit2', ['pygit2.c'], include_dirs=[libgit2_include], library_dirs=[libgit2_lib], From 81e892b8d5bca94acb63ece4c2729aea0c12e2db Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Sat, 7 Apr 2012 12:34:15 +0800 Subject: [PATCH 0162/2237] copy (lib)git2.dll alongside pygit2 add build_lib into sys path so that unittest can found (lib)git2.dll --- setup.py | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index aa5736bca..3f11868ed 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,10 @@ """Setup file for pygit2.""" import os +import sys +import logging from distutils.core import setup, Extension, Command +from distutils.command.build import build # Use environment variable LIBGIT2 to set your own libgit2 configuration. libgit2_path = os.getenv("LIBGIT2") @@ -58,11 +61,47 @@ def finalize_options(self): def run(self): self.run_command('build') + bld = self.distribution.get_command_obj('build') + sys.path = [os.path.abspath(bld.build_lib)] + sys.path import test test.main() -kwargs = {'cmdclass': {'test': TestCommand}} +class BuildWithDLLs(build): + + # On Windows, we install the git2.dll too. + def _get_dlls(self): + # return a list of of (FQ-in-name, relative-out-name) tuples. + ret = [] + bld_ext = self.distribution.get_command_obj('build_ext') + compiler_type = bld_ext.compiler.compiler_type + libgit2_dlls = [] + if compiler_type == 'msvc': + libgit2_dlls.append('git2.dll') + elif compiler_type == 'mingw32': + libgit2_dlls.append('libgit2.dll') + look_dirs = [libgit2_bin] + os.environ.get("PATH","").split(os.pathsep) + target = os.path.abspath(self.build_lib) + for bin in libgit2_dlls: + for look in look_dirs: + f = os.path.join(look, bin) + if os.path.isfile(f): + ret.append((f, target)) + break + else: + logging.warning("Could not find required DLL %r to copy, (looked in %s)", + bin, look_dirs) + return ret + + def run(self): + build.run(self) + if os.name == 'nt': + # On Windows we package up the dlls with the plugin. + for s, d in self._get_dlls(): + self.copy_file(s, d) + + +kwargs = {'cmdclass': {'test': TestCommand, 'build': BuildWithDLLs}} classifiers = [ From 823dc6ae3b12afeb142ba9e8c5b47c74e772b8a6 Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Sat, 7 Apr 2012 12:42:33 +0800 Subject: [PATCH 0163/2237] # -*- coding: UTF-8 -*- # coding: UTF-8 This is necessary under win32. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3f11868ed..6b5d0ea4e 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ # -*- coding: UTF-8 -*- -# coding: utf-8 +# coding: UTF-8 # # Copyright 2010 Google, Inc. # Copyright 2011 Itaapy From ad39bd7b4d2d4ffdd6536a5c6d7891098dd77a9e Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Sat, 7 Apr 2012 14:04:11 +0800 Subject: [PATCH 0164/2237] use distutils.log instead of logging. Pass args to unittest, so that we can running unittest in such way: python setup.py test --args="test.test_tag.TagTest" --- setup.py | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/setup.py b/setup.py index 6b5d0ea4e..fd32e1c93 100644 --- a/setup.py +++ b/setup.py @@ -31,9 +31,9 @@ import os import sys -import logging from distutils.core import setup, Extension, Command from distutils.command.build import build +from distutils import log # Use environment variable LIBGIT2 to set your own libgit2 configuration. libgit2_path = os.getenv("LIBGIT2") @@ -49,11 +49,14 @@ libgit2_lib = os.path.join(libgit2_path, 'lib') class TestCommand(Command): - """Command for running pygit2 tests.""" + """Command for running unittests without install.""" - user_options = [] + user_options = [("args=", None, '''The command args string passed to + unittest framework, such as + --args="-v -f"''')] def initialize_options(self): + self.args = '' pass def finalize_options(self): @@ -62,9 +65,19 @@ def finalize_options(self): def run(self): self.run_command('build') bld = self.distribution.get_command_obj('build') + #Add build_lib in to sys.path so that unittest can found DLLs and libs sys.path = [os.path.abspath(bld.build_lib)] + sys.path - import test - test.main() + + import shlex + import unittest + test_argv0 = [sys.argv[0] + ' test --args='] + #For transfering args to unittest, we have to split args + #by ourself, so that command like: + #python setup.py test --args="-v -f" + #can be executed, and the parameter '-v -f' can be + #transfering to unittest properly. + test_argv = test_argv0 + shlex.split(self.args) + unittest.main(module=None, defaultTest='test.test_suite', argv=test_argv) class BuildWithDLLs(build): @@ -89,8 +102,8 @@ def _get_dlls(self): ret.append((f, target)) break else: - logging.warning("Could not find required DLL %r to copy, (looked in %s)", - bin, look_dirs) + log.warn("Could not find required DLL %r to include", bin) + log.debug("(looked in %s)", look_dirs) return ret def run(self): @@ -101,8 +114,10 @@ def run(self): self.copy_file(s, d) -kwargs = {'cmdclass': {'test': TestCommand, 'build': BuildWithDLLs}} - +cmdclass = {'test': TestCommand} +if os.name == 'nt': + # BuildWithDLLs can copy external DLLs into source directory. + cmdclass['build'] = BuildWithDLLs classifiers = [ "Development Status :: 3 - Alpha", @@ -129,4 +144,4 @@ def run(self): library_dirs=[libgit2_lib], libraries=['git2']), ], - **kwargs) + cmdclass=cmdclass) From cad9b89919195868fc6144f2eef274fc6a44592d Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Mon, 30 Apr 2012 14:19:15 -0300 Subject: [PATCH 0165/2237] Fix use-after-free problem in py_str_to_c_str(). --- pygit2.c | 60 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/pygit2.c b/pygit2.c index a6d90d045..07af7ece9 100644 --- a/pygit2.c +++ b/pygit2.c @@ -322,10 +322,15 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) if (py_hex == NULL) return -1; err = PyString_AsStringAndSize(py_hex, &hex_or_bin, &len); - Py_DECREF(py_hex); - if (err) + if (err) { + Py_DECREF(py_hex); return -1; + } + err = git_oid_fromstrn(oid, hex_or_bin, len); + + Py_DECREF(py_hex); + if (err < 0) { PyErr_SetObject(Error_type(err), py_str); return -1; @@ -386,24 +391,26 @@ git_oid_to_py_str(const git_oid *oid) return PyUnicode_DecodeASCII(hex, GIT_OID_HEXSZ, "strict"); } +// py_str_to_c_str() returns a newly allocated C string holding +// the string contained in the value argument. char * py_str_to_c_str(PyObject *value, const char *encoding) { - char *c_str; - /* Case 1: byte string */ if (PyString_Check(value)) - return PyString_AsString(value); + return strdup(PyString_AsString(value)); /* Case 2: text string */ if (PyUnicode_Check(value)) { + char *c_str = NULL; + if (encoding == NULL) value = PyUnicode_AsUTF8String(value); else value = PyUnicode_AsEncodedString(value, encoding, "strict"); if (value == NULL) return NULL; - c_str = PyString_AsString(value); + c_str = strdup(PyString_AsString(value)); Py_DECREF(value); return c_str; } @@ -715,7 +722,9 @@ Repository_create_commit(Repository *self, PyObject *args) Signature *py_author, *py_committer; PyObject *py_oid, *py_message, *py_parents, *py_parent; PyObject *py_result = NULL; - char *message, *update_ref, *encoding = NULL; + char *message = NULL; + char *update_ref = NULL; + char *encoding = NULL; git_oid oid; git_tree *tree = NULL; int parent_count; @@ -774,6 +783,7 @@ Repository_create_commit(Repository *self, PyObject *args) py_result = git_oid_to_python(oid.id); out: + free(message); git_tree_free(tree); while (i > 0) { i--; @@ -867,8 +877,11 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) /* 2- Lookup */ err = git_reference_lookup(&c_reference, self->repo, c_name); - if (err < 0) - return Error_set_str(err, c_name); + if (err < 0) { + PyObject *err_obj = Error_set_str(err, c_name); + free(c_name); + return err_obj; + } /* 3- Make an instance of Reference and return it */ return wrap_reference(c_reference); @@ -971,9 +984,11 @@ Repository_status_file(Repository *self, PyObject *value) return NULL; err = git_status_file(&status, self->repo, path); - if (err < 0) - return Error_set_str(err, path); - + if (err < 0) { + PyObject *err_obj = Error_set_str(err, path); + free(path); + return err_obj; + } return PyInt_FromLong(status); } @@ -1530,13 +1545,14 @@ Tree_len(Tree *self) static int Tree_contains(Tree *self, PyObject *py_name) { - char *name; - - name = py_path_to_c_str(py_name); + int result = 0; + char *name = py_path_to_c_str(py_name); if (name == NULL) return -1; - return git_tree_entry_byname(self->tree, name) ? 1 : 0; + result = git_tree_entry_byname(self->tree, name) ? 1 : 0; + free(name); + return result; } static TreeEntry * @@ -1629,6 +1645,7 @@ Tree_getitem(Tree *self, PyObject *value) if (name == NULL) return NULL; entry = git_tree_entry_byname(self->tree, name); + free(name); if (!entry) { PyErr_SetObject(PyExc_KeyError, value); return NULL; @@ -1744,14 +1761,14 @@ TreeBuilder_write(TreeBuilder *self) static PyObject * TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename) { - char *filename; - int err; + char *filename = py_path_to_c_str(py_filename); + int err = 0; - filename = py_path_to_c_str(py_filename); if (filename == NULL) return NULL; err = git_treebuilder_remove(self->bld, filename); + free(filename); if (err < 0) return Error_set(err); @@ -2141,6 +2158,7 @@ Index_get_position(Index *self, PyObject *value) idx = git_index_find(self->index, path); if (idx < 0) { Error_set_str(idx, path); + free(path); return -1; } return idx; @@ -2160,6 +2178,7 @@ Index_contains(Index *self, PyObject *value) return 0; if (idx < 0) { Error_set_str(idx, path); + free(path); return -1; } @@ -2677,6 +2696,7 @@ Reference_rename(Reference *self, PyObject *py_name) /* Rename */ err = git_reference_rename(self->reference, c_name, 0); + free(c_name); if (err < 0) return Error_set(err); @@ -2760,6 +2780,7 @@ Reference_set_target(Reference *self, PyObject *py_name) /* Set the new target */ err = git_reference_set_target(self->reference, c_name); + free(c_name); if (err < 0) { Error_set(err); return -1; @@ -2941,6 +2962,7 @@ Signature_init(Signature *self, PyObject *args, PyObject *kwds) return -1; err = git_signature_new(&signature, name, email, time, offset); + free(name); if (err < 0) { Error_set(err); return -1; From 6caf65915db48ecd801930e54cd97c4e013a2663 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Mon, 30 Apr 2012 15:45:02 -0300 Subject: [PATCH 0166/2237] Fix double free in case of initializing a TreeBuilder with Tree object. --- pygit2.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index 07af7ece9..c39878df5 100644 --- a/pygit2.c +++ b/pygit2.c @@ -1000,6 +1000,7 @@ Repository_TreeBuilder(Repository *self, PyObject *args) PyObject *py_src = NULL; git_oid oid; git_tree *tree = NULL; + git_tree *must_free = NULL; int err; if (!PyArg_ParseTuple(args, "|O", &py_src)) @@ -1020,11 +1021,15 @@ Repository_TreeBuilder(Repository *self, PyObject *args) err = git_tree_lookup(&tree, self->repo, &oid); if (err < 0) return Error_set(err); + must_free = tree; } } err = git_treebuilder_create(&bld, tree); - git_tree_free(tree); + if (must_free != NULL) { + git_tree_free(must_free); + } + if (err < 0) return Error_set(err); From fd4f3cf633d1385562a94ba8b0b8e4f27500f88e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 3 May 2012 14:02:31 +0200 Subject: [PATCH 0167/2237] style: do not use tabs --- pygit2.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pygit2.c b/pygit2.c index c39878df5..39172129e 100644 --- a/pygit2.c +++ b/pygit2.c @@ -190,11 +190,10 @@ Error_type(int err) static const char * git_last_error(void) { - const char *ret; + const char *ret; - ret = git_lasterror(); - - return ret != NULL ? ret : "(No error information given)"; + ret = git_lasterror(); + return ret != NULL ? ret : "(No error information given)"; } static PyObject * @@ -1012,10 +1011,10 @@ Repository_TreeBuilder(Repository *self, PyObject *args) if (py_tree->repo->repo != self->repo) { return Error_set(GIT_EINVALIDARGS); } - tree = py_tree->tree; + tree = py_tree->tree; } else { err = py_str_to_git_oid_expand(self->repo, py_src, &oid); - if (err < 0) + if (err < 0) return NULL; err = git_tree_lookup(&tree, self->repo, &oid); From 2bbf62f50a0a25efb1592ebbb83d741fad8e29dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 3 May 2012 14:05:51 +0200 Subject: [PATCH 0168/2237] Release v0.16.2 Highlights: - Fix serious memory bug - Change signature of 'TreeBuilder.insert' - Improve support of Windows Thanks to Benjamin Kircher, Han-Wen Nienhuys and Yonggang Luo. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fd32e1c93..67fd5ab69 100644 --- a/setup.py +++ b/setup.py @@ -131,7 +131,7 @@ def run(self): setup(name='pygit2', description='Python bindings for libgit2.', keywords='git', - version='0.16.1', + version='0.16.2', url='http://github.com/libgit2/pygit2', classifiers=classifiers, license='GPLv2', From 57dd20c5f0279d22a92f6770af9a1a448a3c831a Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 11 May 2012 11:57:50 +0200 Subject: [PATCH 0169/2237] added create_blob-Method for Repository Use case: Add and commit a blob out of memory for bare repositories --- pygit2.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pygit2.c b/pygit2.c index 39172129e..b6e2ae379 100644 --- a/pygit2.c +++ b/pygit2.c @@ -715,6 +715,25 @@ build_signature(Object *obj, const git_signature *signature, return (PyObject*)py_signature; } +static PyObject * +Repository_create_blob(Repository *self, PyObject *args) +{ + git_oid oid; + const char* raw; + Py_ssize_t size; + int err; + + if (!PyArg_ParseTuple(args, "s#", &raw, &size)) + return NULL; + + err = git_blob_create_frombuffer(&oid, self->repo, (const void*)raw, size); + + if (err < 0) + return Error_set(err); + + return git_oid_to_python(oid.id); +} + static PyObject * Repository_create_commit(Repository *self, PyObject *args) { @@ -1060,6 +1079,9 @@ static PyMethodDef Repository_methods[] = { "Return a list with all the references in the repository."}, {"lookup_reference", (PyCFunction)Repository_lookup_reference, METH_O, "Lookup a reference by its name in a repository."}, + {"create_blob", (PyCFunction)Repository_create_blob, + METH_VARARGS, + "Create a new blob from memory"}, {"create_reference", (PyCFunction)Repository_create_reference, METH_VARARGS, "Create a new reference \"name\" that points to the object given by its " From 632750565b0bb3f3c44ab7fd4d558e6a7111122c Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 11 May 2012 14:03:35 +0200 Subject: [PATCH 0170/2237] added tests for Repository.create_blob() --- test/test_blob.py | 17 +++++++++++++++++ test/utils.py | 9 +++++++++ 2 files changed, 26 insertions(+) diff --git a/test/test_blob.py b/test/test_blob.py index 3c8d8e531..532ddaef6 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -38,6 +38,7 @@ __author__ = 'dborowitz@google.com (Dave Borowitz)' BLOB_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' +BLOB_NEW_CONTENT = 'foo bar\n' class BlobTest(utils.BareRepoTestCase): @@ -52,6 +53,22 @@ def test_read_blob(self): self.assertEqual(b'a contents\n', blob.data) self.assertEqual(b'a contents\n', blob.read_raw()) + def test_create_blob(self): + blob_oid = self.repo.create_blob(BLOB_NEW_CONTENT) + blob = self.repo[blob_oid] + + self.assertTrue(isinstance(blob, pygit2.Blob)) + self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) + + self.assertEqual(blob_oid, blob.oid) + self.assertEqual( + utils.gen_blob_sha1(BLOB_NEW_CONTENT), + utils.oid_to_hex(blob_oid) + ) + + self.assertEqual(BLOB_NEW_CONTENT, blob.data) + self.assertEqual(BLOB_NEW_CONTENT.decode('ascii'), blob.read_raw()) + if __name__ == '__main__': unittest.main() diff --git a/test/utils.py b/test/utils.py index 421f928ac..c956b249f 100644 --- a/test/utils.py +++ b/test/utils.py @@ -34,6 +34,7 @@ import tarfile import tempfile import unittest +import hashlib import pygit2 @@ -50,6 +51,14 @@ def force_rm_handle(remove_path, path, excinfo): def oid_to_hex(oid): return b2a_hex(oid).decode('ascii') +def gen_blob_sha1(data): + m = hashlib.sha1() + + # http://stackoverflow.com/questions/552659/assigning-git-sha1s-without-git + git_sha1_format = 'blob %d\0%s' % (len(data), data) + m.update(git_sha1_format) + + return m.hexdigest().decode('ascii') def rmtree(path): """In Windows a read-only file cannot be removed, and shutil.rmtree fails. From c04ba3ac2bbe3aeba6c392b7e303b2eedc04aead Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 15 May 2012 11:24:03 +0200 Subject: [PATCH 0171/2237] python3 compatibility fixed unicode/byte string issues for python3 --- test/test_blob.py | 4 ++-- test/utils.py | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/test/test_blob.py b/test/test_blob.py index 532ddaef6..86608cca6 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -38,7 +38,7 @@ __author__ = 'dborowitz@google.com (Dave Borowitz)' BLOB_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' -BLOB_NEW_CONTENT = 'foo bar\n' +BLOB_NEW_CONTENT = b'foo bar\n' class BlobTest(utils.BareRepoTestCase): @@ -67,7 +67,7 @@ def test_create_blob(self): ) self.assertEqual(BLOB_NEW_CONTENT, blob.data) - self.assertEqual(BLOB_NEW_CONTENT.decode('ascii'), blob.read_raw()) + self.assertEqual(BLOB_NEW_CONTENT, blob.read_raw()) if __name__ == '__main__': diff --git a/test/utils.py b/test/utils.py index c956b249f..8219bebd5 100644 --- a/test/utils.py +++ b/test/utils.py @@ -52,13 +52,12 @@ def oid_to_hex(oid): return b2a_hex(oid).decode('ascii') def gen_blob_sha1(data): - m = hashlib.sha1() - # http://stackoverflow.com/questions/552659/assigning-git-sha1s-without-git - git_sha1_format = 'blob %d\0%s' % (len(data), data) - m.update(git_sha1_format) + m = hashlib.sha1() + m.update(('blob %d\0' % len(data)).encode()) + m.update(data) - return m.hexdigest().decode('ascii') + return m.hexdigest() def rmtree(path): """In Windows a read-only file cannot be removed, and shutil.rmtree fails. From 8dd0c18c62b01c38ed18a896eb03158bee436492 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 16 May 2012 10:58:52 +0200 Subject: [PATCH 0172/2237] adapting new-error-handling in pygit2 New error handling in libgit2 is now done the posix way. One error code for each expected failure, and a generic error code for all critical ones. The last error can be accessed by giterr_last(). It returns a pointer to the global error struct. For more information see docs/error-handling.md in libgit2. --- pygit2.c | 78 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/pygit2.c b/pygit2.c index 39172129e..dc6c827f3 100644 --- a/pygit2.c +++ b/pygit2.c @@ -165,46 +165,62 @@ static PyTypeObject SignatureType; static PyObject *GitError; static PyObject * -Error_type(int err) +Error_type(int type) { - switch (err) { + // Expected + switch (type) { + /** Input does not exist in the scope searched. */ case GIT_ENOTFOUND: return PyExc_KeyError; - case GIT_EOSERR: - return PyExc_OSError; - case GIT_ENOTOID: + + /** A reference with this name already exists */ + case GIT_EEXISTS: return PyExc_ValueError; - case GIT_ENOMEM: - return PyExc_MemoryError; - case GIT_EREVWALKOVER: + + /** The given short oid is ambiguous */ + case GIT_EAMBIGUOUS: + return PyExc_ValueError; + + /** The buffer is too short to satisfy the request */ + case GIT_EBUFS: + return PyExc_ValueError; + + /** Skip and passthrough the given ODB backend */ + case GIT_PASSTHROUGH: + return GitError; + + /** No entries left in ref walker */ + case GIT_REVWALKOVER: return PyExc_StopIteration; + } + + // Critical + const git_error* error = giterr_last(); + switch (error->klass) { + case GITERR_NOMEMORY: + return PyExc_MemoryError; + case GITERR_OS: + return PyExc_OSError; + case GITERR_INVALID: + return PyExc_ValueError; default: return GitError; } } -/* - * Python doesn't like it when the error string is NULL. Not giving - * back an error string could be a bug in the library - */ -static const char * -git_last_error(void) -{ - const char *ret; - - ret = git_lasterror(); - return ret != NULL ? ret : "(No error information given)"; -} static PyObject * Error_set(int err) { assert(err < 0); - if (err == GIT_EOSERR) - return PyErr_SetFromErrno(GitError); + if(err != GIT_ERROR) { //expected failure + PyErr_SetNone(Error_type(err)); + } else { //critical failure + const git_error* error = giterr_last(); + PyErr_SetString(Error_type(err), error->message); + } - PyErr_SetString(Error_type(err), git_last_error()); return NULL; } @@ -217,7 +233,8 @@ Error_set_str(int err, const char *str) return NULL; } - return PyErr_Format(Error_type(err), "%s: %s", str, git_last_error()); + const git_error* error = giterr_last(); + return PyErr_Format(Error_type(err), "%s: %s", str, error->message); } static PyObject * @@ -838,7 +855,7 @@ Repository_listall_references(Repository *self, PyObject *args) return NULL; /* 2- Get the C result */ - err = git_reference_listall(&c_result, self->repo, list_flags); + err = git_reference_list(&c_result, self->repo, list_flags); if (err < 0) return Error_set(err); @@ -957,7 +974,7 @@ read_status_cb(const char *path, unsigned int status_flags, void *payload) flags = PyInt_FromLong((long) status_flags); PyDict_SetItemString(payload, path, flags); - return GIT_SUCCESS; + return GIT_OK; } static PyObject * @@ -1009,7 +1026,8 @@ Repository_TreeBuilder(Repository *self, PyObject *args) if (PyObject_TypeCheck(py_src, &TreeType)) { Tree *py_tree = (Tree *)py_src; if (py_tree->repo->repo != self->repo) { - return Error_set(GIT_EINVALIDARGS); + //return Error_set(GIT_EINVALIDARGS); + return Error_set(GIT_ERROR); } tree = py_tree->tree; } else { @@ -2118,7 +2136,7 @@ Index_read(Index *self) int err; err = git_index_read(self->index); - if (err < GIT_SUCCESS) + if (err < GIT_OK) return Error_set(err); Py_RETURN_NONE; @@ -2130,7 +2148,7 @@ Index_write(Index *self) int err; err = git_index_write(self->index); - if (err < GIT_SUCCESS) + if (err < GIT_OK) return Error_set(err); Py_RETURN_NONE; @@ -2868,7 +2886,7 @@ Reference_get_hex(Reference *self) static PyObject * Reference_get_type(Reference *self) { - git_rtype c_type; + git_ref_t c_type; CHECK_REFERENCE(self); c_type = git_reference_type(self->reference); From 6825bba8c0a3e7d99e1f5cbe5ec80ef1b534fa7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 21 May 2012 18:11:38 +0200 Subject: [PATCH 0173/2237] Release v0.17.0 Changes: - Update to libgit2 v0.17.0 - New method 'Repository.create_blob' Thanks to Nico von Geyso. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 67fd5ab69..de76973ae 100644 --- a/setup.py +++ b/setup.py @@ -131,7 +131,7 @@ def run(self): setup(name='pygit2', description='Python bindings for libgit2.', keywords='git', - version='0.16.2', + version='0.17.0', url='http://github.com/libgit2/pygit2', classifiers=classifiers, license='GPLv2', From d62b8c590d7a525cb5af084f0380f407663314fe Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 24 May 2012 19:07:32 +0200 Subject: [PATCH 0174/2237] basic diff implementation It is now possible to get a diff between two trees: * new diff-method for Tree-Object * new Diff and Hunk Objects --- pygit2.c | 371 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 370 insertions(+), 1 deletion(-) diff --git a/pygit2.c b/pygit2.c index cb295eb3a..f1d77d67e 100644 --- a/pygit2.c +++ b/pygit2.c @@ -30,6 +30,7 @@ #include #include #include +#include /* Python 3 support */ #if PY_MAJOR_VERSION >= 3 @@ -111,6 +112,23 @@ OBJECT_STRUCT(Tag, git_tag, tag) OBJECT_STRUCT(Index, git_index, index) OBJECT_STRUCT(Walker, git_revwalk, walk) +typedef struct { + PyObject_HEAD + Repository *repo; + Tree *t0; + Tree *t1; +} Diff; + +typedef struct { + PyObject_HEAD + int old_start; + int old_lines; + int new_start; + int new_lines; + PyObject *old_data; + PyObject *new_data; +} Hunk; + typedef struct { PyObject_HEAD PyObject *owner; /* Tree or TreeBuilder */ @@ -149,6 +167,8 @@ typedef struct { static PyTypeObject RepositoryType; static PyTypeObject ObjectType; static PyTypeObject CommitType; +static PyTypeObject DiffType; +static PyTypeObject HunkType; static PyTypeObject TreeType; static PyTypeObject TreeBuilderType; static PyTypeObject TreeEntryType; @@ -1697,6 +1717,337 @@ Tree_getitem(Tree *self, PyObject *value) return wrap_tree_entry(entry, self); } +static int diff_data_cb( + void *cb_data, + git_diff_delta *delta, + git_diff_range *range, + char usage, + const char *line, + size_t line_len) +{ + PyObject *hunks, *tmp; + Hunk *hunk; + Py_ssize_t size; + + hunks = PyDict_GetItemString(cb_data, "hunks"); + if(hunks == NULL) + return -1; + + size = PyList_Size(hunks); + hunk = (Hunk*) PyList_GetItem(hunks, size-1); + if(hunk == NULL) + return -1; + + tmp = PyBytes_FromStringAndSize(line, line_len); + + if(usage != GIT_DIFF_LINE_DELETION) + PyBytes_Concat(&hunk->new_data, tmp); + + if(usage != GIT_DIFF_LINE_ADDITION) + PyBytes_Concat(&hunk->old_data, tmp); + + return 0; +} + +static int diff_hunk_cb( + void *cb_data, + git_diff_delta *delta, + git_diff_range *range, + const char *header, + size_t header_len) +{ + PyObject *hunks, *entry; + Hunk *hunk; + + hunks = PyDict_GetItemString(cb_data, "hunks"); + if(hunks == NULL) { + hunks = PyList_New(0); + PyDict_SetItemString(cb_data, "hunks", hunks); + } + + hunk = (Hunk*) PyType_GenericNew(&HunkType, NULL, NULL); + + hunk->old_start = range->old_start; + hunk->old_lines = range->old_lines; + hunk->new_start = range->new_start; + hunk->new_lines = range->new_lines; + +#if PY_MAJOR_VERSION >= 3 + hunk->old_data = Py_BuildValue("y", ""); + hunk->new_data = Py_BuildValue("y", ""); +#else + hunk->old_data = Py_BuildValue("s", ""); + hunk->new_data = Py_BuildValue("s", ""); +#endif + + PyList_Append(hunks, (PyObject*) hunk); + + return 0; +}; + +static int diff_file_cb(void *cb_data, git_diff_delta *delta, float progress) +{ + PyObject *files, *file; + + files = PyDict_GetItemString(cb_data, "files"); + if(files == NULL) { + files = PyList_New(0); + PyDict_SetItemString(cb_data, "files", files); + } + + file = Py_BuildValue("(s,s,i)", + delta->old_file.path, + delta->new_file.path, + delta->status + ); + + PyList_Append(files, file); + + return 0; +} + +static PyObject * +Diff_changes(Diff *self) +{ + git_diff_options opts = {0}; + git_diff_list *changes; + int err; + + err = git_diff_tree_to_tree( + self->repo->repo, + &opts, + self->t0->tree, + self->t1->tree, + &changes); + + if(err < 0) { + Error_set(err); + return NULL; + } + + PyObject *payload; + payload = PyDict_New(); + + git_diff_foreach( + changes, + payload, + &diff_file_cb, + &diff_hunk_cb, + &diff_data_cb + ); + git_diff_list_free(changes); + + return payload; +} + +int diff_print_cb( + void *cb_data, + git_diff_delta *delta, + git_diff_range *range, + char usage, + const char *line, + size_t line_len) +{ + PyObject *data = PyBytes_FromStringAndSize(line, line_len); + PyBytes_ConcatAndDel((PyObject**) cb_data, data); + + return 0; +} + + +static PyObject * +Diff_patch(Diff *self) +{ + git_diff_options opts = {0}; + git_diff_list *changes; + int err; + + err = git_diff_tree_to_tree( + self->repo->repo, + &opts, + self->t0->tree, + self->t1->tree, + &changes); + + if(err < 0) { + Error_set(err); + return NULL; + } + + PyObject *patch = PyBytes_FromString(""); + + git_diff_print_patch(changes, &diff, &diff_print_cb); + + return patch; +} + +static int +Hunk_init(Hunk *self, PyObject *args, PyObject *kwds) +{ + self->old_start = 0; + self->old_lines = 0; + + self->new_start = 0; + self->new_lines = 0; + + self->old_data = PyString_FromString(""); + if (self->old_data == NULL) { + Py_DECREF(self); + return -1; + } + + self->new_data = PyString_FromString(""); + if (self->new_data == NULL) { + Py_DECREF(self); + return -1; + } + + return 0; +} + +static void +Hunk_dealloc(Hunk *self) +{ + Py_XDECREF(self->old_data); + Py_XDECREF(self->new_data); + PyObject_Del(self); +} + +static PyMemberDef Hunk_members[] = { + {"old_start", T_INT, offsetof(Hunk, old_start), 0, "old start"}, + {"old_lines", T_INT, offsetof(Hunk, old_lines), 0, "old lines"}, + {"old_data", T_OBJECT, offsetof(Hunk, old_data), 0, "old data"}, + {"new_start", T_INT, offsetof(Hunk, new_start), 0, "new start"}, + {"new_lines", T_INT, offsetof(Hunk, new_lines), 0, "new lines"}, + {"new_data", T_OBJECT, offsetof(Hunk, new_data), 0, "new data"}, + {NULL} +}; + +static PyTypeObject HunkType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Hunk", /* tp_name */ + sizeof(Hunk), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Hunk_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Hunk object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + Hunk_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Hunk_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +static void +Diff_dealloc(Diff *self) +{ + Py_XDECREF(self->repo); + Py_XDECREF(self->t0); + Py_XDECREF(self->t1); + PyObject_Del(self); +} + + +static PyGetSetDef Diff_getseters[] = { + {"changes", (getter)Diff_changes, NULL, "raw changes", NULL}, + {"patch", (getter)Diff_patch, NULL, "patch", NULL}, + {NULL} +}; + + +static PyTypeObject DiffType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Diff", /* tp_name */ + sizeof(Diff), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Diff_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Diff objects", /* 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 */ + Diff_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 */ +}; + +static PyObject * +Tree_diff_tree(Tree *self, PyObject *args) +{ + Diff *py_diff; + Tree *py_tree; + + if (!PyArg_ParseTuple(args, "O!", &TreeType, &py_tree)) { + return NULL; + } + + py_diff = PyObject_New(Diff, &DiffType); + if (py_diff) { + Py_INCREF(py_diff); + Py_INCREF(self->repo); + Py_INCREF(py_tree); + Py_INCREF(self); + + py_diff->repo = self->repo; + py_diff->t0 = self; + py_diff->t1 = py_tree; + } + + return (PyObject*) py_diff; +} + static PySequenceMethods Tree_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ @@ -1714,6 +2065,12 @@ static PyMappingMethods Tree_as_mapping = { 0, /* mp_ass_subscript */ }; +static PyMethodDef Tree_methods[] = { + {"diff", (PyCFunction)Tree_diff_tree, METH_VARARGS, + "Diff two trees."}, + {NULL} +}; + static PyTypeObject TreeType = { PyVarObject_HEAD_INIT(NULL, 0) "pygit2.Tree", /* tp_name */ @@ -1742,7 +2099,7 @@ static PyTypeObject TreeType = { 0, /* tp_weaklistoffset */ (getiterfunc)Tree_iter, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + Tree_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ @@ -3223,6 +3580,12 @@ moduleinit(PyObject* m) if (PyType_Ready(&TagType) < 0) return NULL; + DiffType.tp_base = &ObjectType; + if (PyType_Ready(&DiffType) < 0) + return NULL; + HunkType.tp_base = &ObjectType; + if (PyType_Ready(&HunkType) < 0) + return NULL; TreeEntryType.tp_new = PyType_GenericNew; if (PyType_Ready(&TreeEntryType) < 0) return NULL; @@ -3260,6 +3623,12 @@ moduleinit(PyObject* m) Py_INCREF(&TreeEntryType); PyModule_AddObject(m, "TreeEntry", (PyObject *)&TreeEntryType); + Py_INCREF(&DiffType); + PyModule_AddObject(m, "Diff", (PyObject *)&DiffType); + + Py_INCREF(&HunkType); + PyModule_AddObject(m, "Hunk", (PyObject *)&HunkType); + Py_INCREF(&TreeType); PyModule_AddObject(m, "Tree", (PyObject *)&TreeType); From 2a49eadc57285e6f7d7577c729e41ac987df3128 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 24 May 2012 19:10:00 +0200 Subject: [PATCH 0175/2237] added some tests for diff --- test/__init__.py | 2 +- test/test_diff.py | 94 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 test/test_diff.py diff --git a/test/__init__.py b/test/__init__.py index 62823aea6..fcb82c572 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -36,7 +36,7 @@ names = ['blob', 'commit', 'index', 'refs', 'repository', 'revwalk', 'tag', - 'tree', 'signature', 'status', 'treebuilder'] + 'tree', 'signature', 'status', 'treebuilder', 'diff'] def test_suite(): modules = ['test.test_%s' % n for n in names] return unittest.defaultTestLoader.loadTestsFromNames(modules) diff --git a/test/test_diff.py b/test/test_diff.py new file mode 100644 index 000000000..cc7e0c711 --- /dev/null +++ b/test/test_diff.py @@ -0,0 +1,94 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2012 Nico von Geyso +# +# 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. + +"""Tests for Diff objects.""" + +from __future__ import absolute_import +from __future__ import unicode_literals +import unittest + +import pygit2 +from . import utils + +__author__ = 'Nico.Geyso@FU-Berlin.de (Nico von Geyso)' + + +COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10' +COMMIT_SHA1_2 = 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c' +PATCH = b"""diff --git a/a b/a +index 7f129fd..af431f2 100644 +--- a/a ++++ b/a +@@ -1 +1 @@ +-a contents 2 ++a contents +diff --git a/c/d b/c/d +deleted file mode 100644 +index 297efb8..0000000 +--- a/c/d ++++ /dev/null +@@ -1 +0,0 @@ +-c/d contents +""" + + +class DiffTest(utils.BareRepoTestCase): + + def test_diff_invalid(self): + commit_a = self.repo[COMMIT_SHA1_1] + commit_b = self.repo[COMMIT_SHA1_2] + self.assertRaises(TypeError, commit_a.tree.diff, commit_b) + + def test_diff_tree(self): + commit_a = self.repo[COMMIT_SHA1_1] + commit_b = self.repo[COMMIT_SHA1_2] + + diff = commit_a.tree.diff(commit_b.tree) + + self.assertIsNotNone(diff) + self.assertIn(('a','a', 3), diff.changes['files']) + self.assertEqual(2, len(diff.changes['hunks'])) + + hunk = diff.changes['hunks'][0] + self.assertEqual(hunk.old_start, 1) + self.assertEqual(hunk.old_lines, 0) + self.assertEqual(hunk.new_start, 1) + self.assertEqual(hunk.new_lines, 0) + + self.assertEqual(hunk.old_data, b'a contents 2\n') + self.assertEqual(hunk.new_data, b'a contents\n') + + def test_diff_patch(self): + commit_a = self.repo[COMMIT_SHA1_1] + commit_b = self.repo[COMMIT_SHA1_2] + + diff = commit_a.tree.diff(commit_b.tree) + self.assertEqual(diff.patch, PATCH) + + +if __name__ == '__main__': + unittest.main() From aec936c94e80f77ac391c0e136a2619345b4719a Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 24 May 2012 19:17:35 +0200 Subject: [PATCH 0176/2237] removed redundant repo entry in DiffObject --- pygit2.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pygit2.c b/pygit2.c index f1d77d67e..ee3e81bbd 100644 --- a/pygit2.c +++ b/pygit2.c @@ -114,7 +114,6 @@ OBJECT_STRUCT(Walker, git_revwalk, walk) typedef struct { PyObject_HEAD - Repository *repo; Tree *t0; Tree *t1; } Diff; @@ -1756,7 +1755,7 @@ static int diff_hunk_cb( const char *header, size_t header_len) { - PyObject *hunks, *entry; + PyObject *hunks; Hunk *hunk; hunks = PyDict_GetItemString(cb_data, "hunks"); @@ -1814,7 +1813,7 @@ Diff_changes(Diff *self) int err; err = git_diff_tree_to_tree( - self->repo->repo, + self->t0->repo->repo, &opts, self->t0->tree, self->t1->tree, @@ -1863,7 +1862,7 @@ Diff_patch(Diff *self) int err; err = git_diff_tree_to_tree( - self->repo->repo, + self->t0->repo->repo, &opts, self->t0->tree, self->t1->tree, @@ -1876,7 +1875,7 @@ Diff_patch(Diff *self) PyObject *patch = PyBytes_FromString(""); - git_diff_print_patch(changes, &diff, &diff_print_cb); + git_diff_print_patch(changes, &patch, &diff_print_cb); return patch; } @@ -1968,7 +1967,6 @@ static PyTypeObject HunkType = { static void Diff_dealloc(Diff *self) { - Py_XDECREF(self->repo); Py_XDECREF(self->t0); Py_XDECREF(self->t1); PyObject_Del(self); @@ -2036,11 +2034,9 @@ Tree_diff_tree(Tree *self, PyObject *args) py_diff = PyObject_New(Diff, &DiffType); if (py_diff) { Py_INCREF(py_diff); - Py_INCREF(self->repo); Py_INCREF(py_tree); Py_INCREF(self); - py_diff->repo = self->repo; py_diff->t0 = self; py_diff->t1 = py_tree; } From 3bb00107d211675013363956370f386b415fd5c1 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 25 May 2012 10:35:39 +0200 Subject: [PATCH 0177/2237] added old and new file path for hunks --- pygit2.c | 17 +++++++++++++++++ test/test_diff.py | 3 +++ 2 files changed, 20 insertions(+) diff --git a/pygit2.c b/pygit2.c index ee3e81bbd..b5f3843c6 100644 --- a/pygit2.c +++ b/pygit2.c @@ -122,8 +122,10 @@ typedef struct { PyObject_HEAD int old_start; int old_lines; + char* old_file; int new_start; int new_lines; + char* new_file; PyObject *old_data; PyObject *new_data; } Hunk; @@ -1771,6 +1773,19 @@ static int diff_hunk_cb( hunk->new_start = range->new_start; hunk->new_lines = range->new_lines; + int len; + char* old_path, *new_path; + + len = strlen(delta->old_file.path) + 1; + old_path = malloc(sizeof(char) * len); + memcpy(old_path, delta->old_file.path, len); + hunk->old_file = old_path; + + len = strlen(delta->new_file.path) + 1; + new_path = malloc(sizeof(char) * len); + memcpy(new_path, delta->new_file.path, len); + hunk->new_file = new_path; + #if PY_MAJOR_VERSION >= 3 hunk->old_data = Py_BuildValue("y", ""); hunk->new_data = Py_BuildValue("y", ""); @@ -1915,9 +1930,11 @@ Hunk_dealloc(Hunk *self) static PyMemberDef Hunk_members[] = { {"old_start", T_INT, offsetof(Hunk, old_start), 0, "old start"}, {"old_lines", T_INT, offsetof(Hunk, old_lines), 0, "old lines"}, + {"old_file", T_STRING, offsetof(Hunk, old_file), 0, "old file"}, {"old_data", T_OBJECT, offsetof(Hunk, old_data), 0, "old data"}, {"new_start", T_INT, offsetof(Hunk, new_start), 0, "new start"}, {"new_lines", T_INT, offsetof(Hunk, new_lines), 0, "new lines"}, + {"new_file", T_STRING, offsetof(Hunk, new_file), 0, "old file"}, {"new_data", T_OBJECT, offsetof(Hunk, new_data), 0, "new data"}, {NULL} }; diff --git a/test/test_diff.py b/test/test_diff.py index cc7e0c711..fadaff22d 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -79,6 +79,9 @@ def test_diff_tree(self): self.assertEqual(hunk.new_start, 1) self.assertEqual(hunk.new_lines, 0) + self.assertEqual(hunk.old_file, 'a') + self.assertEqual(hunk.new_file, 'a') + self.assertEqual(hunk.old_data, b'a contents 2\n') self.assertEqual(hunk.new_data, b'a contents\n') From 6b1a281edcb13fdf40697512895e8d4315fd6374 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 25 May 2012 13:59:59 +0200 Subject: [PATCH 0178/2237] refactoring pygit2.c into several small files The whole extension was organised in one big 3727 lines monster. This refactoring is splitting these 3727 lines into several smaller files. --- include/pygit2/commit.h | 15 + include/pygit2/error.h | 12 + include/pygit2/index.h | 19 + include/pygit2/object.h | 13 + include/pygit2/oid.h | 15 + include/pygit2/reference.h | 18 + include/pygit2/repository.h | 34 + include/pygit2/signature.h | 17 + include/pygit2/tag.h | 14 + include/pygit2/tree.h | 21 + include/pygit2/types.h | 72 + include/pygit2/utils.h | 69 + include/pygit2/walker.h | 16 + pygit2.c | 3345 ----------------------------------- setup.py | 8 +- src/pygit2.c | 261 +++ src/pygit2/blob.c | 49 + src/pygit2/commit.c | 186 ++ src/pygit2/error.c | 82 + src/pygit2/index.c | 492 ++++++ src/pygit2/object.c | 119 ++ src/pygit2/oid.c | 95 + src/pygit2/reference.c | 300 ++++ src/pygit2/repository.c | 806 +++++++++ src/pygit2/signature.c | 179 ++ src/pygit2/tag.c | 102 ++ src/pygit2/tree.c | 462 +++++ src/pygit2/utils.c | 36 + src/pygit2/walker.c | 157 ++ 29 files changed, 3667 insertions(+), 3347 deletions(-) create mode 100644 include/pygit2/commit.h create mode 100644 include/pygit2/error.h create mode 100644 include/pygit2/index.h create mode 100644 include/pygit2/object.h create mode 100644 include/pygit2/oid.h create mode 100644 include/pygit2/reference.h create mode 100644 include/pygit2/repository.h create mode 100644 include/pygit2/signature.h create mode 100644 include/pygit2/tag.h create mode 100644 include/pygit2/tree.h create mode 100644 include/pygit2/types.h create mode 100644 include/pygit2/utils.h create mode 100644 include/pygit2/walker.h delete mode 100644 pygit2.c create mode 100644 src/pygit2.c create mode 100644 src/pygit2/blob.c create mode 100644 src/pygit2/commit.c create mode 100644 src/pygit2/error.c create mode 100644 src/pygit2/index.c create mode 100644 src/pygit2/object.c create mode 100644 src/pygit2/oid.c create mode 100644 src/pygit2/reference.c create mode 100644 src/pygit2/repository.c create mode 100644 src/pygit2/signature.c create mode 100644 src/pygit2/tag.c create mode 100644 src/pygit2/tree.c create mode 100644 src/pygit2/utils.c create mode 100644 src/pygit2/walker.c diff --git a/include/pygit2/commit.h b/include/pygit2/commit.h new file mode 100644 index 000000000..f58630500 --- /dev/null +++ b/include/pygit2/commit.h @@ -0,0 +1,15 @@ +#ifndef INCLUDE_pygit2_commit_h +#define INCLUDE_pygit2_commit_h + +#include +#include + +PyObject* Commit_get_message_encoding(Commit *commit); +PyObject* Commit_get_message(Commit *commit); +PyObject* Commit_get_raw_message(Commit *commit); +PyObject* Commit_get_commit_time(Commit *commit); +PyObject* Commit_get_commit_time_offset(Commit *commit); +PyObject* Commit_get_committer(Commit *self); +PyObject* Commit_get_author(Commit *self); + +#endif diff --git a/include/pygit2/error.h b/include/pygit2/error.h new file mode 100644 index 000000000..cd336dbf2 --- /dev/null +++ b/include/pygit2/error.h @@ -0,0 +1,12 @@ +#ifndef INCLUDE_pygit2_error_h +#define INCLUDE_pygit2_error_h + +#include +#include + +PyObject* Error_type(int type); +PyObject* Error_set(int err); +PyObject* Error_set_str(int err, const char *str); +PyObject* Error_set_oid(int err, const git_oid *oid, size_t len); + +#endif diff --git a/include/pygit2/index.h b/include/pygit2/index.h new file mode 100644 index 000000000..0f072365a --- /dev/null +++ b/include/pygit2/index.h @@ -0,0 +1,19 @@ +#ifndef INCLUDE_pygit2_index_h +#define INCLUDE_pygit2_index_h + +#include +#include + +PyObject* Index_add(Index *self, PyObject *args); +PyObject* Index_clear(Index *self); +PyObject* Index_find(Index *self, PyObject *py_path); +PyObject* Index_read(Index *self); +PyObject* Index_write(Index *self); +PyObject* Index_iter(Index *self); +PyObject* Index_getitem(Index *self, PyObject *value); +PyObject* Index_read_tree(Index *self, PyObject *value); +PyObject* Index_write_tree(Index *self); +Py_ssize_t Index_len(Index *self); +int Index_setitem(Index *self, PyObject *key, PyObject *value); + +#endif diff --git a/include/pygit2/object.h b/include/pygit2/object.h new file mode 100644 index 000000000..513679ee8 --- /dev/null +++ b/include/pygit2/object.h @@ -0,0 +1,13 @@ +#ifndef INCLUDE_pygit2_object_h +#define INCLUDE_pygit2_object_h + +#include +#include +#include + +PyObject* Object_get_oid(Object *self); +PyObject* Object_get_hex(Object *self); +PyObject* Object_get_type(Object *self); +PyObject* Object_read_raw(Object *self); + +#endif diff --git a/include/pygit2/oid.h b/include/pygit2/oid.h new file mode 100644 index 000000000..d9a758ba5 --- /dev/null +++ b/include/pygit2/oid.h @@ -0,0 +1,15 @@ +#ifndef INCLUDE_pygit2_oid_h +#define INCLUDE_pygit2_oid_h + +#include +#include + +int py_str_to_git_oid(PyObject *py_str, git_oid *oid); +int py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, + git_oid *oid); +PyObject* git_oid_to_py_str(const git_oid *oid); + +#define git_oid_to_python(id) \ + PyString_FromStringAndSize((const char*)id, GIT_OID_RAWSZ) + +#endif diff --git a/include/pygit2/reference.h b/include/pygit2/reference.h new file mode 100644 index 000000000..7b3d1793b --- /dev/null +++ b/include/pygit2/reference.h @@ -0,0 +1,18 @@ +#ifndef INCLUDE_pygit2_reference_h +#define INCLUDE_pygit2_reference_h + +#include +#include + +PyObject* Reference_delete(Reference *self, PyObject *args); +PyObject* Reference_rename(Reference *self, PyObject *py_name); +PyObject* Reference_reload(Reference *self); +PyObject* Reference_resolve(Reference *self, PyObject *args); +PyObject* Reference_get_target(Reference *self); +PyObject* Reference_get_name(Reference *self); +PyObject* Reference_get_oid(Reference *self); +PyObject* Reference_get_hex(Reference *self); +PyObject* Reference_get_type(Reference *self); +PyObject* wrap_reference(git_reference * c_reference); + +#endif diff --git a/include/pygit2/repository.h b/include/pygit2/repository.h new file mode 100644 index 000000000..8cec9103c --- /dev/null +++ b/include/pygit2/repository.h @@ -0,0 +1,34 @@ +#ifndef INCLUDE_pygit2_repository_h +#define INCLUDE_pygit2_repository_h + +#include +#include +#include + +int Repository_init(Repository *self, PyObject *args, PyObject *kwds); +int Repository_traverse(Repository *self, visitproc visit, void *arg); +int Repository_clear(Repository *self); +int Repository_contains(Repository *self, PyObject *value); + +git_odb_object* Repository_read_raw(git_repository *repo, const git_oid *oid, size_t len); + +PyObject* Repository_getitem(Repository *self, PyObject *value); +PyObject* Repository_read(Repository *self, PyObject *py_hex); +PyObject* Repository_write(Repository *self, PyObject *args); +PyObject* Repository_get_index(Repository *self, void *closure); +PyObject* Repository_get_path(Repository *self, void *closure); +PyObject* Repository_get_workdir(Repository *self, void *closure); +PyObject* Repository_walk(Repository *self, PyObject *args); +PyObject* Repository_create_blob(Repository *self, PyObject *args); +PyObject* Repository_create_commit(Repository *self, PyObject *args); +PyObject* Repository_create_tag(Repository *self, PyObject *args); +PyObject* Repository_listall_references(Repository *self, PyObject *args); +PyObject* Repository_lookup_reference(Repository *self, PyObject *py_name); +PyObject* Repository_create_reference(Repository *self, PyObject *args); +PyObject* Repository_create_symbolic_reference(Repository *self, PyObject *args); +PyObject* Repository_packall_references(Repository *self, PyObject *args); +PyObject* Repository_status(Repository *self, PyObject *args); +PyObject* Repository_status_file(Repository *self, PyObject *value); +PyObject* Repository_TreeBuilder(Repository *self, PyObject *args); + +#endif diff --git a/include/pygit2/signature.h b/include/pygit2/signature.h new file mode 100644 index 000000000..942e67d01 --- /dev/null +++ b/include/pygit2/signature.h @@ -0,0 +1,17 @@ +#ifndef INCLUDE_pygit2_signature_h +#define INCLUDE_pygit2_signature_h + +#include +#include +#include + +PyObject* Signature_get_encoding(Signature *self); +PyObject* Signature_get_raw_name(Signature *self); +PyObject* Signature_get_raw_email(Signature *self); +PyObject* Signature_get_name(Signature *self); +PyObject* Signature_get_email(Signature *self); +PyObject* Signature_get_time(Signature *self); +PyObject* Signature_get_offset(Signature *self); +PyObject* build_signature(Object *obj, const git_signature *signature, const char *encoding); + +#endif diff --git a/include/pygit2/tag.h b/include/pygit2/tag.h new file mode 100644 index 000000000..b0395a424 --- /dev/null +++ b/include/pygit2/tag.h @@ -0,0 +1,14 @@ +#ifndef INCLUDE_pygit2_tag_h +#define INCLUDE_pygit2_tag_h + +#include +#include +#include + +PyObject* Tag_get_target(Tag *self); +PyObject* Tag_get_name(Tag *self); +PyObject* Tag_get_tagger(Tag *self); +PyObject* Tag_get_message(Tag *self); +PyObject* Tag_get_raw_message(Tag *self); + +#endif diff --git a/include/pygit2/tree.h b/include/pygit2/tree.h new file mode 100644 index 000000000..97a42efbf --- /dev/null +++ b/include/pygit2/tree.h @@ -0,0 +1,21 @@ +#ifndef INCLUDE_pygit2_tree_h +#define INCLUDE_pygit2_tree_h + +#include +#include +#include + +PyObject* TreeEntry_get_attributes(TreeEntry *self); +PyObject* TreeEntry_get_name(TreeEntry *self); +PyObject* TreeEntry_get_oid(TreeEntry *self); +PyObject* TreeEntry_get_hex(TreeEntry *self); +PyObject* TreeEntry_to_object(TreeEntry *self); + +TreeEntry* Tree_getitem_by_index(Tree *self, PyObject *py_index); +TreeEntry* Tree_getitem(Tree *self, PyObject *value); + +PyObject* TreeBuilder_insert(TreeBuilder *self, PyObject *args); +PyObject* TreeBuilder_write(TreeBuilder *self); +PyObject* TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename); +PyObject* TreeBuilder_clear(TreeBuilder *self); +#endif diff --git a/include/pygit2/types.h b/include/pygit2/types.h new file mode 100644 index 000000000..bccb5c9d5 --- /dev/null +++ b/include/pygit2/types.h @@ -0,0 +1,72 @@ +#ifndef INCLUDE_pygit2_objects_h +#define INCLUDE_pygit2_objects_h + +#include +#include + +/* Python objects */ +typedef struct { + PyObject_HEAD + git_repository *repo; + PyObject *index; /* It will be None for a bare repository */ +} Repository; + +/* The structs for some of the object subtypes are identical except for + * the type of their object pointers. */ +#define OBJECT_STRUCT(_name, _ptr_type, _ptr_name) \ + typedef struct {\ + PyObject_HEAD\ + Repository *repo;\ + _ptr_type *_ptr_name;\ + } _name; + +OBJECT_STRUCT(Object, git_object, obj) +OBJECT_STRUCT(Commit, git_commit, commit) +OBJECT_STRUCT(Tree, git_tree, tree) +OBJECT_STRUCT(TreeBuilder, git_treebuilder, bld) +OBJECT_STRUCT(Blob, git_blob, blob) +OBJECT_STRUCT(Tag, git_tag, tag) +OBJECT_STRUCT(Index, git_index, index) +OBJECT_STRUCT(Walker, git_revwalk, walk) + +typedef struct { + PyObject_HEAD + PyObject *owner; /* Tree or TreeBuilder */ + const git_tree_entry *entry; +} TreeEntry; + +typedef struct { + PyObject_HEAD + Tree *owner; + int i; +} TreeIter; + +typedef struct { + PyObject_HEAD + git_index_entry *entry; +} IndexEntry; + +typedef struct { + PyObject_HEAD + Index *owner; + int i; +} IndexIter; + +typedef struct { + PyObject_HEAD + git_reference *reference; +} Reference; + +typedef struct { + PyObject_HEAD + Object *obj; + const git_signature *signature; + const char *encoding; +} Signature; + + +PyObject* lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, + git_otype type); +PyObject* lookup_object(Repository *repo, const git_oid *oid, git_otype type); + +#endif diff --git a/include/pygit2/utils.h b/include/pygit2/utils.h new file mode 100644 index 000000000..23c53f0ae --- /dev/null +++ b/include/pygit2/utils.h @@ -0,0 +1,69 @@ +#ifndef INCLUDE_pygit2_utils_h +#define INCLUDE_pygit2_utils_h + +#include +#include +#include + + +/* Python 3 support */ +#if PY_MAJOR_VERSION >= 3 + #define PyInt_AsLong PyLong_AsLong + #define PyInt_Check PyLong_Check + #define PyInt_FromLong PyLong_FromLong + #define PyString_AS_STRING PyBytes_AS_STRING + #define PyString_AsString PyBytes_AsString + #define PyString_AsStringAndSize PyBytes_AsStringAndSize + #define PyString_Check PyBytes_Check + #define PyString_FromString PyBytes_FromString + #define PyString_FromStringAndSize PyBytes_FromStringAndSize + #define PyString_Size PyBytes_Size +#endif + +#if PY_MAJOR_VERSION == 2 + #define to_path(x) to_bytes(x) + #define to_encoding(x) to_bytes(x) +#else + #define to_path(x) to_unicode(x, Py_FileSystemDefaultEncoding, "strict") + #define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict") +#endif + +#define CHECK_REFERENCE(self)\ + if (self->reference == NULL) {\ + PyErr_SetString(GitError, "deleted reference");\ + return NULL;\ + } + +#define CHECK_REFERENCE_INT(self)\ + if (self->reference == NULL) {\ + PyErr_SetString(GitError, "deleted reference");\ + return -1;\ + } + +/* Utilities */ +Py_LOCAL_INLINE(PyObject*) +to_unicode(const char *value, const char *encoding, const char *errors) +{ + if (encoding == NULL) { + /* If the encoding is not explicit, it may not be UTF-8, so it + * is not safe to decode it strictly. This is rare in the + * wild, but does occur in old commits to git itself + * (e.g. c31820c2). */ + encoding = "utf-8"; + errors = "replace"; + } + return PyUnicode_Decode(value, strlen(value), encoding, errors); +} + +Py_LOCAL_INLINE(PyObject*) +to_bytes(const char * value) +{ + return PyString_FromString(value); +} + +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) + +#endif diff --git a/include/pygit2/walker.h b/include/pygit2/walker.h new file mode 100644 index 000000000..4d6cc667d --- /dev/null +++ b/include/pygit2/walker.h @@ -0,0 +1,16 @@ +#ifndef INCLUDE_pygit2_walker_h +#define INCLUDE_pygit2_walker_h + +#include +#include +#include + +void Walker_dealloc(Walker *self); +PyObject* Walker_hide(Walker *self, PyObject *py_hex); +PyObject* Walker_push(Walker *self, PyObject *py_hex); +PyObject* Walker_sort(Walker *self, PyObject *py_sort_mode); +PyObject* Walker_reset(Walker *self); +PyObject* Walker_iter(Walker *self); +PyObject* Walker_iternext(Walker *self); + +#endif diff --git a/pygit2.c b/pygit2.c deleted file mode 100644 index cb295eb3a..000000000 --- a/pygit2.c +++ /dev/null @@ -1,3345 +0,0 @@ -/* - * Copyright 2010 Google, Inc. - * Copyright 2011 Itaapy - * - * 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 - -/* Python 3 support */ -#if PY_MAJOR_VERSION >= 3 - #define PyInt_AsLong PyLong_AsLong - #define PyInt_Check PyLong_Check - #define PyInt_FromLong PyLong_FromLong - #define PyString_AS_STRING PyBytes_AS_STRING - #define PyString_AsString PyBytes_AsString - #define PyString_AsStringAndSize PyBytes_AsStringAndSize - #define PyString_Check PyBytes_Check - #define PyString_FromString PyBytes_FromString - #define PyString_FromStringAndSize PyBytes_FromStringAndSize - #define PyString_Size PyBytes_Size -#endif - -/* Utilities */ -Py_LOCAL_INLINE(PyObject*) -to_unicode(const char *value, const char *encoding, const char *errors) -{ - if (encoding == NULL) { - /* If the encoding is not explicit, it may not be UTF-8, so it - * is not safe to decode it strictly. This is rare in the - * wild, but does occur in old commits to git itself - * (e.g. c31820c2). */ - encoding = "utf-8"; - errors = "replace"; - } - return PyUnicode_Decode(value, strlen(value), encoding, errors); -} - -Py_LOCAL_INLINE(PyObject*) -to_bytes(const char * value) -{ - return PyString_FromString(value); -} - -#if PY_MAJOR_VERSION == 2 - #define to_path(x) to_bytes(x) - #define to_encoding(x) to_bytes(x) -#else - #define to_path(x) to_unicode(x, Py_FileSystemDefaultEncoding, "strict") - #define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict") -#endif - -#define CHECK_REFERENCE(self)\ - if (self->reference == NULL) {\ - PyErr_SetString(GitError, "deleted reference");\ - return NULL;\ - } - -#define CHECK_REFERENCE_INT(self)\ - if (self->reference == NULL) {\ - PyErr_SetString(GitError, "deleted reference");\ - return -1;\ - } - -/* Python objects */ -typedef struct { - PyObject_HEAD - git_repository *repo; - PyObject *index; /* It will be None for a bare repository */ -} Repository; - -/* The structs for some of the object subtypes are identical except for - * the type of their object pointers. */ -#define OBJECT_STRUCT(_name, _ptr_type, _ptr_name) \ - typedef struct {\ - PyObject_HEAD\ - Repository *repo;\ - _ptr_type *_ptr_name;\ - } _name; - -OBJECT_STRUCT(Object, git_object, obj) -OBJECT_STRUCT(Commit, git_commit, commit) -OBJECT_STRUCT(Tree, git_tree, tree) -OBJECT_STRUCT(TreeBuilder, git_treebuilder, bld) -OBJECT_STRUCT(Blob, git_blob, blob) -OBJECT_STRUCT(Tag, git_tag, tag) -OBJECT_STRUCT(Index, git_index, index) -OBJECT_STRUCT(Walker, git_revwalk, walk) - -typedef struct { - PyObject_HEAD - PyObject *owner; /* Tree or TreeBuilder */ - const git_tree_entry *entry; -} TreeEntry; - -typedef struct { - PyObject_HEAD - Tree *owner; - int i; -} TreeIter; - -typedef struct { - PyObject_HEAD - git_index_entry *entry; -} IndexEntry; - -typedef struct { - PyObject_HEAD - Index *owner; - int i; -} IndexIter; - -typedef struct { - PyObject_HEAD - git_reference *reference; -} Reference; - -typedef struct { - PyObject_HEAD - Object *obj; - const git_signature *signature; - const char *encoding; -} Signature; - -static PyTypeObject RepositoryType; -static PyTypeObject ObjectType; -static PyTypeObject CommitType; -static PyTypeObject TreeType; -static PyTypeObject TreeBuilderType; -static PyTypeObject TreeEntryType; -static PyTypeObject TreeIterType; -static PyTypeObject BlobType; -static PyTypeObject TagType; -static PyTypeObject IndexType; -static PyTypeObject IndexEntryType; -static PyTypeObject IndexIterType; -static PyTypeObject WalkerType; -static PyTypeObject ReferenceType; -static PyTypeObject SignatureType; - -static PyObject *GitError; - -static PyObject * -Error_type(int type) -{ - // Expected - switch (type) { - /** Input does not exist in the scope searched. */ - case GIT_ENOTFOUND: - return PyExc_KeyError; - - /** A reference with this name already exists */ - case GIT_EEXISTS: - return PyExc_ValueError; - - /** The given short oid is ambiguous */ - case GIT_EAMBIGUOUS: - return PyExc_ValueError; - - /** The buffer is too short to satisfy the request */ - case GIT_EBUFS: - return PyExc_ValueError; - - /** Skip and passthrough the given ODB backend */ - case GIT_PASSTHROUGH: - return GitError; - - /** No entries left in ref walker */ - case GIT_REVWALKOVER: - return PyExc_StopIteration; - } - - // Critical - const git_error* error = giterr_last(); - switch (error->klass) { - case GITERR_NOMEMORY: - return PyExc_MemoryError; - case GITERR_OS: - return PyExc_OSError; - case GITERR_INVALID: - return PyExc_ValueError; - default: - return GitError; - } -} - - -static PyObject * -Error_set(int err) -{ - assert(err < 0); - - if(err != GIT_ERROR) { //expected failure - PyErr_SetNone(Error_type(err)); - } else { //critical failure - const git_error* error = giterr_last(); - PyErr_SetString(Error_type(err), error->message); - } - - return NULL; -} - -static PyObject * -Error_set_str(int err, const char *str) -{ - if (err == GIT_ENOTFOUND) { - /* KeyError expects the arg to be the missing key. */ - PyErr_SetString(PyExc_KeyError, str); - return NULL; - } - - const git_error* error = giterr_last(); - return PyErr_Format(Error_type(err), "%s: %s", str, error->message); -} - -static PyObject * -Error_set_oid(int err, const git_oid *oid, size_t len) -{ - char hex[GIT_OID_HEXSZ + 1]; - - git_oid_fmt(hex, oid); - hex[len] = '\0'; - return Error_set_str(err, hex); -} - -static PyObject * -lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, - git_otype type) -{ - int err; - git_object *obj; - Object *py_obj = NULL; - - err = git_object_lookup_prefix(&obj, repo->repo, oid, - (unsigned int)len, type); - if (err < 0) - return Error_set_oid(err, oid, len); - - switch (git_object_type(obj)) { - case GIT_OBJ_COMMIT: - py_obj = PyObject_New(Object, &CommitType); - break; - case GIT_OBJ_TREE: - py_obj = PyObject_New(Object, &TreeType); - break; - case GIT_OBJ_BLOB: - py_obj = PyObject_New(Object, &BlobType); - break; - case GIT_OBJ_TAG: - py_obj = PyObject_New(Object, &TagType); - break; - default: - assert(0); - } - - if (py_obj) { - py_obj->obj = obj; - py_obj->repo = repo; - Py_INCREF(repo); - } - return (PyObject*)py_obj; -} - -static PyObject * -lookup_object(Repository *repo, const git_oid *oid, git_otype type) -{ - return lookup_object_prefix(repo, oid, GIT_OID_HEXSZ, type); -} - -static git_otype -int_to_loose_object_type(int type_id) -{ - switch((git_otype)type_id) { - case GIT_OBJ_COMMIT: return GIT_OBJ_COMMIT; - case GIT_OBJ_TREE: return GIT_OBJ_TREE; - case GIT_OBJ_BLOB: return GIT_OBJ_BLOB; - case GIT_OBJ_TAG: return GIT_OBJ_TAG; - default: return GIT_OBJ_BAD; - } -} - -static PyObject * -wrap_reference(git_reference * c_reference) -{ - Reference *py_reference=NULL; - - py_reference = PyObject_New(Reference, &ReferenceType); - if (py_reference) - py_reference->reference = c_reference; - - return (PyObject *)py_reference; -} - -static int -py_str_to_git_oid(PyObject *py_str, git_oid *oid) -{ - PyObject *py_hex; - char *hex_or_bin; - int err; - Py_ssize_t len; - - /* Case 1: raw sha */ - if (PyString_Check(py_str)) { - hex_or_bin = PyString_AsString(py_str); - if (hex_or_bin == NULL) - return -1; - git_oid_fromraw(oid, (const unsigned char*)hex_or_bin); - return GIT_OID_HEXSZ; - } - - /* Case 2: hex sha */ - if (PyUnicode_Check(py_str)) { - py_hex = PyUnicode_AsASCIIString(py_str); - if (py_hex == NULL) - return -1; - err = PyString_AsStringAndSize(py_hex, &hex_or_bin, &len); - if (err) { - Py_DECREF(py_hex); - return -1; - } - - err = git_oid_fromstrn(oid, hex_or_bin, len); - - Py_DECREF(py_hex); - - if (err < 0) { - PyErr_SetObject(Error_type(err), py_str); - return -1; - } - return len; - } - - /* Type error */ - PyErr_Format(PyExc_TypeError, - "Git object id must be byte or a text string, not: %.200s", - Py_TYPE(py_str)->tp_name); - return -1; -} - -static int -py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, git_oid *oid) -{ - int err; - int len; - git_odb *odb; - git_odb_object *obj; - - len = py_str_to_git_oid(py_str, oid); - - if (len == GIT_OID_HEXSZ || len < 0) - return len; - - err = git_repository_odb(&odb, repo); - if (err < 0) { - Error_set(err); - return -1; - } - - err = git_odb_read_prefix(&obj, odb, oid, len); - if (err < 0) { - git_odb_free(odb); - Error_set(err); - return err; - } - - git_oid_cpy(oid, git_odb_object_id(obj)); - - git_odb_object_free(obj); - git_odb_free(odb); - - return 0; -} - -#define git_oid_to_python(id) \ - PyString_FromStringAndSize((const char*)id, GIT_OID_RAWSZ) - -static PyObject * -git_oid_to_py_str(const git_oid *oid) -{ - char hex[GIT_OID_HEXSZ]; - - git_oid_fmt(hex, oid); - return PyUnicode_DecodeASCII(hex, GIT_OID_HEXSZ, "strict"); -} - -// py_str_to_c_str() returns a newly allocated C string holding -// the string contained in the value argument. -char * -py_str_to_c_str(PyObject *value, const char *encoding) -{ - /* Case 1: byte string */ - if (PyString_Check(value)) - return strdup(PyString_AsString(value)); - - /* Case 2: text string */ - if (PyUnicode_Check(value)) { - char *c_str = NULL; - - if (encoding == NULL) - value = PyUnicode_AsUTF8String(value); - else - value = PyUnicode_AsEncodedString(value, encoding, "strict"); - if (value == NULL) - return NULL; - c_str = strdup(PyString_AsString(value)); - Py_DECREF(value); - return c_str; - } - - /* Type error */ - PyErr_Format(PyExc_TypeError, "unexpected %.200s", - Py_TYPE(value)->tp_name); - return NULL; -} - -#define py_path_to_c_str(py_path) \ - py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding) - - -static int -Repository_init(Repository *self, PyObject *args, PyObject *kwds) -{ - char *path; - int err; - - if (kwds) { - PyErr_SetString(PyExc_TypeError, - "Repository takes no keyword arguments"); - return -1; - } - - if (!PyArg_ParseTuple(args, "s", &path)) - return -1; - - err = git_repository_open(&self->repo, path); - if (err < 0) { - Error_set_str(err, path); - return -1; - } - - return 0; -} - -static void -Repository_dealloc(Repository *self) -{ - PyObject_GC_UnTrack(self); - Py_XDECREF(self->index); - git_repository_free(self->repo); - PyObject_GC_Del(self); -} - -static int -Repository_traverse(Repository *self, visitproc visit, void *arg) -{ - Py_VISIT(self->index); - return 0; -} - -static int -Repository_clear(Repository *self) -{ - Py_CLEAR(self->index); - return 0; -} - -static int -Repository_contains(Repository *self, PyObject *value) -{ - git_oid oid; - git_odb *odb; - int err, len, exists; - - len = py_str_to_git_oid(value, &oid); - if (len < 0) - return -1; - - err = git_repository_odb(&odb, self->repo); - if (err < 0) { - Error_set(err); - return -1; - } - - if (len < GIT_OID_HEXSZ) { - git_odb_object *obj = NULL; - err = git_odb_read_prefix(&obj, odb, &oid, len); - if (err < 0 && err != GIT_ENOTFOUND) { - Error_set(err); - exists = -1; - } else { - exists = (err == 0); - if (obj) - git_odb_object_free(obj); - } - } else { - exists = git_odb_exists(odb, &oid); - } - - git_odb_free(odb); - return exists; -} - -static PyObject * -Repository_getitem(Repository *self, PyObject *value) -{ - git_oid oid; - int len; - - len = py_str_to_git_oid(value, &oid); - if (len < 0) - return NULL; - - return lookup_object_prefix(self, &oid, len, GIT_OBJ_ANY); -} - -static git_odb_object * -Repository_read_raw(git_repository *repo, const git_oid *oid, size_t len) -{ - git_odb *odb; - git_odb_object *obj; - int err; - - err = git_repository_odb(&odb, repo); - if (err < 0) { - Error_set(err); - return NULL; - } - - err = git_odb_read_prefix(&obj, odb, oid, (unsigned int)len); - git_odb_free(odb); - if (err < 0) { - Error_set_oid(err, oid, len); - return NULL; - } - - return obj; -} - -static PyObject * -Repository_read(Repository *self, PyObject *py_hex) -{ - git_oid oid; - git_odb_object *obj; - int len; - PyObject* tuple; - - len = py_str_to_git_oid(py_hex, &oid); - if (len < 0) - return NULL; - - obj = Repository_read_raw(self->repo, &oid, len); - if (obj == NULL) - return NULL; - - tuple = Py_BuildValue( - "(ns#)", - git_odb_object_type(obj), - git_odb_object_data(obj), - git_odb_object_size(obj)); - - git_odb_object_free(obj); - return tuple; -} - -static PyObject * -Repository_write(Repository *self, PyObject *args) -{ - int err; - git_oid oid; - git_odb *odb; - git_odb_stream* stream; - int type_id; - const char* buffer; - Py_ssize_t buflen; - git_otype type; - - if (!PyArg_ParseTuple(args, "Is#", &type_id, &buffer, &buflen)) - return NULL; - - type = int_to_loose_object_type(type_id); - if (type == GIT_OBJ_BAD) - return PyErr_Format(PyExc_ValueError, "%d", type_id); - - err = git_repository_odb(&odb, self->repo); - if (err < 0) - return Error_set(err); - - err = git_odb_open_wstream(&stream, odb, buflen, type); - git_odb_free(odb); - if (err < 0) - return Error_set(err); - - stream->write(stream, buffer, buflen); - err = stream->finalize_write(&oid, stream); - stream->free(stream); - return git_oid_to_python(oid.id); -} - -static PyObject * -Repository_get_index(Repository *self, void *closure) -{ - int err; - git_index *index; - Index *py_index; - - assert(self->repo); - - if (self->index == NULL) { - err = git_repository_index(&index, self->repo); - if (err < 0) - return Error_set(err); - - py_index = PyObject_GC_New(Index, &IndexType); - if (!py_index) { - git_index_free(index); - return NULL; - } - - Py_INCREF(self); - py_index->repo = self; - py_index->index = index; - PyObject_GC_Track(py_index); - self->index = (PyObject*)py_index; - } - - Py_INCREF(self->index); - return self->index; -} - -static PyObject * -Repository_get_path(Repository *self, void *closure) -{ - return to_path(git_repository_path(self->repo)); -} - -static PyObject * -Repository_get_workdir(Repository *self, void *closure) -{ - const char *c_path; - - c_path = git_repository_workdir(self->repo); - if (c_path == NULL) - Py_RETURN_NONE; - - return to_path(c_path); -} - -static PyObject * -Repository_walk(Repository *self, PyObject *args) -{ - PyObject *value; - unsigned int sort; - int err; - git_oid oid; - git_revwalk *walk; - Walker *py_walker; - - if (!PyArg_ParseTuple(args, "OI", &value, &sort)) - return NULL; - - err = git_revwalk_new(&walk, self->repo); - if (err < 0) - return Error_set(err); - - /* Sort */ - git_revwalk_sorting(walk, sort); - - /* Push */ - if (value != Py_None) { - err = py_str_to_git_oid_expand(self->repo, value, &oid); - if (err < 0) { - git_revwalk_free(walk); - return Error_set(err); - } - - err = git_revwalk_push(walk, &oid); - if (err < 0) { - git_revwalk_free(walk); - return Error_set(err); - } - } - - py_walker = PyObject_New(Walker, &WalkerType); - if (!py_walker) { - git_revwalk_free(walk); - return NULL; - } - - Py_INCREF(self); - py_walker->repo = self; - py_walker->walk = walk; - return (PyObject*)py_walker; -} - -static PyObject * -build_signature(Object *obj, const git_signature *signature, - const char *encoding) -{ - Signature *py_signature; - - py_signature = PyObject_New(Signature, &SignatureType); - if (py_signature) { - Py_INCREF(obj); - py_signature->obj = obj; - py_signature->signature = signature; - py_signature->encoding = encoding; - } - return (PyObject*)py_signature; -} - -static PyObject * -Repository_create_blob(Repository *self, PyObject *args) -{ - git_oid oid; - const char* raw; - Py_ssize_t size; - int err; - - if (!PyArg_ParseTuple(args, "s#", &raw, &size)) - return NULL; - - err = git_blob_create_frombuffer(&oid, self->repo, (const void*)raw, size); - - if (err < 0) - return Error_set(err); - - return git_oid_to_python(oid.id); -} - -static PyObject * -Repository_create_commit(Repository *self, PyObject *args) -{ - Signature *py_author, *py_committer; - PyObject *py_oid, *py_message, *py_parents, *py_parent; - PyObject *py_result = NULL; - char *message = NULL; - char *update_ref = NULL; - char *encoding = NULL; - git_oid oid; - git_tree *tree = NULL; - int parent_count; - git_commit **parents = NULL; - int err = 0, i = 0, len; - - if (!PyArg_ParseTuple(args, "zO!O!OOO!|s", - &update_ref, - &SignatureType, &py_author, - &SignatureType, &py_committer, - &py_message, - &py_oid, - &PyList_Type, &py_parents, - &encoding)) - return NULL; - - len = py_str_to_git_oid(py_oid, &oid); - if (len < 0) - goto out; - - message = py_str_to_c_str(py_message, encoding); - if (message == NULL) - goto out; - - err = git_tree_lookup_prefix(&tree, self->repo, &oid, (unsigned int)len); - if (err < 0) { - Error_set(err); - goto out; - } - - parent_count = (int)PyList_Size(py_parents); - parents = malloc(parent_count * sizeof(git_commit*)); - if (parents == NULL) { - PyErr_SetNone(PyExc_MemoryError); - goto out; - } - for (; i < parent_count; i++) { - py_parent = PyList_GET_ITEM(py_parents, i); - len = py_str_to_git_oid(py_parent, &oid); - if (len < 0) - goto out; - if (git_commit_lookup_prefix(&parents[i], self->repo, &oid, - (unsigned int)len)) - goto out; - } - - err = git_commit_create(&oid, self->repo, update_ref, - py_author->signature, py_committer->signature, - encoding, message, tree, parent_count, - (const git_commit**)parents); - if (err < 0) { - Error_set(err); - goto out; - } - - py_result = git_oid_to_python(oid.id); - -out: - free(message); - git_tree_free(tree); - while (i > 0) { - i--; - git_commit_free(parents[i]); - } - free(parents); - return py_result; -} - -static PyObject * -Repository_create_tag(Repository *self, PyObject *args) -{ - PyObject *py_oid; - Signature *py_tagger; - char *tag_name, *message; - git_oid oid; - git_object *target = NULL; - int err, target_type, len; - - if (!PyArg_ParseTuple(args, "sOiO!s", - &tag_name, - &py_oid, - &target_type, - &SignatureType, &py_tagger, - &message)) - return NULL; - - len = py_str_to_git_oid(py_oid, &oid); - if (len < 0) - return NULL; - - err = git_object_lookup_prefix(&target, self->repo, &oid, - (unsigned int)len, target_type); - err = err < 0 ? err : git_tag_create(&oid, self->repo, tag_name, target, - py_tagger->signature, message, 0); - git_object_free(target); - if (err < 0) - return Error_set_oid(err, &oid, len); - return git_oid_to_python(oid.id); -} - -static PyObject * -Repository_listall_references(Repository *self, PyObject *args) -{ - unsigned list_flags=GIT_REF_LISTALL; - git_strarray c_result; - PyObject *py_result, *py_string; - unsigned index; - int err; - - /* 1- Get list_flags */ - if (!PyArg_ParseTuple(args, "|I", &list_flags)) - return NULL; - - /* 2- Get the C result */ - err = git_reference_list(&c_result, self->repo, list_flags); - if (err < 0) - return Error_set(err); - - /* 3- Create a new PyTuple */ - py_result = PyTuple_New(c_result.count); - if (py_result == NULL) - goto out; - - /* 4- Fill it */ - for (index=0; index < c_result.count; index++) { - py_string = to_path((c_result.strings)[index]); - if (py_string == NULL) { - Py_CLEAR(py_result); - goto out; - } - PyTuple_SET_ITEM(py_result, index, py_string); - } - -out: - git_strarray_free(&c_result); - return py_result; -} - -static PyObject * -Repository_lookup_reference(Repository *self, PyObject *py_name) -{ - git_reference *c_reference; - char *c_name; - int err; - - /* 1- Get the C name */ - c_name = py_path_to_c_str(py_name); - if (c_name == NULL) - return NULL; - - /* 2- Lookup */ - err = git_reference_lookup(&c_reference, self->repo, c_name); - if (err < 0) { - PyObject *err_obj = Error_set_str(err, c_name); - free(c_name); - return err_obj; - } - - /* 3- Make an instance of Reference and return it */ - return wrap_reference(c_reference); -} - -static PyObject * -Repository_create_reference(Repository *self, PyObject *args) -{ - PyObject *py_oid; - git_reference *c_reference; - char *c_name; - git_oid oid; - int err; - - /* 1- Get the C variables */ - if (!PyArg_ParseTuple(args, "sO", &c_name, &py_oid)) - return NULL; - - err = py_str_to_git_oid_expand(self->repo, py_oid, &oid); - if (err < 0) - return Error_set(err); - - /* 2- Create the reference */ - err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid, 0); - if (err < 0) - return Error_set(err); - - /* 3- Make an instance of Reference and return it */ - return wrap_reference(c_reference); -} - -static PyObject * -Repository_create_symbolic_reference(Repository *self, PyObject *args) -{ - git_reference *c_reference; - char *c_name, *c_target; - int err; - - /* 1- Get the C variables */ - if (!PyArg_ParseTuple(args, "ss", &c_name, &c_target)) - return NULL; - - /* 2- Create the reference */ - err = git_reference_create_symbolic(&c_reference, self->repo, c_name, - c_target, 0); - if (err < 0) - return Error_set(err); - - /* 3- Make an instance of Reference and return it */ - return wrap_reference(c_reference); -} - -static PyObject * -Repository_packall_references(Repository *self, PyObject *args) -{ - int err; - - /* 1- Pack */ - err = git_reference_packall(self->repo); - if (err < 0) - return Error_set(err); - - /* 2- Return None */ - Py_RETURN_NONE; -} - -static int -read_status_cb(const char *path, unsigned int status_flags, void *payload) -{ - /* This is the callback that will be called in git_status_foreach. It - * will be called for every path.*/ - PyObject *flags; - - flags = PyInt_FromLong((long) status_flags); - PyDict_SetItemString(payload, path, flags); - - return GIT_OK; -} - -static PyObject * -Repository_status(Repository *self, PyObject *args) -{ - PyObject *payload_dict; - - payload_dict = PyDict_New(); - git_status_foreach(self->repo, read_status_cb, payload_dict); - - return payload_dict; -} - -static PyObject * -Repository_status_file(Repository *self, PyObject *value) -{ - char *path; - unsigned int status; - int err; - - path = py_path_to_c_str(value); - if (!path) - return NULL; - - err = git_status_file(&status, self->repo, path); - if (err < 0) { - PyObject *err_obj = Error_set_str(err, path); - free(path); - return err_obj; - } - return PyInt_FromLong(status); -} - -static PyObject * -Repository_TreeBuilder(Repository *self, PyObject *args) -{ - TreeBuilder *builder; - git_treebuilder *bld; - PyObject *py_src = NULL; - git_oid oid; - git_tree *tree = NULL; - git_tree *must_free = NULL; - int err; - - if (!PyArg_ParseTuple(args, "|O", &py_src)) - return NULL; - - if (py_src) { - if (PyObject_TypeCheck(py_src, &TreeType)) { - Tree *py_tree = (Tree *)py_src; - if (py_tree->repo->repo != self->repo) { - //return Error_set(GIT_EINVALIDARGS); - return Error_set(GIT_ERROR); - } - tree = py_tree->tree; - } else { - err = py_str_to_git_oid_expand(self->repo, py_src, &oid); - if (err < 0) - return NULL; - - err = git_tree_lookup(&tree, self->repo, &oid); - if (err < 0) - return Error_set(err); - must_free = tree; - } - } - - err = git_treebuilder_create(&bld, tree); - if (must_free != NULL) { - git_tree_free(must_free); - } - - if (err < 0) - return Error_set(err); - - builder = PyObject_New(TreeBuilder, &TreeBuilderType); - if (builder) { - builder->repo = self; - builder->bld = bld; - Py_INCREF(self); - } - - return (PyObject*)builder; -} - -static PyMethodDef Repository_methods[] = { - {"create_commit", (PyCFunction)Repository_create_commit, METH_VARARGS, - "Create a new commit object, return its SHA."}, - {"create_tag", (PyCFunction)Repository_create_tag, METH_VARARGS, - "Create a new tag object, return its SHA."}, - {"walk", (PyCFunction)Repository_walk, METH_VARARGS, - "Generator that traverses the history starting from the given commit."}, - {"read", (PyCFunction)Repository_read, METH_O, - "Read raw object data from the repository."}, - {"write", (PyCFunction)Repository_write, METH_VARARGS, - "Write raw object data into the repository. First arg is the object\n" - "type, the second one a buffer with data. Return the object id (sha)\n" - "of the created object."}, - {"listall_references", (PyCFunction)Repository_listall_references, - METH_VARARGS, - "Return a list with all the references in the repository."}, - {"lookup_reference", (PyCFunction)Repository_lookup_reference, METH_O, - "Lookup a reference by its name in a repository."}, - {"create_blob", (PyCFunction)Repository_create_blob, - METH_VARARGS, - "Create a new blob from memory"}, - {"create_reference", (PyCFunction)Repository_create_reference, - METH_VARARGS, - "Create a new reference \"name\" that points to the object given by its " - "\"sha\"."}, - {"create_symbolic_reference", - (PyCFunction)Repository_create_symbolic_reference, METH_VARARGS, - "Create a new symbolic reference \"name\" that points to the reference\n" - "\"target\"."}, - {"packall_references", (PyCFunction)Repository_packall_references, - METH_NOARGS, "Pack all the loose references in the repository."}, - {"status", (PyCFunction)Repository_status, METH_NOARGS, "Reads the " - "status of the repository and returns a dictionary with file paths " - "as keys and status flags as values.\nSee pygit2.GIT_STATUS_*."}, - {"status_file", (PyCFunction)Repository_status_file, METH_O, - "Returns the status of the given file path."}, - {"TreeBuilder", (PyCFunction)Repository_TreeBuilder, METH_VARARGS, - "Create a TreeBuilder object for this repository."}, - {NULL} -}; - -static PyGetSetDef Repository_getseters[] = { - {"index", (getter)Repository_get_index, NULL, "index file. ", NULL}, - {"path", (getter)Repository_get_path, NULL, - "The normalized path to the git repository.", NULL}, - {"workdir", (getter)Repository_get_workdir, NULL, - "The normalized path to the working directory of the repository. " - "If the repository is bare, None will be returned.", NULL}, - {NULL} -}; - -static PySequenceMethods Repository_as_sequence = { - 0, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - 0, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - (objobjproc)Repository_contains, /* sq_contains */ -}; - -static PyMappingMethods Repository_as_mapping = { - 0, /* mp_length */ - (binaryfunc)Repository_getitem, /* mp_subscript */ - 0, /* mp_ass_subscript */ -}; - -static PyTypeObject RepositoryType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Repository", /* tp_name */ - sizeof(Repository), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Repository_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - &Repository_as_sequence, /* tp_as_sequence */ - &Repository_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | - Py_TPFLAGS_BASETYPE | - Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "Git repository", /* tp_doc */ - (traverseproc)Repository_traverse, /* tp_traverse */ - (inquiry)Repository_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Repository_methods, /* tp_methods */ - 0, /* tp_members */ - Repository_getseters, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Repository_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - -static void -Object_dealloc(Object* self) -{ - git_object_free(self->obj); - Py_XDECREF(self->repo); - PyObject_Del(self); -} - -static PyObject * -Object_get_oid(Object *self) -{ - const git_oid *oid; - - oid = git_object_id(self->obj); - assert(oid); - - return git_oid_to_python(oid->id); -} - -static PyObject * -Object_get_hex(Object *self) -{ - const git_oid *oid; - - oid = git_object_id(self->obj); - assert(oid); - - return git_oid_to_py_str(oid); -} - -static PyObject * -Object_get_type(Object *self) -{ - return PyInt_FromLong(git_object_type(self->obj)); -} - -static PyObject * -Object_read_raw(Object *self) -{ - const git_oid *oid; - git_odb_object *obj; - PyObject *aux; - - oid = git_object_id(self->obj); - assert(oid); - - obj = Repository_read_raw(self->repo->repo, oid, GIT_OID_HEXSZ); - if (obj == NULL) - return NULL; - - aux = PyString_FromStringAndSize( - git_odb_object_data(obj), - git_odb_object_size(obj)); - - git_odb_object_free(obj); - return aux; -} - -static PyGetSetDef Object_getseters[] = { - {"oid", (getter)Object_get_oid, NULL, "object id", NULL}, - {"hex", (getter)Object_get_hex, NULL, "hex oid", NULL}, - {"type", (getter)Object_get_type, NULL, "type number", NULL}, - {NULL} -}; - -static PyMethodDef Object_methods[] = { - {"read_raw", (PyCFunction)Object_read_raw, METH_NOARGS, - "Read the raw contents of the object from the repo."}, - {NULL} -}; - -static PyTypeObject ObjectType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Object", /* tp_name */ - sizeof(Object), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Object_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Object objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Object_methods, /* tp_methods */ - 0, /* tp_members */ - Object_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 */ -}; - -static PyObject * -Commit_get_message_encoding(Commit *commit) -{ - const char *encoding; - - encoding = git_commit_message_encoding(commit->commit); - if (encoding == NULL) - Py_RETURN_NONE; - - return to_encoding(encoding); -} - -static PyObject * -Commit_get_message(Commit *commit) -{ - const char *message, *encoding; - - message = git_commit_message(commit->commit); - encoding = git_commit_message_encoding(commit->commit); - return to_unicode(message, encoding, "strict"); -} - -static PyObject * -Commit_get_raw_message(Commit *commit) -{ - return PyString_FromString(git_commit_message(commit->commit)); -} - -static PyObject * -Commit_get_commit_time(Commit *commit) -{ - return PyLong_FromLong(git_commit_time(commit->commit)); -} - -static PyObject * -Commit_get_commit_time_offset(Commit *commit) -{ - return PyLong_FromLong(git_commit_time_offset(commit->commit)); -} - -static PyObject * -Commit_get_committer(Commit *self) -{ - const git_signature *signature; - const char *encoding; - - signature = git_commit_committer(self->commit); - encoding = git_commit_message_encoding(self->commit); - - return build_signature((Object*)self, signature, encoding); -} - -static PyObject * -Commit_get_author(Commit *self) -{ - const git_signature *signature; - const char *encoding; - - signature = git_commit_author(self->commit); - encoding = git_commit_message_encoding(self->commit); - - return build_signature((Object*)self, signature, encoding); -} - -static PyObject * -Commit_get_tree(Commit *commit) -{ - git_tree *tree; - Tree *py_tree; - int err; - - err = git_commit_tree(&tree, commit->commit); - if (err == GIT_ENOTFOUND) - Py_RETURN_NONE; - - if (err < 0) - return Error_set(err); - - py_tree = PyObject_New(Tree, &TreeType); - if (py_tree) { - Py_INCREF(commit->repo); - py_tree->repo = commit->repo; - py_tree->tree = (git_tree*)tree; - } - return (PyObject*)py_tree; -} - -static PyObject * -Commit_get_parents(Commit *commit) -{ - unsigned int i, parent_count; - const git_oid *parent_oid; - PyObject *obj; - PyObject *list; - - parent_count = git_commit_parentcount(commit->commit); - list = PyList_New(parent_count); - if (!list) - return NULL; - - for (i=0; i < parent_count; i++) { - parent_oid = git_commit_parent_oid(commit->commit, i); - if (parent_oid == NULL) { - Py_DECREF(list); - Error_set(GIT_ENOTFOUND); - return NULL; - } - obj = lookup_object(commit->repo, parent_oid, GIT_OBJ_COMMIT); - if (obj == NULL) { - Py_DECREF(list); - return NULL; - } - - PyList_SET_ITEM(list, i, obj); - } - - return list; -} - -static PyGetSetDef Commit_getseters[] = { - {"message_encoding", (getter)Commit_get_message_encoding, NULL, - "message encoding", NULL}, - {"message", (getter)Commit_get_message, NULL, "message", NULL}, - {"_message", (getter)Commit_get_raw_message, NULL, "message (bytes)", NULL}, - {"commit_time", (getter)Commit_get_commit_time, NULL, "commit time", - NULL}, - {"commit_time_offset", (getter)Commit_get_commit_time_offset, NULL, - "commit time offset", NULL}, - {"committer", (getter)Commit_get_committer, NULL, "committer", NULL}, - {"author", (getter)Commit_get_author, NULL, "author", NULL}, - {"tree", (getter)Commit_get_tree, NULL, "tree object", NULL}, - {"parents", (getter)Commit_get_parents, NULL, "parents of this commit", - NULL}, - {NULL} -}; - -static PyTypeObject CommitType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Commit", /* tp_name */ - sizeof(Commit), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* 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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Commit objects", /* 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 */ - Commit_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 */ -}; - -static void -TreeEntry_dealloc(TreeEntry *self) -{ - Py_XDECREF(self->owner); - PyObject_Del(self); -} - -static PyObject * -TreeEntry_get_attributes(TreeEntry *self) -{ - return PyInt_FromLong(git_tree_entry_attributes(self->entry)); -} - -static PyObject * -TreeEntry_get_name(TreeEntry *self) -{ - return to_path(git_tree_entry_name(self->entry)); -} - -static PyObject * -TreeEntry_get_oid(TreeEntry *self) -{ - const git_oid *oid; - - oid = git_tree_entry_id(self->entry); - return git_oid_to_python(oid->id); -} - -static PyObject * -TreeEntry_get_hex(TreeEntry *self) -{ - return git_oid_to_py_str(git_tree_entry_id(self->entry)); -} - -static PyObject * -TreeEntry_to_object(TreeEntry *self) -{ - const git_oid *entry_oid; - Repository *repo; - - repo = ((Object*)(self->owner))->repo; - entry_oid = git_tree_entry_id(self->entry); - return lookup_object(repo, entry_oid, GIT_OBJ_ANY); -} - -static PyGetSetDef TreeEntry_getseters[] = { - {"attributes", (getter)TreeEntry_get_attributes, NULL, "attributes", NULL}, - {"name", (getter)TreeEntry_get_name, NULL, "name", NULL}, - {"oid", (getter)TreeEntry_get_oid, NULL, "object id", NULL}, - {"hex", (getter)TreeEntry_get_hex, NULL, "hex oid", NULL}, - {NULL} -}; - -static PyMethodDef TreeEntry_methods[] = { - {"to_object", (PyCFunction)TreeEntry_to_object, METH_NOARGS, - "Look up the corresponding object in the repo."}, - {NULL, NULL, 0, NULL} -}; - -static PyTypeObject TreeEntryType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.TreeEntry", /* tp_name */ - sizeof(TreeEntry), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)TreeEntry_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "TreeEntry objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - TreeEntry_methods, /* tp_methods */ - 0, /* tp_members */ - TreeEntry_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 */ -}; - -static Py_ssize_t -Tree_len(Tree *self) -{ - assert(self->tree); - return (Py_ssize_t)git_tree_entrycount(self->tree); -} - -static int -Tree_contains(Tree *self, PyObject *py_name) -{ - int result = 0; - char *name = py_path_to_c_str(py_name); - if (name == NULL) - return -1; - - result = git_tree_entry_byname(self->tree, name) ? 1 : 0; - free(name); - return result; -} - -static TreeEntry * -wrap_tree_entry(const git_tree_entry *entry, Tree *tree) -{ - TreeEntry *py_entry; - - py_entry = PyObject_New(TreeEntry, &TreeEntryType); - if (py_entry) { - py_entry->entry = entry; - py_entry->owner = (PyObject*)tree; - Py_INCREF(tree); - } - return py_entry; -} - -static int -Tree_fix_index(Tree *self, PyObject *py_index) -{ - long index; - size_t len; - long slen; - - index = PyInt_AsLong(py_index); - if (PyErr_Occurred()) - return -1; - - len = git_tree_entrycount(self->tree); - slen = (long)len; - if (index >= slen) { - PyErr_SetObject(PyExc_IndexError, py_index); - return -1; - } - else if (index < -slen) { - PyErr_SetObject(PyExc_IndexError, py_index); - return -1; - } - - /* This function is called via mp_subscript, which doesn't do negative - * index rewriting, so we have to do it manually. */ - if (index < 0) - index = len + index; - return (int)index; -} - -static PyObject * -Tree_iter(Tree *self) -{ - TreeIter *iter; - - iter = PyObject_New(TreeIter, &TreeIterType); - if (iter) { - Py_INCREF(self); - iter->owner = self; - iter->i = 0; - } - return (PyObject*)iter; -} - -static TreeEntry * -Tree_getitem_by_index(Tree *self, PyObject *py_index) -{ - int index; - const git_tree_entry *entry; - - index = Tree_fix_index(self, py_index); - if (PyErr_Occurred()) - return NULL; - - entry = git_tree_entry_byindex(self->tree, index); - if (!entry) { - PyErr_SetObject(PyExc_IndexError, py_index); - return NULL; - } - return wrap_tree_entry(entry, self); -} - -static TreeEntry * -Tree_getitem(Tree *self, PyObject *value) -{ - char *name; - const git_tree_entry *entry; - - /* Case 1: integer */ - if (PyInt_Check(value)) - return Tree_getitem_by_index(self, value); - - /* Case 2: byte or text string */ - name = py_path_to_c_str(value); - if (name == NULL) - return NULL; - entry = git_tree_entry_byname(self->tree, name); - free(name); - if (!entry) { - PyErr_SetObject(PyExc_KeyError, value); - return NULL; - } - return wrap_tree_entry(entry, self); -} - -static PySequenceMethods Tree_as_sequence = { - 0, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - 0, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - (objobjproc)Tree_contains, /* sq_contains */ -}; - -static PyMappingMethods Tree_as_mapping = { - (lenfunc)Tree_len, /* mp_length */ - (binaryfunc)Tree_getitem, /* mp_subscript */ - 0, /* mp_ass_subscript */ -}; - -static PyTypeObject TreeType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Tree", /* tp_name */ - sizeof(Tree), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - &Tree_as_sequence, /* tp_as_sequence */ - &Tree_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Tree objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)Tree_iter, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* 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 */ -}; - -static void -TreeBuilder_dealloc(TreeBuilder* self) -{ - Py_XDECREF(self->repo); - git_treebuilder_free(self->bld); - PyObject_Del(self); -} - -static PyObject * -TreeBuilder_insert(TreeBuilder *self, PyObject *args) -{ - PyObject *py_oid; - int len, err, attr; - git_oid oid; - const char *fname; - - if (!PyArg_ParseTuple(args, "sOi", &fname, &py_oid, &attr)) { - return NULL; - } - - len = py_str_to_git_oid(py_oid, &oid); - if (len < 0) { - return NULL; - } - - err = git_treebuilder_insert(NULL, self->bld, fname, &oid, attr); - if (err < 0) { - Error_set(err); - return NULL; - } - - Py_RETURN_NONE; -} - -static PyObject * -TreeBuilder_write(TreeBuilder *self) -{ - int err; - git_oid oid; - - err = git_treebuilder_write(&oid, self->repo->repo, self->bld); - if (err < 0) - return Error_set(err); - - return git_oid_to_python(&oid); -} - -static PyObject * -TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename) -{ - char *filename = py_path_to_c_str(py_filename); - int err = 0; - - if (filename == NULL) - return NULL; - - err = git_treebuilder_remove(self->bld, filename); - free(filename); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; -} - -static PyObject * -TreeBuilder_clear(TreeBuilder *self) -{ - git_treebuilder_clear(self->bld); - Py_RETURN_NONE; -} - -static PyMethodDef TreeBuilder_methods[] = { - {"insert", (PyCFunction)TreeBuilder_insert, METH_VARARGS, - "Insert or replace an entry in the treebuilder"}, - {"write", (PyCFunction)TreeBuilder_write, METH_NOARGS, - "Write the tree to the given repository"}, - {"remove", (PyCFunction)TreeBuilder_remove, METH_O, - "Remove an entry from the builder"}, - {"clear", (PyCFunction)TreeBuilder_clear, METH_NOARGS, - "Clear all the entries in the builder"}, - {NULL, NULL, 0, NULL} -}; - -static PyTypeObject TreeBuilderType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.TreeBuilder", /* tp_name */ - sizeof(TreeBuilder), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)TreeBuilder_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "TreeBuilder objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - TreeBuilder_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* 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 */ -}; - -static void -TreeIter_dealloc(TreeIter *self) -{ - Py_CLEAR(self->owner); - PyObject_Del(self); -} - -static TreeEntry * -TreeIter_iternext(TreeIter *self) -{ - const git_tree_entry *tree_entry; - - tree_entry = git_tree_entry_byindex(self->owner->tree, self->i); - if (!tree_entry) - return NULL; - - self->i += 1; - return (TreeEntry*)wrap_tree_entry(tree_entry, self->owner); -} - -static PyTypeObject TreeIterType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.TreeIter", /* tp_name */ - sizeof(TreeIter), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)TreeIter_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)TreeIter_iternext, /* tp_iternext */ -}; - -static PyGetSetDef Blob_getseters[] = { - {"data", (getter)Object_read_raw, NULL, "raw data", NULL}, - {NULL} -}; - -static PyTypeObject BlobType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Blob", /* tp_name */ - sizeof(Blob), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* 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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Blob objects", /* 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 */ - Blob_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 */ -}; - -static PyObject * -Tag_get_target(Tag *self) -{ - const git_oid *oid; - - oid = git_tag_target_oid(self->tag); - return git_oid_to_python(oid->id); -} - -static PyObject * -Tag_get_name(Tag *self) -{ - const char *name; - name = git_tag_name(self->tag); - if (!name) - Py_RETURN_NONE; - return to_unicode(name, "utf-8", "strict"); -} - -static PyObject * -Tag_get_tagger(Tag *self) -{ - const git_signature *signature = git_tag_tagger(self->tag); - if (!signature) - Py_RETURN_NONE; - - return build_signature((Object*)self, signature, "utf-8"); -} - -static PyObject * -Tag_get_message(Tag *self) -{ - const char *message; - message = git_tag_message(self->tag); - if (!message) - Py_RETURN_NONE; - return to_unicode(message, "utf-8", "strict"); -} - -static PyObject * -Tag_get_raw_message(Tag *self) -{ - return PyString_FromString(git_tag_message(self->tag)); -} - -static PyGetSetDef Tag_getseters[] = { - {"target", (getter)Tag_get_target, NULL, "tagged object", NULL}, - {"name", (getter)Tag_get_name, NULL, "tag name", NULL}, - {"tagger", (getter)Tag_get_tagger, NULL, "tagger", NULL}, - {"message", (getter)Tag_get_message, NULL, "tag message", NULL}, - {"_message", (getter)Tag_get_raw_message, NULL, "tag message (bytes)", NULL}, - {NULL} -}; - -static PyTypeObject TagType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Tag", /* tp_name */ - sizeof(Tag), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* 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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Tag objects", /* 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 */ - Tag_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 */ -}; - -static int -Index_init(Index *self, PyObject *args, PyObject *kwds) -{ - char *path; - int err; - - if (kwds) { - PyErr_SetString(PyExc_TypeError, - "Index takes no keyword arguments"); - return -1; - } - - if (!PyArg_ParseTuple(args, "s", &path)) - return -1; - - err = git_index_open(&self->index, path); - if (err < 0) { - Error_set_str(err, path); - return -1; - } - - return 0; -} - -static void -Index_dealloc(Index* self) -{ - PyObject_GC_UnTrack(self); - Py_XDECREF(self->repo); - git_index_free(self->index); - PyObject_GC_Del(self); -} - -static int -Index_traverse(Index *self, visitproc visit, void *arg) -{ - Py_VISIT(self->repo); - return 0; -} - -static PyObject * -Index_add(Index *self, PyObject *args) -{ - int err; - const char *path; - int stage=0; - - if (!PyArg_ParseTuple(args, "s|i", &path, &stage)) - return NULL; - - err = git_index_add(self->index, path, stage); - if (err < 0) - return Error_set_str(err, path); - - Py_RETURN_NONE; -} - -static PyObject * -Index_clear(Index *self) -{ - git_index_clear(self->index); - Py_RETURN_NONE; -} - -static PyObject * -Index_find(Index *self, PyObject *py_path) -{ - char *path; - long idx; - - path = PyString_AsString(py_path); - if (!path) - return NULL; - - idx = (long)git_index_find(self->index, path); - if (idx < 0) - return Error_set_str(idx, path); - - return PyInt_FromLong(idx); -} - -static PyObject * -Index_read(Index *self) -{ - int err; - - err = git_index_read(self->index); - if (err < GIT_OK) - return Error_set(err); - - Py_RETURN_NONE; -} - -static PyObject * -Index_write(Index *self) -{ - int err; - - err = git_index_write(self->index); - if (err < GIT_OK) - return Error_set(err); - - Py_RETURN_NONE; -} - -/* This is an internal function, used by Index_getitem and Index_setitem */ -static int -Index_get_position(Index *self, PyObject *value) -{ - char *path; - int idx; - - /* Case 1: integer */ - if (PyInt_Check(value)) { - idx = (int)PyInt_AsLong(value); - if (idx == -1 && PyErr_Occurred()) - return -1; - if (idx < 0) { - PyErr_SetObject(PyExc_ValueError, value); - return -1; - } - return idx; - } - - /* Case 2: byte or text string */ - path = py_path_to_c_str(value); - if (!path) - return -1; - idx = git_index_find(self->index, path); - if (idx < 0) { - Error_set_str(idx, path); - free(path); - return -1; - } - return idx; -} - -static int -Index_contains(Index *self, PyObject *value) -{ - char *path; - int idx; - - path = py_path_to_c_str(value); - if (!path) - return -1; - idx = git_index_find(self->index, path); - if (idx == GIT_ENOTFOUND) - return 0; - if (idx < 0) { - Error_set_str(idx, path); - free(path); - return -1; - } - - return 1; -} - -static PyObject * -Index_iter(Index *self) -{ - IndexIter *iter; - - iter = PyObject_New(IndexIter, &IndexIterType); - if (iter) { - Py_INCREF(self); - iter->owner = self; - iter->i = 0; - } - return (PyObject*)iter; -} - -static Py_ssize_t -Index_len(Index *self) -{ - return (Py_ssize_t)git_index_entrycount(self->index); -} - -static PyObject * -wrap_index_entry(git_index_entry *entry, Index *index) -{ - IndexEntry *py_entry; - - py_entry = PyObject_New(IndexEntry, &IndexEntryType); - if (py_entry) - py_entry->entry = entry; - - return (PyObject*)py_entry; -} - -static PyObject * -Index_getitem(Index *self, PyObject *value) -{ - int idx; - git_index_entry *index_entry; - - idx = Index_get_position(self, value); - if (idx == -1) - return NULL; - - index_entry = git_index_get(self->index, idx); - if (!index_entry) { - PyErr_SetObject(PyExc_KeyError, value); - return NULL; - } - - return wrap_index_entry(index_entry, self); -} - -static int -Index_setitem(Index *self, PyObject *key, PyObject *value) -{ - int err; - int idx; - - if (value) { - PyErr_SetString(PyExc_NotImplementedError, - "set item on index not yet implemented"); - return -1; - } - - idx = Index_get_position(self, key); - if (idx == -1) - return -1; - - err = git_index_remove(self->index, idx); - if (err < 0) { - Error_set(err); - return -1; - } - - return 0; -} - -static PyObject * -Index_read_tree(Index *self, PyObject *value) -{ - git_oid oid; - git_tree *tree; - int err, len; - - len = py_str_to_git_oid(value, &oid); - if (len < 0) - return NULL; - - err = git_tree_lookup_prefix(&tree, self->repo->repo, &oid, - (unsigned int)len); - if (err < 0) - return Error_set(err); - - err = git_index_read_tree(self->index, tree); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; -} - -static PyObject * -Index_write_tree(Index *self) -{ - git_oid oid; - int err; - - err = git_tree_create_fromindex(&oid, self->index); - if (err < 0) - return Error_set(err); - - return git_oid_to_python(oid.id); -} - -static PyMethodDef Index_methods[] = { - {"add", (PyCFunction)Index_add, METH_VARARGS, - "Add or update an index entry from a file in disk."}, - {"clear", (PyCFunction)Index_clear, METH_NOARGS, - "Clear the contents (all the entries) of an index object."}, - {"_find", (PyCFunction)Index_find, METH_O, - "Find the first index of any entries which point to given path in the" - " Git index."}, - {"read", (PyCFunction)Index_read, METH_NOARGS, - "Update the contents of an existing index object in memory by reading" - " from the hard disk."}, - {"write", (PyCFunction)Index_write, METH_NOARGS, - "Write an existing index object from memory back to disk using an" - " atomic file lock."}, - {"read_tree", (PyCFunction)Index_read_tree, METH_O, - "Update the index file from the given tree object."}, - {"write_tree", (PyCFunction)Index_write_tree, METH_NOARGS, - "Create a tree object from the index file, return its oid."}, - {NULL} -}; - -static PySequenceMethods Index_as_sequence = { - 0, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - 0, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - (objobjproc)Index_contains, /* sq_contains */ -}; - -static PyMappingMethods Index_as_mapping = { - (lenfunc)Index_len, /* mp_length */ - (binaryfunc)Index_getitem, /* mp_subscript */ - (objobjargproc)Index_setitem, /* mp_ass_subscript */ -}; - -static PyTypeObject IndexType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Index", /* tp_name */ - sizeof(Index), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Index_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - &Index_as_sequence, /* tp_as_sequence */ - &Index_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | - Py_TPFLAGS_BASETYPE | - Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "Index file", /* tp_doc */ - (traverseproc)Index_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)Index_iter, /* tp_iter */ - 0, /* tp_iternext */ - Index_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Index_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - - -static void -IndexIter_dealloc(IndexIter *self) -{ - Py_CLEAR(self->owner); - PyObject_Del(self); -} - -static PyObject * -IndexIter_iternext(IndexIter *self) -{ - git_index_entry *index_entry; - - index_entry = git_index_get(self->owner->index, self->i); - if (!index_entry) - return NULL; - - self->i += 1; - return wrap_index_entry(index_entry, self->owner); -} - -static PyTypeObject IndexIterType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.IndexIter", /* tp_name */ - sizeof(IndexIter), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)IndexIter_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)IndexIter_iternext, /* tp_iternext */ -}; - -static void -IndexEntry_dealloc(IndexEntry *self) -{ - PyObject_Del(self); -} - -static PyObject * -IndexEntry_get_mode(IndexEntry *self) -{ - return PyInt_FromLong(self->entry->mode); -} - -static PyObject * -IndexEntry_get_path(IndexEntry *self) -{ - return to_path(self->entry->path); -} - -static PyObject * -IndexEntry_get_oid(IndexEntry *self) -{ - return git_oid_to_python(self->entry->oid.id); -} - -static PyObject * -IndexEntry_get_hex(IndexEntry *self) -{ - return git_oid_to_py_str(&self->entry->oid); -} - -static PyGetSetDef IndexEntry_getseters[] = { - {"mode", (getter)IndexEntry_get_mode, NULL, "mode", NULL}, - {"path", (getter)IndexEntry_get_path, NULL, "path", NULL}, - {"oid", (getter)IndexEntry_get_oid, NULL, "object id", NULL}, - {"hex", (getter)IndexEntry_get_hex, NULL, "hex oid", NULL}, - {NULL}, -}; - -static PyTypeObject IndexEntryType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.IndexEntry", /* tp_name */ - sizeof(IndexEntry), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)IndexEntry_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - "Index entry", /* 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 */ - IndexEntry_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 */ -}; - -static void -Walker_dealloc(Walker *self) -{ - git_revwalk_free(self->walk); - Py_DECREF(self->repo); - PyObject_Del(self); -} - -static PyObject * -Walker_hide(Walker *self, PyObject *py_hex) -{ - int err; - git_oid oid; - - err = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); - - if (err < 0) - return Error_set(err); - - err = git_revwalk_hide(self->walk, &oid); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; -} - -static PyObject * -Walker_push(Walker *self, PyObject *py_hex) -{ - int err; - git_oid oid; - - err = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); - if (err < 0) - return Error_set(err); - - err = git_revwalk_push(self->walk, &oid); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; -} - -static PyObject * -Walker_sort(Walker *self, PyObject *py_sort_mode) -{ - int sort_mode; - - sort_mode = (int)PyInt_AsLong(py_sort_mode); - if (sort_mode == -1 && PyErr_Occurred()) - return NULL; - - git_revwalk_sorting(self->walk, sort_mode); - - Py_RETURN_NONE; -} - -static PyObject * -Walker_reset(Walker *self) -{ - git_revwalk_reset(self->walk); - Py_RETURN_NONE; -} - -static PyObject * -Walker_iter(Walker *self) -{ - Py_INCREF(self); - return (PyObject*)self; -} - -static PyObject * -Walker_iternext(Walker *self) -{ - int err; - git_commit *commit; - Commit *py_commit; - git_oid oid; - - err = git_revwalk_next(&oid, self->walk); - if (err < 0) - return Error_set(err); - - err = git_commit_lookup(&commit, self->repo->repo, &oid); - if (err < 0) - return Error_set(err); - - py_commit = PyObject_New(Commit, &CommitType); - if (py_commit) { - py_commit->commit = commit; - Py_INCREF(self->repo); - py_commit->repo = self->repo; - } - return (PyObject*)py_commit; -} - -static PyMethodDef Walker_methods[] = { - {"hide", (PyCFunction)Walker_hide, METH_O, - "Mark a commit (and its ancestors) uninteresting for the output."}, - {"push", (PyCFunction)Walker_push, METH_O, - "Mark a commit to start traversal from."}, - {"reset", (PyCFunction)Walker_reset, METH_NOARGS, - "Reset the walking machinery for reuse."}, - {"sort", (PyCFunction)Walker_sort, METH_O, - "Change the sorting mode (this resets the walker)."}, - {NULL} -}; - -static PyTypeObject WalkerType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Walker", /* tp_name */ - sizeof(Walker), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Walker_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - "Revision walker", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)Walker_iter, /* tp_iter */ - (iternextfunc)Walker_iternext, /* tp_iternext */ - Walker_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* 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 */ -}; - -static void -Reference_dealloc(Reference *self) -{ - git_reference_free(self->reference); - PyObject_Del(self); -} - -static PyObject * -Reference_delete(Reference *self, PyObject *args) -{ - int err; - - CHECK_REFERENCE(self); - - /* Delete the reference */ - err = git_reference_delete(self->reference); - if (err < 0) - return Error_set(err); - - self->reference = NULL; /* Invalidate the pointer */ - Py_RETURN_NONE; /* Return None */ -} - -static PyObject * -Reference_rename(Reference *self, PyObject *py_name) -{ - char *c_name; - int err; - - CHECK_REFERENCE(self); - - /* Get the C name */ - c_name = py_path_to_c_str(py_name); - if (c_name == NULL) - return NULL; - - /* Rename */ - err = git_reference_rename(self->reference, c_name, 0); - free(c_name); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; /* Return None */ -} - -static PyObject * -Reference_reload(Reference *self) -{ - int err; - - CHECK_REFERENCE(self); - - err = git_reference_reload(self->reference); - if (err < 0) { - self->reference = NULL; - return Error_set(err); - } - - Py_RETURN_NONE; -} - - -static PyObject * -Reference_resolve(Reference *self, PyObject *args) -{ - git_reference *c_reference; - int err; - - CHECK_REFERENCE(self); - - /* Direct: reload */ - if (git_reference_type(self->reference) == GIT_REF_OID) { - err = git_reference_reload(self->reference); - if (err < 0) { - self->reference = NULL; - return Error_set(err); - } - Py_INCREF(self); - return (PyObject *)self; - } - - /* Symbolic: resolve */ - err = git_reference_resolve(&c_reference, self->reference); - if (err < 0) - return Error_set(err); - - return wrap_reference(c_reference); -} - -static PyObject * -Reference_get_target(Reference *self) -{ - const char * c_name; - - CHECK_REFERENCE(self); - - /* Get the target */ - c_name = git_reference_target(self->reference); - if (c_name == NULL) { - PyErr_SetString(PyExc_ValueError, "no target available"); - return NULL; - } - - /* Make a PyString and return it */ - return to_path(c_name); -} - -static int -Reference_set_target(Reference *self, PyObject *py_name) -{ - char *c_name; - int err; - - CHECK_REFERENCE_INT(self); - - /* Get the C name */ - c_name = py_path_to_c_str(py_name); - if (c_name == NULL) - return -1; - - /* Set the new target */ - err = git_reference_set_target(self->reference, c_name); - free(c_name); - if (err < 0) { - Error_set(err); - return -1; - } - - return 0; -} - -static PyObject * -Reference_get_name(Reference *self) -{ - CHECK_REFERENCE(self); - return to_path(git_reference_name(self->reference)); -} - -static PyObject * -Reference_get_oid(Reference *self) -{ - const git_oid *oid; - - CHECK_REFERENCE(self); - - /* Get the oid (only for "direct" references) */ - oid = git_reference_oid(self->reference); - if (oid == NULL) { - PyErr_SetString(PyExc_ValueError, - "oid is only available if the reference is direct " - "(i.e. not symbolic)"); - return NULL; - } - - /* Convert and return it */ - return git_oid_to_python(oid->id); -} - -static int -Reference_set_oid(Reference *self, PyObject *py_hex) -{ - git_oid oid; - int err; - - CHECK_REFERENCE_INT(self); - - /* Get the oid */ - err = py_str_to_git_oid_expand(git_reference_owner(self->reference), py_hex, &oid); - if (err < 0) { - Error_set(err); - return -1; - } - - /* Set the oid */ - err = git_reference_set_oid(self->reference, &oid); - if (err < 0) { - Error_set(err); - return -1; - } - - return 0; -} - -static PyObject * -Reference_get_hex(Reference *self) -{ - const git_oid *oid; - - CHECK_REFERENCE(self); - - /* Get the oid (only for "direct" references) */ - oid = git_reference_oid(self->reference); - if (oid == NULL) { - PyErr_SetString(PyExc_ValueError, - "oid is only available if the reference is direct " - "(i.e. not symbolic)"); - return NULL; - } - - /* Convert and return it */ - return git_oid_to_py_str(oid); -} - -static PyObject * -Reference_get_type(Reference *self) -{ - git_ref_t c_type; - - CHECK_REFERENCE(self); - c_type = git_reference_type(self->reference); - return PyInt_FromLong(c_type); -} - -static PyMethodDef Reference_methods[] = { - {"delete", (PyCFunction)Reference_delete, METH_NOARGS, - "Delete this reference. It will no longer be valid!"}, - {"rename", (PyCFunction)Reference_rename, METH_O, - "Rename the reference."}, - {"reload", (PyCFunction)Reference_reload, METH_NOARGS, - "Reload the reference from the file-system."}, - {"resolve", (PyCFunction)Reference_resolve, METH_NOARGS, - "Resolve a symbolic reference and return a direct reference."}, - {NULL} -}; - -static PyGetSetDef Reference_getseters[] = { - {"name", (getter)Reference_get_name, NULL, - "The full name of a reference.", NULL}, - {"oid", (getter)Reference_get_oid, (setter)Reference_set_oid, "object id", - NULL}, - {"hex", (getter)Reference_get_hex, NULL, "hex oid", NULL}, - {"target", (getter)Reference_get_target, (setter)Reference_set_target, - "target", NULL}, - {"type", (getter)Reference_get_type, NULL, - "type (GIT_REF_OID, GIT_REF_SYMBOLIC or GIT_REF_PACKED).", NULL}, - {NULL} -}; - -static PyTypeObject ReferenceType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Reference", /* tp_name */ - sizeof(Reference), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Reference_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - "Reference", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Reference_methods, /* tp_methods */ - 0, /* tp_members */ - Reference_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 */ -}; - -static int -Signature_init(Signature *self, PyObject *args, PyObject *kwds) -{ - PyObject *py_name; - char *name, *email, *encoding = NULL; - long long time; - int offset; - int err; - git_signature *signature; - - if (kwds) { - PyErr_SetString(PyExc_TypeError, - "Signature takes no keyword arguments"); - return -1; - } - - if (!PyArg_ParseTuple(args, "OsLi|s", - &py_name, &email, &time, &offset, &encoding)) - return -1; - - name = py_str_to_c_str(py_name, encoding); - if (name == NULL) - return -1; - - err = git_signature_new(&signature, name, email, time, offset); - free(name); - if (err < 0) { - Error_set(err); - return -1; - } - - self->obj = NULL; - self->signature = signature; - - if (encoding) { - self->encoding = strdup(encoding); - if (self->encoding == NULL) { - PyErr_NoMemory(); - return -1; - } - } - - return 0; -} - -static void -Signature_dealloc(Signature *self) -{ - if (self->obj) - Py_DECREF(self->obj); - else { - git_signature_free((git_signature*)self->signature); - free((void*)self->encoding); - } - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static PyObject * -Signature_get_encoding(Signature *self) -{ - const char *encoding; - - encoding = self->encoding; - if (encoding == NULL) - encoding = "utf-8"; - - return to_encoding(encoding); -} - -static PyObject * -Signature_get_raw_name(Signature *self) -{ - return to_bytes(self->signature->name); -} - -static PyObject * -Signature_get_raw_email(Signature *self) -{ - return to_bytes(self->signature->email); -} - -static PyObject * -Signature_get_name(Signature *self) -{ - return to_unicode(self->signature->name, self->encoding, "strict"); -} - -static PyObject * -Signature_get_email(Signature *self) -{ - return to_unicode(self->signature->email, self->encoding, "strict"); -} - -static PyObject * -Signature_get_time(Signature *self) -{ - return PyInt_FromLong(self->signature->when.time); -} - -static PyObject * -Signature_get_offset(Signature *self) -{ - return PyInt_FromLong(self->signature->when.offset); -} - -static PyGetSetDef Signature_getseters[] = { - {"_encoding", (getter)Signature_get_encoding, NULL, "encoding", NULL}, - {"_name", (getter)Signature_get_raw_name, NULL, "Name (bytes)", NULL}, - {"_email", (getter)Signature_get_raw_email, NULL, "Email (bytes)", NULL}, - {"name", (getter)Signature_get_name, NULL, "Name", NULL}, - {"email", (getter)Signature_get_email, NULL, "Email", NULL}, - {"time", (getter)Signature_get_time, NULL, "Time", NULL}, - {"offset", (getter)Signature_get_offset, NULL, "Offset", NULL}, - {NULL} -}; - -static PyTypeObject SignatureType = { - PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Signature", /* tp_name */ - sizeof(Signature), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Signature_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - "Signature", /* 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 */ - Signature_getseters, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Signature_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - -static PyObject * -init_repository(PyObject *self, PyObject *args) -{ - git_repository *repo; - Repository *py_repo; - const char *path; - unsigned int bare; - int err; - - if (!PyArg_ParseTuple(args, "sI", &path, &bare)) - return NULL; - - err = git_repository_init(&repo, path, bare); - if (err < 0) - return Error_set_str(err, path); - - py_repo = PyObject_GC_New(Repository, &RepositoryType); - if (py_repo) { - py_repo->repo = repo; - py_repo->index = NULL; - PyObject_GC_Track(py_repo); - return (PyObject*)py_repo; - } - - git_repository_free(repo); - return NULL; -}; - -static PyObject * -discover_repository(PyObject *self, PyObject *args) -{ - const char *path; - int across_fs = 0; - const char *ceiling_dirs = NULL; - char repo_path[MAXPATHLEN]; - int err; - - if (!PyArg_ParseTuple(args, "s|Is", &path, &across_fs, &ceiling_dirs)) - return NULL; - - err = git_repository_discover(repo_path, sizeof(repo_path), - path, across_fs, ceiling_dirs); - if (err < 0) - return Error_set_str(err, path); - - return to_path(repo_path); -}; - -static PyMethodDef module_methods[] = { - {"init_repository", init_repository, METH_VARARGS, - "Creates a new Git repository in the given folder."}, - {"discover_repository", discover_repository, METH_VARARGS, - "Look for a git repository and return its path."}, - {NULL} -}; - -PyObject* -moduleinit(PyObject* m) -{ - if (m == NULL) - return NULL; - - GitError = PyErr_NewException("pygit2.GitError", NULL, NULL); - - RepositoryType.tp_new = PyType_GenericNew; - if (PyType_Ready(&RepositoryType) < 0) - return NULL; - - /* Do not set 'tp_new' for Git objects. To create Git objects use the - * Repository.create_XXX methods */ - if (PyType_Ready(&ObjectType) < 0) - return NULL; - CommitType.tp_base = &ObjectType; - if (PyType_Ready(&CommitType) < 0) - return NULL; - TreeType.tp_base = &ObjectType; - if (PyType_Ready(&TreeType) < 0) - return NULL; - BlobType.tp_base = &ObjectType; - if (PyType_Ready(&BlobType) < 0) - return NULL; - TagType.tp_base = &ObjectType; - if (PyType_Ready(&TagType) < 0) - return NULL; - - TreeEntryType.tp_new = PyType_GenericNew; - if (PyType_Ready(&TreeEntryType) < 0) - return NULL; - IndexType.tp_new = PyType_GenericNew; - if (PyType_Ready(&IndexType) < 0) - return NULL; - IndexEntryType.tp_new = PyType_GenericNew; - if (PyType_Ready(&IndexEntryType) < 0) - return NULL; - TreeBuilderType.tp_new = PyType_GenericNew; - if (PyType_Ready(&TreeBuilderType) < 0) - return NULL; - WalkerType.tp_new = PyType_GenericNew; - if (PyType_Ready(&WalkerType) < 0) - return NULL; - ReferenceType.tp_new = PyType_GenericNew; - if (PyType_Ready(&ReferenceType) < 0) - return NULL; - SignatureType.tp_new = PyType_GenericNew; - if (PyType_Ready(&SignatureType) < 0) - return NULL; - - Py_INCREF(GitError); - PyModule_AddObject(m, "GitError", GitError); - - Py_INCREF(&RepositoryType); - PyModule_AddObject(m, "Repository", (PyObject *)&RepositoryType); - - Py_INCREF(&ObjectType); - PyModule_AddObject(m, "Object", (PyObject *)&ObjectType); - - Py_INCREF(&CommitType); - PyModule_AddObject(m, "Commit", (PyObject *)&CommitType); - - Py_INCREF(&TreeEntryType); - PyModule_AddObject(m, "TreeEntry", (PyObject *)&TreeEntryType); - - Py_INCREF(&TreeType); - PyModule_AddObject(m, "Tree", (PyObject *)&TreeType); - - Py_INCREF(&BlobType); - PyModule_AddObject(m, "Blob", (PyObject *)&BlobType); - - Py_INCREF(&TagType); - PyModule_AddObject(m, "Tag", (PyObject *)&TagType); - - Py_INCREF(&IndexType); - PyModule_AddObject(m, "Index", (PyObject *)&IndexType); - - Py_INCREF(&IndexEntryType); - PyModule_AddObject(m, "IndexEntry", (PyObject *)&IndexEntryType); - - Py_INCREF(&ReferenceType); - PyModule_AddObject(m, "Reference", (PyObject *)&ReferenceType); - - Py_INCREF(&SignatureType); - PyModule_AddObject(m, "Signature", (PyObject *)&SignatureType); - - 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); - PyModule_AddIntConstant(m, "GIT_OBJ_BLOB", GIT_OBJ_BLOB); - PyModule_AddIntConstant(m, "GIT_OBJ_TAG", GIT_OBJ_TAG); - PyModule_AddIntConstant(m, "GIT_SORT_NONE", GIT_SORT_NONE); - PyModule_AddIntConstant(m, "GIT_SORT_TOPOLOGICAL", GIT_SORT_TOPOLOGICAL); - PyModule_AddIntConstant(m, "GIT_SORT_TIME", GIT_SORT_TIME); - PyModule_AddIntConstant(m, "GIT_SORT_REVERSE", GIT_SORT_REVERSE); - PyModule_AddIntConstant(m, "GIT_REF_OID", GIT_REF_OID); - PyModule_AddIntConstant(m, "GIT_REF_SYMBOLIC", GIT_REF_SYMBOLIC); - PyModule_AddIntConstant(m, "GIT_REF_PACKED", GIT_REF_PACKED); - - /* Git status flags */ - PyModule_AddIntConstant(m, "GIT_STATUS_CURRENT", GIT_STATUS_CURRENT); - PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_NEW", GIT_STATUS_INDEX_NEW); - PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_MODIFIED", - GIT_STATUS_INDEX_MODIFIED); - PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_DELETED" , - GIT_STATUS_INDEX_DELETED); - PyModule_AddIntConstant(m, "GIT_STATUS_WT_NEW", GIT_STATUS_WT_NEW); - PyModule_AddIntConstant(m, "GIT_STATUS_WT_MODIFIED" , - GIT_STATUS_WT_MODIFIED); - PyModule_AddIntConstant(m, "GIT_STATUS_WT_DELETED", GIT_STATUS_WT_DELETED); - - /* Flags for ignored files */ - PyModule_AddIntConstant(m, "GIT_STATUS_IGNORED", GIT_STATUS_IGNORED); - - return m; -} - - -#if PY_MAJOR_VERSION < 3 - PyMODINIT_FUNC - initpygit2(void) - { - PyObject* m; - m = Py_InitModule3("pygit2", module_methods, - "Python bindings for libgit2."); - moduleinit(m); - } -#else - static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "pygit2", /* m_name */ - "Python bindings for libgit2.", /* m_doc */ - -1, /* m_size */ - module_methods, /* m_methods */ - NULL, /* m_reload */ - NULL, /* m_traverse */ - NULL, /* m_clear */ - NULL, /* m_free */ - }; - - PyMODINIT_FUNC - PyInit_pygit2(void) - { - PyObject* m; - m = PyModule_Create(&moduledef); - return moduleinit(m); - } -#endif diff --git a/setup.py b/setup.py index de76973ae..9a6db3129 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,10 @@ libgit2_bin = os.path.join(libgit2_path, 'bin') libgit2_include = os.path.join(libgit2_path, 'include') libgit2_lib = os.path.join(libgit2_path, 'lib') +pygit2_exts = [os.path.join('src', 'pygit2.c')] + [ + os.path.join('src', 'pygit2', entry) + for entry in os.listdir('src/pygit2') + if entry.endswith('.c')] class TestCommand(Command): """Command for running unittests without install.""" @@ -139,8 +143,8 @@ def run(self): maintainer_email='jdavid.ibp@gmail.com', long_description=long_description, ext_modules=[ - Extension('pygit2', ['pygit2.c'], - include_dirs=[libgit2_include], + Extension('pygit2', pygit2_exts, + include_dirs=[libgit2_include, 'include'], library_dirs=[libgit2_lib], libraries=['git2']), ], diff --git a/src/pygit2.c b/src/pygit2.c new file mode 100644 index 000000000..2f7fe0bfa --- /dev/null +++ b/src/pygit2.c @@ -0,0 +1,261 @@ +/* + * Copyright 2010 Google, Inc. + * Copyright 2011 Itaapy + * + * 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 +#include +#include + +extern PyObject *GitError; + +PyTypeObject RepositoryType; +PyTypeObject ObjectType; +PyTypeObject CommitType; +PyTypeObject TreeType; +PyTypeObject TreeBuilderType; +PyTypeObject TreeEntryType; +PyTypeObject TreeIterType; +PyTypeObject BlobType; +PyTypeObject TagType; +PyTypeObject IndexType; +PyTypeObject IndexEntryType; +PyTypeObject IndexIterType; +PyTypeObject WalkerType; +PyTypeObject ReferenceType; +PyTypeObject SignatureType; + + +PyObject * +init_repository(PyObject *self, PyObject *args) +{ + git_repository *repo; + Repository *py_repo; + const char *path; + unsigned int bare; + int err; + + if (!PyArg_ParseTuple(args, "sI", &path, &bare)) + return NULL; + + err = git_repository_init(&repo, path, bare); + if (err < 0) + return Error_set_str(err, path); + + py_repo = PyObject_GC_New(Repository, &RepositoryType); + if (py_repo) { + py_repo->repo = repo; + py_repo->index = NULL; + PyObject_GC_Track(py_repo); + return (PyObject*)py_repo; + } + + git_repository_free(repo); + return NULL; +}; + +PyObject * +discover_repository(PyObject *self, PyObject *args) +{ + const char *path; + int across_fs = 0; + const char *ceiling_dirs = NULL; + char repo_path[MAXPATHLEN]; + int err; + + if (!PyArg_ParseTuple(args, "s|Is", &path, &across_fs, &ceiling_dirs)) + return NULL; + + err = git_repository_discover(repo_path, sizeof(repo_path), + path, across_fs, ceiling_dirs); + if (err < 0) + return Error_set_str(err, path); + + return to_path(repo_path); +}; + +PyMethodDef module_methods[] = { + {"init_repository", init_repository, METH_VARARGS, + "Creates a new Git repository in the given folder."}, + {"discover_repository", discover_repository, METH_VARARGS, + "Look for a git repository and return its path."}, + {NULL} +}; + +PyObject* +moduleinit(PyObject* m) +{ + if (m == NULL) + return NULL; + + GitError = PyErr_NewException("pygit2.GitError", NULL, NULL); + + RepositoryType.tp_new = PyType_GenericNew; + if (PyType_Ready(&RepositoryType) < 0) + return NULL; + + /* Do not set 'tp_new' for Git objects. To create Git objects use the + * Repository.create_XXX methods */ + if (PyType_Ready(&ObjectType) < 0) + return NULL; + CommitType.tp_base = &ObjectType; + if (PyType_Ready(&CommitType) < 0) + return NULL; + TreeType.tp_base = &ObjectType; + if (PyType_Ready(&TreeType) < 0) + return NULL; + BlobType.tp_base = &ObjectType; + if (PyType_Ready(&BlobType) < 0) + return NULL; + TagType.tp_base = &ObjectType; + if (PyType_Ready(&TagType) < 0) + return NULL; + + TreeEntryType.tp_new = PyType_GenericNew; + if (PyType_Ready(&TreeEntryType) < 0) + return NULL; + IndexType.tp_new = PyType_GenericNew; + if (PyType_Ready(&IndexType) < 0) + return NULL; + IndexEntryType.tp_new = PyType_GenericNew; + if (PyType_Ready(&IndexEntryType) < 0) + return NULL; + TreeBuilderType.tp_new = PyType_GenericNew; + if (PyType_Ready(&TreeBuilderType) < 0) + return NULL; + WalkerType.tp_new = PyType_GenericNew; + if (PyType_Ready(&WalkerType) < 0) + return NULL; + ReferenceType.tp_new = PyType_GenericNew; + if (PyType_Ready(&ReferenceType) < 0) + return NULL; + SignatureType.tp_new = PyType_GenericNew; + if (PyType_Ready(&SignatureType) < 0) + return NULL; + + Py_INCREF(GitError); + PyModule_AddObject(m, "GitError", GitError); + + Py_INCREF(&RepositoryType); + PyModule_AddObject(m, "Repository", (PyObject *)&RepositoryType); + + Py_INCREF(&ObjectType); + PyModule_AddObject(m, "Object", (PyObject *)&ObjectType); + + Py_INCREF(&CommitType); + PyModule_AddObject(m, "Commit", (PyObject *)&CommitType); + + Py_INCREF(&TreeEntryType); + PyModule_AddObject(m, "TreeEntry", (PyObject *)&TreeEntryType); + + Py_INCREF(&TreeType); + PyModule_AddObject(m, "Tree", (PyObject *)&TreeType); + + Py_INCREF(&BlobType); + PyModule_AddObject(m, "Blob", (PyObject *)&BlobType); + + Py_INCREF(&TagType); + PyModule_AddObject(m, "Tag", (PyObject *)&TagType); + + Py_INCREF(&IndexType); + PyModule_AddObject(m, "Index", (PyObject *)&IndexType); + + Py_INCREF(&IndexEntryType); + PyModule_AddObject(m, "IndexEntry", (PyObject *)&IndexEntryType); + + Py_INCREF(&ReferenceType); + PyModule_AddObject(m, "Reference", (PyObject *)&ReferenceType); + + Py_INCREF(&SignatureType); + PyModule_AddObject(m, "Signature", (PyObject *)&SignatureType); + + 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); + PyModule_AddIntConstant(m, "GIT_OBJ_BLOB", GIT_OBJ_BLOB); + PyModule_AddIntConstant(m, "GIT_OBJ_TAG", GIT_OBJ_TAG); + PyModule_AddIntConstant(m, "GIT_SORT_NONE", GIT_SORT_NONE); + PyModule_AddIntConstant(m, "GIT_SORT_TOPOLOGICAL", GIT_SORT_TOPOLOGICAL); + PyModule_AddIntConstant(m, "GIT_SORT_TIME", GIT_SORT_TIME); + PyModule_AddIntConstant(m, "GIT_SORT_REVERSE", GIT_SORT_REVERSE); + PyModule_AddIntConstant(m, "GIT_REF_OID", GIT_REF_OID); + PyModule_AddIntConstant(m, "GIT_REF_SYMBOLIC", GIT_REF_SYMBOLIC); + PyModule_AddIntConstant(m, "GIT_REF_PACKED", GIT_REF_PACKED); + + /* Git status flags */ + PyModule_AddIntConstant(m, "GIT_STATUS_CURRENT", GIT_STATUS_CURRENT); + PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_NEW", GIT_STATUS_INDEX_NEW); + PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_MODIFIED", + GIT_STATUS_INDEX_MODIFIED); + PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_DELETED" , + GIT_STATUS_INDEX_DELETED); + PyModule_AddIntConstant(m, "GIT_STATUS_WT_NEW", GIT_STATUS_WT_NEW); + PyModule_AddIntConstant(m, "GIT_STATUS_WT_MODIFIED" , + GIT_STATUS_WT_MODIFIED); + PyModule_AddIntConstant(m, "GIT_STATUS_WT_DELETED", GIT_STATUS_WT_DELETED); + + /* Flags for ignored files */ + PyModule_AddIntConstant(m, "GIT_STATUS_IGNORED", GIT_STATUS_IGNORED); + + return m; +} + + +#if PY_MAJOR_VERSION < 3 + PyMODINIT_FUNC + initpygit2(void) + { + PyObject* m; + m = Py_InitModule3("pygit2", module_methods, + "Python bindings for libgit2."); + moduleinit(m); + } +#else + struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "pygit2", /* m_name */ + "Python bindings for libgit2.", /* m_doc */ + -1, /* m_size */ + module_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ + }; + + PyMODINIT_FUNC + PyInit_pygit2(void) + { + PyObject* m; + m = PyModule_Create(&moduledef); + return moduleinit(m); + } +#endif diff --git a/src/pygit2/blob.c b/src/pygit2/blob.c new file mode 100644 index 000000000..f4ac8f443 --- /dev/null +++ b/src/pygit2/blob.c @@ -0,0 +1,49 @@ +#include +#include + +PyGetSetDef Blob_getseters[] = { + {"data", (getter)Object_read_raw, NULL, "raw data", NULL}, + {NULL} +}; + +PyTypeObject BlobType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Blob", /* tp_name */ + sizeof(Blob), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* 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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Blob objects", /* 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 */ + Blob_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/pygit2/commit.c b/src/pygit2/commit.c new file mode 100644 index 000000000..de4c1100f --- /dev/null +++ b/src/pygit2/commit.c @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include +#include +#include + +extern PyTypeObject TreeType; + +PyObject * +Commit_get_message_encoding(Commit *commit) +{ + const char *encoding; + + encoding = git_commit_message_encoding(commit->commit); + if (encoding == NULL) + Py_RETURN_NONE; + + return to_encoding(encoding); +} + +PyObject * +Commit_get_message(Commit *commit) +{ + const char *message, *encoding; + + message = git_commit_message(commit->commit); + encoding = git_commit_message_encoding(commit->commit); + return to_unicode(message, encoding, "strict"); +} + +PyObject * +Commit_get_raw_message(Commit *commit) +{ + return PyString_FromString(git_commit_message(commit->commit)); +} + +PyObject * +Commit_get_commit_time(Commit *commit) +{ + return PyLong_FromLong(git_commit_time(commit->commit)); +} + +PyObject * +Commit_get_commit_time_offset(Commit *commit) +{ + return PyLong_FromLong(git_commit_time_offset(commit->commit)); +} + +PyObject * +Commit_get_committer(Commit *self) +{ + const git_signature *signature; + const char *encoding; + + signature = git_commit_committer(self->commit); + encoding = git_commit_message_encoding(self->commit); + + return build_signature((Object*)self, signature, encoding); +} + +PyObject * +Commit_get_author(Commit *self) +{ + const git_signature *signature; + const char *encoding; + + signature = git_commit_author(self->commit); + encoding = git_commit_message_encoding(self->commit); + + return build_signature((Object*)self, signature, encoding); +} + +PyObject * +Commit_get_tree(Commit *commit) +{ + git_tree *tree; + Tree *py_tree; + int err; + + err = git_commit_tree(&tree, commit->commit); + if (err == GIT_ENOTFOUND) + Py_RETURN_NONE; + + if (err < 0) + return Error_set(err); + + py_tree = PyObject_New(Tree, &TreeType); + if (py_tree) { + Py_INCREF(commit->repo); + py_tree->repo = commit->repo; + py_tree->tree = (git_tree*)tree; + } + return (PyObject*)py_tree; +} + +PyObject * +Commit_get_parents(Commit *commit) +{ + unsigned int i, parent_count; + const git_oid *parent_oid; + PyObject *obj; + PyObject *list; + + parent_count = git_commit_parentcount(commit->commit); + list = PyList_New(parent_count); + if (!list) + return NULL; + + for (i=0; i < parent_count; i++) { + parent_oid = git_commit_parent_oid(commit->commit, i); + if (parent_oid == NULL) { + Py_DECREF(list); + Error_set(GIT_ENOTFOUND); + return NULL; + } + obj = lookup_object(commit->repo, parent_oid, GIT_OBJ_COMMIT); + if (obj == NULL) { + Py_DECREF(list); + return NULL; + } + + PyList_SET_ITEM(list, i, obj); + } + + return list; +} + +PyGetSetDef Commit_getseters[] = { + {"message_encoding", (getter)Commit_get_message_encoding, NULL, + "message encoding", NULL}, + {"message", (getter)Commit_get_message, NULL, "message", NULL}, + {"_message", (getter)Commit_get_raw_message, NULL, "message (bytes)", NULL}, + {"commit_time", (getter)Commit_get_commit_time, NULL, "commit time", + NULL}, + {"commit_time_offset", (getter)Commit_get_commit_time_offset, NULL, + "commit time offset", NULL}, + {"committer", (getter)Commit_get_committer, NULL, "committer", NULL}, + {"author", (getter)Commit_get_author, NULL, "author", NULL}, + {"tree", (getter)Commit_get_tree, NULL, "tree object", NULL}, + {"parents", (getter)Commit_get_parents, NULL, "parents of this commit", + NULL}, + {NULL} +}; + +PyTypeObject CommitType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Commit", /* tp_name */ + sizeof(Commit), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* 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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Commit objects", /* 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 */ + Commit_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/pygit2/error.c b/src/pygit2/error.c new file mode 100644 index 000000000..45fd02a65 --- /dev/null +++ b/src/pygit2/error.c @@ -0,0 +1,82 @@ +#include + +PyObject *GitError; + +PyObject * Error_type(int type) +{ + // Expected + switch (type) { + /** Input does not exist in the scope searched. */ + case GIT_ENOTFOUND: + return PyExc_KeyError; + + /** A reference with this name already exists */ + case GIT_EEXISTS: + return PyExc_ValueError; + + /** The given short oid is ambiguous */ + case GIT_EAMBIGUOUS: + return PyExc_ValueError; + + /** The buffer is too short to satisfy the request */ + case GIT_EBUFS: + return PyExc_ValueError; + + /** Skip and passthrough the given ODB backend */ + case GIT_PASSTHROUGH: + return GitError; + + /** No entries left in ref walker */ + case GIT_REVWALKOVER: + return PyExc_StopIteration; + } + + // Critical + const git_error* error = giterr_last(); + switch (error->klass) { + case GITERR_NOMEMORY: + return PyExc_MemoryError; + case GITERR_OS: + return PyExc_OSError; + case GITERR_INVALID: + return PyExc_ValueError; + default: + return GitError; + } +} + + +PyObject* Error_set(int err) +{ + assert(err < 0); + + if(err != GIT_ERROR) { //expected failure + PyErr_SetNone(Error_type(err)); + } else { //critical failure + const git_error* error = giterr_last(); + PyErr_SetString(Error_type(err), error->message); + } + + return NULL; +} + +PyObject* Error_set_str(int err, const char *str) +{ + if (err == GIT_ENOTFOUND) { + /* KeyError expects the arg to be the missing key. */ + PyErr_SetString(PyExc_KeyError, str); + return NULL; + } + + const git_error* error = giterr_last(); + return PyErr_Format(Error_type(err), "%s: %s", str, error->message); +} + +PyObject* Error_set_oid(int err, const git_oid *oid, size_t len) +{ + char hex[GIT_OID_HEXSZ + 1]; + + git_oid_fmt(hex, oid); + hex[len] = '\0'; + return Error_set_str(err, hex); +} diff --git a/src/pygit2/index.c b/src/pygit2/index.c new file mode 100644 index 000000000..256b778b6 --- /dev/null +++ b/src/pygit2/index.c @@ -0,0 +1,492 @@ +#include +#include +#include +#include +#include +#include + +extern PyTypeObject IndexType; +extern PyTypeObject IndexIterType; +extern PyTypeObject IndexEntryType; + +int +Index_init(Index *self, PyObject *args, PyObject *kwds) +{ + char *path; + int err; + + if (kwds) { + PyErr_SetString(PyExc_TypeError, + "Index takes no keyword arguments"); + return -1; + } + + if (!PyArg_ParseTuple(args, "s", &path)) + return -1; + + err = git_index_open(&self->index, path); + if (err < 0) { + Error_set_str(err, path); + return -1; + } + + return 0; +} + +void +Index_dealloc(Index* self) +{ + PyObject_GC_UnTrack(self); + Py_XDECREF(self->repo); + git_index_free(self->index); + PyObject_GC_Del(self); +} + +int +Index_traverse(Index *self, visitproc visit, void *arg) +{ + Py_VISIT(self->repo); + return 0; +} + +PyObject * +Index_add(Index *self, PyObject *args) +{ + int err; + const char *path; + int stage=0; + + if (!PyArg_ParseTuple(args, "s|i", &path, &stage)) + return NULL; + + err = git_index_add(self->index, path, stage); + if (err < 0) + return Error_set_str(err, path); + + Py_RETURN_NONE; +} + +PyObject * +Index_clear(Index *self) +{ + git_index_clear(self->index); + Py_RETURN_NONE; +} + +PyObject * +Index_find(Index *self, PyObject *py_path) +{ + char *path; + long idx; + + path = PyString_AsString(py_path); + if (!path) + return NULL; + + idx = (long)git_index_find(self->index, path); + if (idx < 0) + return Error_set_str(idx, path); + + return PyInt_FromLong(idx); +} + +PyObject * +Index_read(Index *self) +{ + int err; + + err = git_index_read(self->index); + if (err < GIT_OK) + return Error_set(err); + + Py_RETURN_NONE; +} + +PyObject * +Index_write(Index *self) +{ + int err; + + err = git_index_write(self->index); + if (err < GIT_OK) + return Error_set(err); + + Py_RETURN_NONE; +} + +/* This is an internal function, used by Index_getitem and Index_setitem */ +int +Index_get_position(Index *self, PyObject *value) +{ + char *path; + int idx; + + /* Case 1: integer */ + if (PyInt_Check(value)) { + idx = (int)PyInt_AsLong(value); + if (idx == -1 && PyErr_Occurred()) + return -1; + if (idx < 0) { + PyErr_SetObject(PyExc_ValueError, value); + return -1; + } + return idx; + } + + /* Case 2: byte or text string */ + path = py_path_to_c_str(value); + if (!path) + return -1; + idx = git_index_find(self->index, path); + if (idx < 0) { + Error_set_str(idx, path); + free(path); + return -1; + } + return idx; +} + +int +Index_contains(Index *self, PyObject *value) +{ + char *path; + int idx; + + path = py_path_to_c_str(value); + if (!path) + return -1; + idx = git_index_find(self->index, path); + if (idx == GIT_ENOTFOUND) + return 0; + if (idx < 0) { + Error_set_str(idx, path); + free(path); + return -1; + } + + return 1; +} + +PyObject * +Index_iter(Index *self) +{ + IndexIter *iter; + + iter = PyObject_New(IndexIter, &IndexIterType); + if (iter) { + Py_INCREF(self); + iter->owner = self; + iter->i = 0; + } + return (PyObject*)iter; +} + +Py_ssize_t +Index_len(Index *self) +{ + return (Py_ssize_t)git_index_entrycount(self->index); +} + +PyObject * +wrap_index_entry(git_index_entry *entry, Index *index) +{ + IndexEntry *py_entry; + + py_entry = PyObject_New(IndexEntry, &IndexEntryType); + if (py_entry) + py_entry->entry = entry; + + return (PyObject*)py_entry; +} + +PyObject * +Index_getitem(Index *self, PyObject *value) +{ + int idx; + git_index_entry *index_entry; + + idx = Index_get_position(self, value); + if (idx == -1) + return NULL; + + index_entry = git_index_get(self->index, idx); + if (!index_entry) { + PyErr_SetObject(PyExc_KeyError, value); + return NULL; + } + + return wrap_index_entry(index_entry, self); +} + +int +Index_setitem(Index *self, PyObject *key, PyObject *value) +{ + int err; + int idx; + + if (value) { + PyErr_SetString(PyExc_NotImplementedError, + "set item on index not yet implemented"); + return -1; + } + + idx = Index_get_position(self, key); + if (idx == -1) + return -1; + + err = git_index_remove(self->index, idx); + if (err < 0) { + Error_set(err); + return -1; + } + + return 0; +} + +PyObject * +Index_read_tree(Index *self, PyObject *value) +{ + git_oid oid; + git_tree *tree; + int err, len; + + len = py_str_to_git_oid(value, &oid); + if (len < 0) + return NULL; + + err = git_tree_lookup_prefix(&tree, self->repo->repo, &oid, + (unsigned int)len); + if (err < 0) + return Error_set(err); + + err = git_index_read_tree(self->index, tree); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + +PyObject * +Index_write_tree(Index *self) +{ + git_oid oid; + int err; + + err = git_tree_create_fromindex(&oid, self->index); + if (err < 0) + return Error_set(err); + + return git_oid_to_python(oid.id); +} + +PyMethodDef Index_methods[] = { + {"add", (PyCFunction)Index_add, METH_VARARGS, + "Add or update an index entry from a file in disk."}, + {"clear", (PyCFunction)Index_clear, METH_NOARGS, + "Clear the contents (all the entries) of an index object."}, + {"_find", (PyCFunction)Index_find, METH_O, + "Find the first index of any entries which point to given path in the" + " Git index."}, + {"read", (PyCFunction)Index_read, METH_NOARGS, + "Update the contents of an existing index object in memory by reading" + " from the hard disk."}, + {"write", (PyCFunction)Index_write, METH_NOARGS, + "Write an existing index object from memory back to disk using an" + " atomic file lock."}, + {"read_tree", (PyCFunction)Index_read_tree, METH_O, + "Update the index file from the given tree object."}, + {"write_tree", (PyCFunction)Index_write_tree, METH_NOARGS, + "Create a tree object from the index file, return its oid."}, + {NULL} +}; + +PySequenceMethods Index_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)Index_contains, /* sq_contains */ +}; + +PyMappingMethods Index_as_mapping = { + (lenfunc)Index_len, /* mp_length */ + (binaryfunc)Index_getitem, /* mp_subscript */ + (objobjargproc)Index_setitem, /* mp_ass_subscript */ +}; + +PyTypeObject IndexType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Index", /* tp_name */ + sizeof(Index), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Index_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &Index_as_sequence, /* tp_as_sequence */ + &Index_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC, /* tp_flags */ + "Index file", /* tp_doc */ + (traverseproc)Index_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)Index_iter, /* tp_iter */ + 0, /* tp_iternext */ + Index_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Index_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +void +IndexIter_dealloc(IndexIter *self) +{ + Py_CLEAR(self->owner); + PyObject_Del(self); +} + +PyObject * +IndexIter_iternext(IndexIter *self) +{ + git_index_entry *index_entry; + + index_entry = git_index_get(self->owner->index, self->i); + if (!index_entry) + return NULL; + + self->i += 1; + return wrap_index_entry(index_entry, self->owner); +} + +PyTypeObject IndexIterType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.IndexIter", /* tp_name */ + sizeof(IndexIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)IndexIter_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)IndexIter_iternext, /* tp_iternext */ +}; + +void +IndexEntry_dealloc(IndexEntry *self) +{ + PyObject_Del(self); +} + +PyObject * +IndexEntry_get_mode(IndexEntry *self) +{ + return PyInt_FromLong(self->entry->mode); +} + +PyObject * +IndexEntry_get_path(IndexEntry *self) +{ + return to_path(self->entry->path); +} + +PyObject * +IndexEntry_get_oid(IndexEntry *self) +{ + return git_oid_to_python(self->entry->oid.id); +} + +PyObject * +IndexEntry_get_hex(IndexEntry *self) +{ + return git_oid_to_py_str(&self->entry->oid); +} + +PyGetSetDef IndexEntry_getseters[] = { + {"mode", (getter)IndexEntry_get_mode, NULL, "mode", NULL}, + {"path", (getter)IndexEntry_get_path, NULL, "path", NULL}, + {"oid", (getter)IndexEntry_get_oid, NULL, "object id", NULL}, + {"hex", (getter)IndexEntry_get_hex, NULL, "hex oid", NULL}, + {NULL}, +}; + +PyTypeObject IndexEntryType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.IndexEntry", /* tp_name */ + sizeof(IndexEntry), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)IndexEntry_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Index entry", /* 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 */ + IndexEntry_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/pygit2/object.c b/src/pygit2/object.c new file mode 100644 index 000000000..44a0d760e --- /dev/null +++ b/src/pygit2/object.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include + +void +Object_dealloc(Object* self) +{ + git_object_free(self->obj); + Py_XDECREF(self->repo); + PyObject_Del(self); +} + +PyObject * +Object_get_oid(Object *self) +{ + const git_oid *oid; + + oid = git_object_id(self->obj); + assert(oid); + + return git_oid_to_python(oid->id); +} + +PyObject * +Object_get_hex(Object *self) +{ + const git_oid *oid; + + oid = git_object_id(self->obj); + assert(oid); + + return git_oid_to_py_str(oid); +} + +PyObject * +Object_get_type(Object *self) +{ + return PyInt_FromLong(git_object_type(self->obj)); +} + +PyObject * +Object_read_raw(Object *self) +{ + const git_oid *oid; + git_odb_object *obj; + PyObject *aux; + + oid = git_object_id(self->obj); + assert(oid); + + obj = Repository_read_raw(self->repo->repo, oid, GIT_OID_HEXSZ); + if (obj == NULL) + return NULL; + + aux = PyString_FromStringAndSize( + git_odb_object_data(obj), + git_odb_object_size(obj)); + + git_odb_object_free(obj); + return aux; +} + +PyGetSetDef Object_getseters[] = { + {"oid", (getter)Object_get_oid, NULL, "object id", NULL}, + {"hex", (getter)Object_get_hex, NULL, "hex oid", NULL}, + {"type", (getter)Object_get_type, NULL, "type number", NULL}, + {NULL} +}; + +PyMethodDef Object_methods[] = { + {"read_raw", (PyCFunction)Object_read_raw, METH_NOARGS, + "Read the raw contents of the object from the repo."}, + {NULL} +}; + +PyTypeObject ObjectType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Object", /* tp_name */ + sizeof(Object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Object_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Object objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Object_methods, /* tp_methods */ + 0, /* tp_members */ + Object_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/pygit2/oid.c b/src/pygit2/oid.c new file mode 100644 index 000000000..fd6e13508 --- /dev/null +++ b/src/pygit2/oid.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include + +int +py_str_to_git_oid(PyObject *py_str, git_oid *oid) +{ + PyObject *py_hex; + char *hex_or_bin; + int err; + Py_ssize_t len; + + /* Case 1: raw sha */ + if (PyString_Check(py_str)) { + hex_or_bin = PyString_AsString(py_str); + if (hex_or_bin == NULL) + return -1; + git_oid_fromraw(oid, (const unsigned char*)hex_or_bin); + return GIT_OID_HEXSZ; + } + + /* Case 2: hex sha */ + if (PyUnicode_Check(py_str)) { + py_hex = PyUnicode_AsASCIIString(py_str); + if (py_hex == NULL) + return -1; + err = PyString_AsStringAndSize(py_hex, &hex_or_bin, &len); + if (err) { + Py_DECREF(py_hex); + return -1; + } + + err = git_oid_fromstrn(oid, hex_or_bin, len); + + Py_DECREF(py_hex); + + if (err < 0) { + PyErr_SetObject(Error_type(err), py_str); + return -1; + } + return len; + } + + /* Type error */ + PyErr_Format(PyExc_TypeError, + "Git object id must be byte or a text string, not: %.200s", + Py_TYPE(py_str)->tp_name); + return -1; +} + +int +py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, git_oid *oid) +{ + int err; + int len; + git_odb *odb; + git_odb_object *obj; + + len = py_str_to_git_oid(py_str, oid); + + if (len == GIT_OID_HEXSZ || len < 0) + return len; + + err = git_repository_odb(&odb, repo); + if (err < 0) { + Error_set(err); + return -1; + } + + err = git_odb_read_prefix(&obj, odb, oid, len); + if (err < 0) { + git_odb_free(odb); + Error_set(err); + return err; + } + + git_oid_cpy(oid, git_odb_object_id(obj)); + + git_odb_object_free(obj); + git_odb_free(odb); + + return 0; +} + +PyObject * +git_oid_to_py_str(const git_oid *oid) +{ + char hex[GIT_OID_HEXSZ]; + + git_oid_fmt(hex, oid); + return PyUnicode_DecodeASCII(hex, GIT_OID_HEXSZ, "strict"); +} + diff --git a/src/pygit2/reference.c b/src/pygit2/reference.c new file mode 100644 index 000000000..9ce78aada --- /dev/null +++ b/src/pygit2/reference.c @@ -0,0 +1,300 @@ +#include +#include +#include +#include +#include +#include + +extern PyObject *GitError; + +void +Reference_dealloc(Reference *self) +{ + git_reference_free(self->reference); + PyObject_Del(self); +} + +PyObject * +Reference_delete(Reference *self, PyObject *args) +{ + int err; + + CHECK_REFERENCE(self); + + /* Delete the reference */ + err = git_reference_delete(self->reference); + if (err < 0) + return Error_set(err); + + self->reference = NULL; /* Invalidate the pointer */ + Py_RETURN_NONE; /* Return None */ +} + +PyObject * +Reference_rename(Reference *self, PyObject *py_name) +{ + char *c_name; + int err; + + CHECK_REFERENCE(self); + + /* Get the C name */ + c_name = py_path_to_c_str(py_name); + if (c_name == NULL) + return NULL; + + /* Rename */ + err = git_reference_rename(self->reference, c_name, 0); + free(c_name); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; /* Return None */ +} + +PyObject * +Reference_reload(Reference *self) +{ + int err; + + CHECK_REFERENCE(self); + + err = git_reference_reload(self->reference); + if (err < 0) { + self->reference = NULL; + return Error_set(err); + } + + Py_RETURN_NONE; +} + + +PyObject * +Reference_resolve(Reference *self, PyObject *args) +{ + git_reference *c_reference; + int err; + + CHECK_REFERENCE(self); + + /* Direct: reload */ + if (git_reference_type(self->reference) == GIT_REF_OID) { + err = git_reference_reload(self->reference); + if (err < 0) { + self->reference = NULL; + return Error_set(err); + } + Py_INCREF(self); + return (PyObject *)self; + } + + /* Symbolic: resolve */ + err = git_reference_resolve(&c_reference, self->reference); + if (err < 0) + return Error_set(err); + + return wrap_reference(c_reference); +} + +PyObject * +Reference_get_target(Reference *self) +{ + const char * c_name; + + CHECK_REFERENCE(self); + + /* Get the target */ + c_name = git_reference_target(self->reference); + if (c_name == NULL) { + PyErr_SetString(PyExc_ValueError, "no target available"); + return NULL; + } + + /* Make a PyString and return it */ + return to_path(c_name); +} + +int +Reference_set_target(Reference *self, PyObject *py_name) +{ + char *c_name; + int err; + + CHECK_REFERENCE_INT(self); + + /* Get the C name */ + c_name = py_path_to_c_str(py_name); + if (c_name == NULL) + return -1; + + /* Set the new target */ + err = git_reference_set_target(self->reference, c_name); + free(c_name); + if (err < 0) { + Error_set(err); + return -1; + } + + return 0; +} + +PyObject * +Reference_get_name(Reference *self) +{ + CHECK_REFERENCE(self); + return to_path(git_reference_name(self->reference)); +} + +PyObject * +Reference_get_oid(Reference *self) +{ + const git_oid *oid; + + CHECK_REFERENCE(self); + + /* Get the oid (only for "direct" references) */ + oid = git_reference_oid(self->reference); + if (oid == NULL) { + PyErr_SetString(PyExc_ValueError, + "oid is only available if the reference is direct " + "(i.e. not symbolic)"); + return NULL; + } + + /* Convert and return it */ + return git_oid_to_python(oid->id); +} + +int +Reference_set_oid(Reference *self, PyObject *py_hex) +{ + git_oid oid; + int err; + + CHECK_REFERENCE_INT(self); + + /* Get the oid */ + err = py_str_to_git_oid_expand(git_reference_owner(self->reference), py_hex, &oid); + if (err < 0) { + Error_set(err); + return -1; + } + + /* Set the oid */ + err = git_reference_set_oid(self->reference, &oid); + if (err < 0) { + Error_set(err); + return -1; + } + + return 0; +} + +PyObject * +Reference_get_hex(Reference *self) +{ + const git_oid *oid; + + CHECK_REFERENCE(self); + + /* Get the oid (only for "direct" references) */ + oid = git_reference_oid(self->reference); + if (oid == NULL) { + PyErr_SetString(PyExc_ValueError, + "oid is only available if the reference is direct " + "(i.e. not symbolic)"); + return NULL; + } + + /* Convert and return it */ + return git_oid_to_py_str(oid); +} + +PyObject * +Reference_get_type(Reference *self) +{ + git_ref_t c_type; + + CHECK_REFERENCE(self); + c_type = git_reference_type(self->reference); + return PyInt_FromLong(c_type); +} + +PyMethodDef Reference_methods[] = { + {"delete", (PyCFunction)Reference_delete, METH_NOARGS, + "Delete this reference. It will no longer be valid!"}, + {"rename", (PyCFunction)Reference_rename, METH_O, + "Rename the reference."}, + {"reload", (PyCFunction)Reference_reload, METH_NOARGS, + "Reload the reference from the file-system."}, + {"resolve", (PyCFunction)Reference_resolve, METH_NOARGS, + "Resolve a symbolic reference and return a direct reference."}, + {NULL} +}; + +PyGetSetDef Reference_getseters[] = { + {"name", (getter)Reference_get_name, NULL, + "The full name of a reference.", NULL}, + {"oid", (getter)Reference_get_oid, (setter)Reference_set_oid, "object id", + NULL}, + {"hex", (getter)Reference_get_hex, NULL, "hex oid", NULL}, + {"target", (getter)Reference_get_target, (setter)Reference_set_target, + "target", NULL}, + {"type", (getter)Reference_get_type, NULL, + "type (GIT_REF_OID, GIT_REF_SYMBOLIC or GIT_REF_PACKED).", NULL}, + {NULL} +}; + +PyTypeObject ReferenceType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Reference", /* tp_name */ + sizeof(Reference), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Reference_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Reference", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Reference_methods, /* tp_methods */ + 0, /* tp_members */ + Reference_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 */ +}; + + +PyObject * +wrap_reference(git_reference * c_reference) +{ + Reference *py_reference=NULL; + + py_reference = PyObject_New(Reference, &ReferenceType); + if (py_reference) + py_reference->reference = c_reference; + + return (PyObject *)py_reference; +} diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c new file mode 100644 index 000000000..65c862893 --- /dev/null +++ b/src/pygit2/repository.c @@ -0,0 +1,806 @@ +#include +#include +#include +#include +#include +#include +#include + +extern PyTypeObject IndexType; +extern PyTypeObject WalkerType; +extern PyTypeObject SignatureType; +extern PyTypeObject TreeType; +extern PyTypeObject CommitType; +extern PyTypeObject BlobType; +extern PyTypeObject TagType; +extern PyTypeObject TreeBuilderType; + +git_otype +int_to_loose_object_type(int type_id) +{ + switch((git_otype)type_id) { + case GIT_OBJ_COMMIT: return GIT_OBJ_COMMIT; + case GIT_OBJ_TREE: return GIT_OBJ_TREE; + case GIT_OBJ_BLOB: return GIT_OBJ_BLOB; + case GIT_OBJ_TAG: return GIT_OBJ_TAG; + default: return GIT_OBJ_BAD; + } +} + +PyObject * +lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, + git_otype type) +{ + int err; + git_object *obj; + Object *py_obj = NULL; + + err = git_object_lookup_prefix(&obj, repo->repo, oid, + (unsigned int)len, type); + if (err < 0) + return Error_set_oid(err, oid, len); + + switch (git_object_type(obj)) { + case GIT_OBJ_COMMIT: + py_obj = PyObject_New(Object, &CommitType); + break; + case GIT_OBJ_TREE: + py_obj = PyObject_New(Object, &TreeType); + break; + case GIT_OBJ_BLOB: + py_obj = PyObject_New(Object, &BlobType); + break; + case GIT_OBJ_TAG: + py_obj = PyObject_New(Object, &TagType); + break; + default: + assert(0); + } + + if (py_obj) { + py_obj->obj = obj; + py_obj->repo = repo; + Py_INCREF(repo); + } + return (PyObject*)py_obj; +} + +PyObject * +lookup_object(Repository *repo, const git_oid *oid, git_otype type) +{ + return lookup_object_prefix(repo, oid, GIT_OID_HEXSZ, type); +} + +int +Repository_init(Repository *self, PyObject *args, PyObject *kwds) +{ + char *path; + int err; + + if (kwds) { + PyErr_SetString(PyExc_TypeError, + "Repository takes no keyword arguments"); + return -1; + } + + if (!PyArg_ParseTuple(args, "s", &path)) + return -1; + + err = git_repository_open(&self->repo, path); + if (err < 0) { + Error_set_str(err, path); + return -1; + } + + return 0; +} + +void +Repository_dealloc(Repository *self) +{ + PyObject_GC_UnTrack(self); + Py_XDECREF(self->index); + git_repository_free(self->repo); + PyObject_GC_Del(self); +} + +int +Repository_traverse(Repository *self, visitproc visit, void *arg) +{ + Py_VISIT(self->index); + return 0; +} + +int +Repository_clear(Repository *self) +{ + Py_CLEAR(self->index); + return 0; +} + +int +Repository_contains(Repository *self, PyObject *value) +{ + git_oid oid; + git_odb *odb; + int err, len, exists; + + len = py_str_to_git_oid(value, &oid); + if (len < 0) + return -1; + + err = git_repository_odb(&odb, self->repo); + if (err < 0) { + Error_set(err); + return -1; + } + + if (len < GIT_OID_HEXSZ) { + git_odb_object *obj = NULL; + err = git_odb_read_prefix(&obj, odb, &oid, len); + if (err < 0 && err != GIT_ENOTFOUND) { + Error_set(err); + exists = -1; + } else { + exists = (err == 0); + if (obj) + git_odb_object_free(obj); + } + } else { + exists = git_odb_exists(odb, &oid); + } + + git_odb_free(odb); + return exists; +} + +PyObject * +Repository_getitem(Repository *self, PyObject *value) +{ + git_oid oid; + int len; + + len = py_str_to_git_oid(value, &oid); + if (len < 0) + return NULL; + + return lookup_object_prefix(self, &oid, len, GIT_OBJ_ANY); +} + +git_odb_object * +Repository_read_raw(git_repository *repo, const git_oid *oid, size_t len) +{ + git_odb *odb; + git_odb_object *obj; + int err; + + err = git_repository_odb(&odb, repo); + if (err < 0) { + Error_set(err); + return NULL; + } + + err = git_odb_read_prefix(&obj, odb, oid, (unsigned int)len); + git_odb_free(odb); + if (err < 0) { + Error_set_oid(err, oid, len); + return NULL; + } + + return obj; +} + +PyObject * +Repository_read(Repository *self, PyObject *py_hex) +{ + git_oid oid; + git_odb_object *obj; + int len; + PyObject* tuple; + + len = py_str_to_git_oid(py_hex, &oid); + if (len < 0) + return NULL; + + obj = Repository_read_raw(self->repo, &oid, len); + if (obj == NULL) + return NULL; + + tuple = Py_BuildValue( + "(ns#)", + git_odb_object_type(obj), + git_odb_object_data(obj), + git_odb_object_size(obj)); + + git_odb_object_free(obj); + return tuple; +} + +PyObject * +Repository_write(Repository *self, PyObject *args) +{ + int err; + git_oid oid; + git_odb *odb; + git_odb_stream* stream; + int type_id; + const char* buffer; + Py_ssize_t buflen; + git_otype type; + + if (!PyArg_ParseTuple(args, "Is#", &type_id, &buffer, &buflen)) + return NULL; + + type = int_to_loose_object_type(type_id); + if (type == GIT_OBJ_BAD) + return PyErr_Format(PyExc_ValueError, "%d", type_id); + + err = git_repository_odb(&odb, self->repo); + if (err < 0) + return Error_set(err); + + err = git_odb_open_wstream(&stream, odb, buflen, type); + git_odb_free(odb); + if (err < 0) + return Error_set(err); + + stream->write(stream, buffer, buflen); + err = stream->finalize_write(&oid, stream); + stream->free(stream); + return git_oid_to_python(oid.id); +} + +PyObject * +Repository_get_index(Repository *self, void *closure) +{ + int err; + git_index *index; + Index *py_index; + + assert(self->repo); + + if (self->index == NULL) { + err = git_repository_index(&index, self->repo); + if (err < 0) + return Error_set(err); + + py_index = PyObject_GC_New(Index, &IndexType); + if (!py_index) { + git_index_free(index); + return NULL; + } + + Py_INCREF(self); + py_index->repo = self; + py_index->index = index; + PyObject_GC_Track(py_index); + self->index = (PyObject*)py_index; + } + + Py_INCREF(self->index); + return self->index; +} + +PyObject * +Repository_get_path(Repository *self, void *closure) +{ + return to_path(git_repository_path(self->repo)); +} + +PyObject * +Repository_get_workdir(Repository *self, void *closure) +{ + const char *c_path; + + c_path = git_repository_workdir(self->repo); + if (c_path == NULL) + Py_RETURN_NONE; + + return to_path(c_path); +} + +PyObject * +Repository_walk(Repository *self, PyObject *args) +{ + PyObject *value; + unsigned int sort; + int err; + git_oid oid; + git_revwalk *walk; + Walker *py_walker; + + if (!PyArg_ParseTuple(args, "OI", &value, &sort)) + return NULL; + + err = git_revwalk_new(&walk, self->repo); + if (err < 0) + return Error_set(err); + + /* Sort */ + git_revwalk_sorting(walk, sort); + + /* Push */ + if (value != Py_None) { + err = py_str_to_git_oid_expand(self->repo, value, &oid); + if (err < 0) { + git_revwalk_free(walk); + return Error_set(err); + } + + err = git_revwalk_push(walk, &oid); + if (err < 0) { + git_revwalk_free(walk); + return Error_set(err); + } + } + + py_walker = PyObject_New(Walker, &WalkerType); + if (!py_walker) { + git_revwalk_free(walk); + return NULL; + } + + Py_INCREF(self); + py_walker->repo = self; + py_walker->walk = walk; + return (PyObject*)py_walker; +} + + +PyObject * +Repository_create_blob(Repository *self, PyObject *args) +{ + git_oid oid; + const char* raw; + Py_ssize_t size; + int err; + + if (!PyArg_ParseTuple(args, "s#", &raw, &size)) + return NULL; + + err = git_blob_create_frombuffer(&oid, self->repo, (const void*)raw, size); + + if (err < 0) + return Error_set(err); + + return git_oid_to_python(oid.id); +} + +PyObject * +Repository_create_commit(Repository *self, PyObject *args) +{ + Signature *py_author, *py_committer; + PyObject *py_oid, *py_message, *py_parents, *py_parent; + PyObject *py_result = NULL; + char *message = NULL; + char *update_ref = NULL; + char *encoding = NULL; + git_oid oid; + git_tree *tree = NULL; + int parent_count; + git_commit **parents = NULL; + int err = 0, i = 0, len; + + if (!PyArg_ParseTuple(args, "zO!O!OOO!|s", + &update_ref, + &SignatureType, &py_author, + &SignatureType, &py_committer, + &py_message, + &py_oid, + &PyList_Type, &py_parents, + &encoding)) + return NULL; + + len = py_str_to_git_oid(py_oid, &oid); + if (len < 0) + goto out; + + message = py_str_to_c_str(py_message, encoding); + if (message == NULL) + goto out; + + err = git_tree_lookup_prefix(&tree, self->repo, &oid, (unsigned int)len); + if (err < 0) { + Error_set(err); + goto out; + } + + parent_count = (int)PyList_Size(py_parents); + parents = malloc(parent_count * sizeof(git_commit*)); + if (parents == NULL) { + PyErr_SetNone(PyExc_MemoryError); + goto out; + } + for (; i < parent_count; i++) { + py_parent = PyList_GET_ITEM(py_parents, i); + len = py_str_to_git_oid(py_parent, &oid); + if (len < 0) + goto out; + if (git_commit_lookup_prefix(&parents[i], self->repo, &oid, + (unsigned int)len)) + goto out; + } + + err = git_commit_create(&oid, self->repo, update_ref, + py_author->signature, py_committer->signature, + encoding, message, tree, parent_count, + (const git_commit**)parents); + if (err < 0) { + Error_set(err); + goto out; + } + + py_result = git_oid_to_python(oid.id); + +out: + free(message); + git_tree_free(tree); + while (i > 0) { + i--; + git_commit_free(parents[i]); + } + free(parents); + return py_result; +} + +PyObject * +Repository_create_tag(Repository *self, PyObject *args) +{ + PyObject *py_oid; + Signature *py_tagger; + char *tag_name, *message; + git_oid oid; + git_object *target = NULL; + int err, target_type, len; + + if (!PyArg_ParseTuple(args, "sOiO!s", + &tag_name, + &py_oid, + &target_type, + &SignatureType, &py_tagger, + &message)) + return NULL; + + len = py_str_to_git_oid(py_oid, &oid); + if (len < 0) + return NULL; + + err = git_object_lookup_prefix(&target, self->repo, &oid, + (unsigned int)len, target_type); + err = err < 0 ? err : git_tag_create(&oid, self->repo, tag_name, target, + py_tagger->signature, message, 0); + git_object_free(target); + if (err < 0) + return Error_set_oid(err, &oid, len); + return git_oid_to_python(oid.id); +} + +PyObject * +Repository_listall_references(Repository *self, PyObject *args) +{ + unsigned list_flags=GIT_REF_LISTALL; + git_strarray c_result; + PyObject *py_result, *py_string; + unsigned index; + int err; + + /* 1- Get list_flags */ + if (!PyArg_ParseTuple(args, "|I", &list_flags)) + return NULL; + + /* 2- Get the C result */ + err = git_reference_list(&c_result, self->repo, list_flags); + if (err < 0) + return Error_set(err); + + /* 3- Create a new PyTuple */ + py_result = PyTuple_New(c_result.count); + if (py_result == NULL) + goto out; + + /* 4- Fill it */ + for (index=0; index < c_result.count; index++) { + py_string = to_path((c_result.strings)[index]); + if (py_string == NULL) { + Py_CLEAR(py_result); + goto out; + } + PyTuple_SET_ITEM(py_result, index, py_string); + } + +out: + git_strarray_free(&c_result); + return py_result; +} + +PyObject * +Repository_lookup_reference(Repository *self, PyObject *py_name) +{ + git_reference *c_reference; + char *c_name; + int err; + + /* 1- Get the C name */ + c_name = py_path_to_c_str(py_name); + if (c_name == NULL) + return NULL; + + /* 2- Lookup */ + err = git_reference_lookup(&c_reference, self->repo, c_name); + if (err < 0) { + PyObject *err_obj = Error_set_str(err, c_name); + free(c_name); + return err_obj; + } + + /* 3- Make an instance of Reference and return it */ + return wrap_reference(c_reference); +} + +PyObject * +Repository_create_reference(Repository *self, PyObject *args) +{ + PyObject *py_oid; + git_reference *c_reference; + char *c_name; + git_oid oid; + int err; + + /* 1- Get the C variables */ + if (!PyArg_ParseTuple(args, "sO", &c_name, &py_oid)) + return NULL; + + err = py_str_to_git_oid_expand(self->repo, py_oid, &oid); + if (err < 0) + return Error_set(err); + + /* 2- Create the reference */ + err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid, 0); + if (err < 0) + return Error_set(err); + + /* 3- Make an instance of Reference and return it */ + return wrap_reference(c_reference); +} + +PyObject * +Repository_create_symbolic_reference(Repository *self, PyObject *args) +{ + git_reference *c_reference; + char *c_name, *c_target; + int err; + + /* 1- Get the C variables */ + if (!PyArg_ParseTuple(args, "ss", &c_name, &c_target)) + return NULL; + + /* 2- Create the reference */ + err = git_reference_create_symbolic(&c_reference, self->repo, c_name, + c_target, 0); + if (err < 0) + return Error_set(err); + + /* 3- Make an instance of Reference and return it */ + return wrap_reference(c_reference); +} + +PyObject * +Repository_packall_references(Repository *self, PyObject *args) +{ + int err; + + /* 1- Pack */ + err = git_reference_packall(self->repo); + if (err < 0) + return Error_set(err); + + /* 2- Return None */ + Py_RETURN_NONE; +} + +int +read_status_cb(const char *path, unsigned int status_flags, void *payload) +{ + /* This is the callback that will be called in git_status_foreach. It + * will be called for every path.*/ + PyObject *flags; + + flags = PyInt_FromLong((long) status_flags); + PyDict_SetItemString(payload, path, flags); + + return GIT_OK; +} + +PyObject * +Repository_status(Repository *self, PyObject *args) +{ + PyObject *payload_dict; + + payload_dict = PyDict_New(); + git_status_foreach(self->repo, read_status_cb, payload_dict); + + return payload_dict; +} + +PyObject * +Repository_status_file(Repository *self, PyObject *value) +{ + char *path; + unsigned int status; + int err; + + path = py_path_to_c_str(value); + if (!path) + return NULL; + + err = git_status_file(&status, self->repo, path); + if (err < 0) { + PyObject *err_obj = Error_set_str(err, path); + free(path); + return err_obj; + } + return PyInt_FromLong(status); +} + +PyObject * +Repository_TreeBuilder(Repository *self, PyObject *args) +{ + TreeBuilder *builder; + git_treebuilder *bld; + PyObject *py_src = NULL; + git_oid oid; + git_tree *tree = NULL; + git_tree *must_free = NULL; + int err; + + if (!PyArg_ParseTuple(args, "|O", &py_src)) + return NULL; + + if (py_src) { + if (PyObject_TypeCheck(py_src, &TreeType)) { + Tree *py_tree = (Tree *)py_src; + if (py_tree->repo->repo != self->repo) { + //return Error_set(GIT_EINVALIDARGS); + return Error_set(GIT_ERROR); + } + tree = py_tree->tree; + } else { + err = py_str_to_git_oid_expand(self->repo, py_src, &oid); + if (err < 0) + return NULL; + + err = git_tree_lookup(&tree, self->repo, &oid); + if (err < 0) + return Error_set(err); + must_free = tree; + } + } + + err = git_treebuilder_create(&bld, tree); + if (must_free != NULL) { + git_tree_free(must_free); + } + + if (err < 0) + return Error_set(err); + + builder = PyObject_New(TreeBuilder, &TreeBuilderType); + if (builder) { + builder->repo = self; + builder->bld = bld; + Py_INCREF(self); + } + + return (PyObject*)builder; +} + +PyMethodDef Repository_methods[] = { + {"create_commit", (PyCFunction)Repository_create_commit, METH_VARARGS, + "Create a new commit object, return its SHA."}, + {"create_tag", (PyCFunction)Repository_create_tag, METH_VARARGS, + "Create a new tag object, return its SHA."}, + {"walk", (PyCFunction)Repository_walk, METH_VARARGS, + "Generator that traverses the history starting from the given commit."}, + {"read", (PyCFunction)Repository_read, METH_O, + "Read raw object data from the repository."}, + {"write", (PyCFunction)Repository_write, METH_VARARGS, + "Write raw object data into the repository. First arg is the object\n" + "type, the second one a buffer with data. Return the object id (sha)\n" + "of the created object."}, + {"listall_references", (PyCFunction)Repository_listall_references, + METH_VARARGS, + "Return a list with all the references in the repository."}, + {"lookup_reference", (PyCFunction)Repository_lookup_reference, METH_O, + "Lookup a reference by its name in a repository."}, + {"create_blob", (PyCFunction)Repository_create_blob, + METH_VARARGS, + "Create a new blob from memory"}, + {"create_reference", (PyCFunction)Repository_create_reference, + METH_VARARGS, + "Create a new reference \"name\" that points to the object given by its " + "\"sha\"."}, + {"create_symbolic_reference", + (PyCFunction)Repository_create_symbolic_reference, METH_VARARGS, + "Create a new symbolic reference \"name\" that points to the reference\n" + "\"target\"."}, + {"packall_references", (PyCFunction)Repository_packall_references, + METH_NOARGS, "Pack all the loose references in the repository."}, + {"status", (PyCFunction)Repository_status, METH_NOARGS, "Reads the " + "status of the repository and returns a dictionary with file paths " + "as keys and status flags as values.\nSee pygit2.GIT_STATUS_*."}, + {"status_file", (PyCFunction)Repository_status_file, METH_O, + "Returns the status of the given file path."}, + {"TreeBuilder", (PyCFunction)Repository_TreeBuilder, METH_VARARGS, + "Create a TreeBuilder object for this repository."}, + {NULL} +}; + +PyGetSetDef Repository_getseters[] = { + {"index", (getter)Repository_get_index, NULL, "index file. ", NULL}, + {"path", (getter)Repository_get_path, NULL, + "The normalized path to the git repository.", NULL}, + {"workdir", (getter)Repository_get_workdir, NULL, + "The normalized path to the working directory of the repository. " + "If the repository is bare, None will be returned.", NULL}, + {NULL} +}; + +PySequenceMethods Repository_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)Repository_contains, /* sq_contains */ +}; + +PyMappingMethods Repository_as_mapping = { + 0, /* mp_length */ + (binaryfunc)Repository_getitem, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + +PyTypeObject RepositoryType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Repository", /* tp_name */ + sizeof(Repository), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Repository_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &Repository_as_sequence, /* tp_as_sequence */ + &Repository_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC, /* tp_flags */ + "Git repository", /* tp_doc */ + (traverseproc)Repository_traverse, /* tp_traverse */ + (inquiry)Repository_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Repository_methods, /* tp_methods */ + 0, /* tp_members */ + Repository_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Repository_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; diff --git a/src/pygit2/signature.c b/src/pygit2/signature.c new file mode 100644 index 000000000..e750c821d --- /dev/null +++ b/src/pygit2/signature.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include +#include + +int +Signature_init(Signature *self, PyObject *args, PyObject *kwds) +{ + PyObject *py_name; + char *name, *email, *encoding = NULL; + long long time; + int offset; + int err; + git_signature *signature; + + if (kwds) { + PyErr_SetString(PyExc_TypeError, + "Signature takes no keyword arguments"); + return -1; + } + + if (!PyArg_ParseTuple(args, "OsLi|s", + &py_name, &email, &time, &offset, &encoding)) + return -1; + + name = py_str_to_c_str(py_name, encoding); + if (name == NULL) + return -1; + + err = git_signature_new(&signature, name, email, time, offset); + free(name); + if (err < 0) { + Error_set(err); + return -1; + } + + self->obj = NULL; + self->signature = signature; + + if (encoding) { + self->encoding = strdup(encoding); + if (self->encoding == NULL) { + PyErr_NoMemory(); + return -1; + } + } + + return 0; +} + +void +Signature_dealloc(Signature *self) +{ + if (self->obj) + Py_DECREF(self->obj); + else { + git_signature_free((git_signature*)self->signature); + free((void*)self->encoding); + } + Py_TYPE(self)->tp_free((PyObject*)self); +} + +PyObject * +Signature_get_encoding(Signature *self) +{ + const char *encoding; + + encoding = self->encoding; + if (encoding == NULL) + encoding = "utf-8"; + + return to_encoding(encoding); +} + +PyObject * +Signature_get_raw_name(Signature *self) +{ + return to_bytes(self->signature->name); +} + +PyObject * +Signature_get_raw_email(Signature *self) +{ + return to_bytes(self->signature->email); +} + +PyObject * +Signature_get_name(Signature *self) +{ + return to_unicode(self->signature->name, self->encoding, "strict"); +} + +PyObject * +Signature_get_email(Signature *self) +{ + return to_unicode(self->signature->email, self->encoding, "strict"); +} + +PyObject * +Signature_get_time(Signature *self) +{ + return PyInt_FromLong(self->signature->when.time); +} + +PyObject * +Signature_get_offset(Signature *self) +{ + return PyInt_FromLong(self->signature->when.offset); +} + +PyGetSetDef Signature_getseters[] = { + {"_encoding", (getter)Signature_get_encoding, NULL, "encoding", NULL}, + {"_name", (getter)Signature_get_raw_name, NULL, "Name (bytes)", NULL}, + {"_email", (getter)Signature_get_raw_email, NULL, "Email (bytes)", NULL}, + {"name", (getter)Signature_get_name, NULL, "Name", NULL}, + {"email", (getter)Signature_get_email, NULL, "Email", NULL}, + {"time", (getter)Signature_get_time, NULL, "Time", NULL}, + {"offset", (getter)Signature_get_offset, NULL, "Offset", NULL}, + {NULL} +}; + +PyTypeObject SignatureType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Signature", /* tp_name */ + sizeof(Signature), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Signature_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Signature", /* 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 */ + Signature_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Signature_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyObject * +build_signature(Object *obj, const git_signature *signature, + const char *encoding) +{ + Signature *py_signature; + + py_signature = PyObject_New(Signature, &SignatureType); + if (py_signature) { + Py_INCREF(obj); + py_signature->obj = obj; + py_signature->signature = signature; + py_signature->encoding = encoding; + } + return (PyObject*)py_signature; +} diff --git a/src/pygit2/tag.c b/src/pygit2/tag.c new file mode 100644 index 000000000..b1536e262 --- /dev/null +++ b/src/pygit2/tag.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include +#include + +PyObject * +Tag_get_target(Tag *self) +{ + const git_oid *oid; + + oid = git_tag_target_oid(self->tag); + return git_oid_to_python(oid->id); +} + +PyObject * +Tag_get_name(Tag *self) +{ + const char *name; + name = git_tag_name(self->tag); + if (!name) + Py_RETURN_NONE; + return to_unicode(name, "utf-8", "strict"); +} + +PyObject * +Tag_get_tagger(Tag *self) +{ + const git_signature *signature = git_tag_tagger(self->tag); + if (!signature) + Py_RETURN_NONE; + + return build_signature((Object*)self, signature, "utf-8"); +} + +PyObject * +Tag_get_message(Tag *self) +{ + const char *message; + message = git_tag_message(self->tag); + if (!message) + Py_RETURN_NONE; + return to_unicode(message, "utf-8", "strict"); +} + +PyObject * +Tag_get_raw_message(Tag *self) +{ + return PyString_FromString(git_tag_message(self->tag)); +} + +PyGetSetDef Tag_getseters[] = { + {"target", (getter)Tag_get_target, NULL, "tagged object", NULL}, + {"name", (getter)Tag_get_name, NULL, "tag name", NULL}, + {"tagger", (getter)Tag_get_tagger, NULL, "tagger", NULL}, + {"message", (getter)Tag_get_message, NULL, "tag message", NULL}, + {"_message", (getter)Tag_get_raw_message, NULL, "tag message (bytes)", NULL}, + {NULL} +}; + +PyTypeObject TagType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Tag", /* tp_name */ + sizeof(Tag), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* 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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Tag objects", /* 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 */ + Tag_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/pygit2/tree.c b/src/pygit2/tree.c new file mode 100644 index 000000000..0445aa19e --- /dev/null +++ b/src/pygit2/tree.c @@ -0,0 +1,462 @@ +#include +#include +#include +#include +#include +#include + +extern PyTypeObject TreeType; +extern PyTypeObject TreeIterType; + +void +TreeEntry_dealloc(TreeEntry *self) +{ + Py_XDECREF(self->owner); + PyObject_Del(self); +} + +PyObject * +TreeEntry_get_attributes(TreeEntry *self) +{ + return PyInt_FromLong(git_tree_entry_attributes(self->entry)); +} + +PyObject * +TreeEntry_get_name(TreeEntry *self) +{ + return to_path(git_tree_entry_name(self->entry)); +} + +PyObject * +TreeEntry_get_oid(TreeEntry *self) +{ + const git_oid *oid; + + oid = git_tree_entry_id(self->entry); + return git_oid_to_python(oid->id); +} + +PyObject * +TreeEntry_get_hex(TreeEntry *self) +{ + return git_oid_to_py_str(git_tree_entry_id(self->entry)); +} + +PyObject * +TreeEntry_to_object(TreeEntry *self) +{ + const git_oid *entry_oid; + Repository *repo; + + repo = ((Object*)(self->owner))->repo; + entry_oid = git_tree_entry_id(self->entry); + return lookup_object(repo, entry_oid, GIT_OBJ_ANY); +} + +PyGetSetDef TreeEntry_getseters[] = { + {"attributes", (getter)TreeEntry_get_attributes, NULL, "attributes", NULL}, + {"name", (getter)TreeEntry_get_name, NULL, "name", NULL}, + {"oid", (getter)TreeEntry_get_oid, NULL, "object id", NULL}, + {"hex", (getter)TreeEntry_get_hex, NULL, "hex oid", NULL}, + {NULL} +}; + +PyMethodDef TreeEntry_methods[] = { + {"to_object", (PyCFunction)TreeEntry_to_object, METH_NOARGS, + "Look up the corresponding object in the repo."}, + {NULL, NULL, 0, NULL} +}; + +PyTypeObject TreeEntryType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.TreeEntry", /* tp_name */ + sizeof(TreeEntry), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)TreeEntry_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "TreeEntry objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + TreeEntry_methods, /* tp_methods */ + 0, /* tp_members */ + TreeEntry_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 */ +}; + +Py_ssize_t +Tree_len(Tree *self) +{ + assert(self->tree); + return (Py_ssize_t)git_tree_entrycount(self->tree); +} + +int +Tree_contains(Tree *self, PyObject *py_name) +{ + int result = 0; + char *name = py_path_to_c_str(py_name); + if (name == NULL) + return -1; + + result = git_tree_entry_byname(self->tree, name) ? 1 : 0; + free(name); + return result; +} + +TreeEntry * +wrap_tree_entry(const git_tree_entry *entry, Tree *tree) +{ + TreeEntry *py_entry; + + py_entry = PyObject_New(TreeEntry, &TreeEntryType); + if (py_entry) { + py_entry->entry = entry; + py_entry->owner = (PyObject*)tree; + Py_INCREF(tree); + } + return py_entry; +} + +int +Tree_fix_index(Tree *self, PyObject *py_index) +{ + long index; + size_t len; + long slen; + + index = PyInt_AsLong(py_index); + if (PyErr_Occurred()) + return -1; + + len = git_tree_entrycount(self->tree); + slen = (long)len; + if (index >= slen) { + PyErr_SetObject(PyExc_IndexError, py_index); + return -1; + } + else if (index < -slen) { + PyErr_SetObject(PyExc_IndexError, py_index); + return -1; + } + + /* This function is called via mp_subscript, which doesn't do negative + * index rewriting, so we have to do it manually. */ + if (index < 0) + index = len + index; + return (int)index; +} + +PyObject * +Tree_iter(Tree *self) +{ + TreeIter *iter; + + iter = PyObject_New(TreeIter, &TreeIterType); + if (iter) { + Py_INCREF(self); + iter->owner = self; + iter->i = 0; + } + return (PyObject*)iter; +} + +TreeEntry * +Tree_getitem_by_index(Tree *self, PyObject *py_index) +{ + int index; + const git_tree_entry *entry; + + index = Tree_fix_index(self, py_index); + if (PyErr_Occurred()) + return NULL; + + entry = git_tree_entry_byindex(self->tree, index); + if (!entry) { + PyErr_SetObject(PyExc_IndexError, py_index); + return NULL; + } + return wrap_tree_entry(entry, self); +} + +TreeEntry * +Tree_getitem(Tree *self, PyObject *value) +{ + char *name; + const git_tree_entry *entry; + + /* Case 1: integer */ + if (PyInt_Check(value)) + return Tree_getitem_by_index(self, value); + + /* Case 2: byte or text string */ + name = py_path_to_c_str(value); + if (name == NULL) + return NULL; + entry = git_tree_entry_byname(self->tree, name); + free(name); + if (!entry) { + PyErr_SetObject(PyExc_KeyError, value); + return NULL; + } + return wrap_tree_entry(entry, self); +} + +PySequenceMethods Tree_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)Tree_contains, /* sq_contains */ +}; + +PyMappingMethods Tree_as_mapping = { + (lenfunc)Tree_len, /* mp_length */ + (binaryfunc)Tree_getitem, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + +PyTypeObject TreeType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Tree", /* tp_name */ + sizeof(Tree), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &Tree_as_sequence, /* tp_as_sequence */ + &Tree_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Tree objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)Tree_iter, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* 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 */ +}; + + + +void +TreeBuilder_dealloc(TreeBuilder* self) +{ + Py_XDECREF(self->repo); + git_treebuilder_free(self->bld); + PyObject_Del(self); +} + +PyObject * +TreeBuilder_insert(TreeBuilder *self, PyObject *args) +{ + PyObject *py_oid; + int len, err, attr; + git_oid oid; + const char *fname; + + if (!PyArg_ParseTuple(args, "sOi", &fname, &py_oid, &attr)) { + return NULL; + } + + len = py_str_to_git_oid(py_oid, &oid); + if (len < 0) { + return NULL; + } + + err = git_treebuilder_insert(NULL, self->bld, fname, &oid, attr); + if (err < 0) { + Error_set(err); + return NULL; + } + + Py_RETURN_NONE; +} + +PyObject * +TreeBuilder_write(TreeBuilder *self) +{ + int err; + git_oid oid; + + err = git_treebuilder_write(&oid, self->repo->repo, self->bld); + if (err < 0) + return Error_set(err); + + return git_oid_to_python(&oid); +} + +PyObject * +TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename) +{ + char *filename = py_path_to_c_str(py_filename); + int err = 0; + + if (filename == NULL) + return NULL; + + err = git_treebuilder_remove(self->bld, filename); + free(filename); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + +PyObject * +TreeBuilder_clear(TreeBuilder *self) +{ + git_treebuilder_clear(self->bld); + Py_RETURN_NONE; +} + +PyMethodDef TreeBuilder_methods[] = { + {"insert", (PyCFunction)TreeBuilder_insert, METH_VARARGS, + "Insert or replace an entry in the treebuilder"}, + {"write", (PyCFunction)TreeBuilder_write, METH_NOARGS, + "Write the tree to the given repository"}, + {"remove", (PyCFunction)TreeBuilder_remove, METH_O, + "Remove an entry from the builder"}, + {"clear", (PyCFunction)TreeBuilder_clear, METH_NOARGS, + "Clear all the entries in the builder"}, + {NULL, NULL, 0, NULL} +}; + +PyTypeObject TreeBuilderType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.TreeBuilder", /* tp_name */ + sizeof(TreeBuilder), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)TreeBuilder_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "TreeBuilder objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + TreeBuilder_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* 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 */ +}; + +void +TreeIter_dealloc(TreeIter *self) +{ + Py_CLEAR(self->owner); + PyObject_Del(self); +} + +TreeEntry * +TreeIter_iternext(TreeIter *self) +{ + const git_tree_entry *tree_entry; + + tree_entry = git_tree_entry_byindex(self->owner->tree, self->i); + if (!tree_entry) + return NULL; + + self->i += 1; + return (TreeEntry*)wrap_tree_entry(tree_entry, self->owner); +} + +PyTypeObject TreeIterType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.TreeIter", /* tp_name */ + sizeof(TreeIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)TreeIter_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)TreeIter_iternext, /* tp_iternext */ +}; diff --git a/src/pygit2/utils.c b/src/pygit2/utils.c new file mode 100644 index 000000000..0bd3aacbb --- /dev/null +++ b/src/pygit2/utils.c @@ -0,0 +1,36 @@ +#include +#include + +extern PyTypeObject ReferenceType; + +// py_str_to_c_str() returns a newly allocated C string holding +// the string contained in the value argument. +char * py_str_to_c_str(PyObject *value, const char *encoding) +{ + /* Case 1: byte string */ + if (PyString_Check(value)) + return strdup(PyString_AsString(value)); + + /* Case 2: text string */ + if (PyUnicode_Check(value)) { + char *c_str = NULL; + + if (encoding == NULL) + value = PyUnicode_AsUTF8String(value); + else + value = PyUnicode_AsEncodedString(value, encoding, "strict"); + if (value == NULL) + return NULL; + c_str = strdup(PyString_AsString(value)); + Py_DECREF(value); + return c_str; + } + + /* Type error */ + PyErr_Format(PyExc_TypeError, "unexpected %.200s", + Py_TYPE(value)->tp_name); + return NULL; +} + + + diff --git a/src/pygit2/walker.c b/src/pygit2/walker.c new file mode 100644 index 000000000..36db770f4 --- /dev/null +++ b/src/pygit2/walker.c @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include +#include + +extern PyTypeObject CommitType; + +void +Walker_dealloc(Walker *self) +{ + git_revwalk_free(self->walk); + Py_DECREF(self->repo); + PyObject_Del(self); +} + +PyObject * +Walker_hide(Walker *self, PyObject *py_hex) +{ + int err; + git_oid oid; + + err = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); + + if (err < 0) + return Error_set(err); + + err = git_revwalk_hide(self->walk, &oid); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + +PyObject * +Walker_push(Walker *self, PyObject *py_hex) +{ + int err; + git_oid oid; + + err = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); + if (err < 0) + return Error_set(err); + + err = git_revwalk_push(self->walk, &oid); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + +PyObject * +Walker_sort(Walker *self, PyObject *py_sort_mode) +{ + int sort_mode; + + sort_mode = (int)PyInt_AsLong(py_sort_mode); + if (sort_mode == -1 && PyErr_Occurred()) + return NULL; + + git_revwalk_sorting(self->walk, sort_mode); + + Py_RETURN_NONE; +} + +PyObject * +Walker_reset(Walker *self) +{ + git_revwalk_reset(self->walk); + Py_RETURN_NONE; +} + +PyObject * +Walker_iter(Walker *self) +{ + Py_INCREF(self); + return (PyObject*)self; +} + +PyObject * +Walker_iternext(Walker *self) +{ + int err; + git_commit *commit; + Commit *py_commit; + git_oid oid; + + err = git_revwalk_next(&oid, self->walk); + if (err < 0) + return Error_set(err); + + err = git_commit_lookup(&commit, self->repo->repo, &oid); + if (err < 0) + return Error_set(err); + + py_commit = PyObject_New(Commit, &CommitType); + if (py_commit) { + py_commit->commit = commit; + Py_INCREF(self->repo); + py_commit->repo = self->repo; + } + return (PyObject*)py_commit; +} + +PyMethodDef Walker_methods[] = { + {"hide", (PyCFunction)Walker_hide, METH_O, + "Mark a commit (and its ancestors) uninteresting for the output."}, + {"push", (PyCFunction)Walker_push, METH_O, + "Mark a commit to start traversal from."}, + {"reset", (PyCFunction)Walker_reset, METH_NOARGS, + "Reset the walking machinery for reuse."}, + {"sort", (PyCFunction)Walker_sort, METH_O, + "Change the sorting mode (this resets the walker)."}, + {NULL} +}; + +PyTypeObject WalkerType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Walker", /* tp_name */ + sizeof(Walker), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Walker_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Revision walker", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)Walker_iter, /* tp_iter */ + (iternextfunc)Walker_iternext, /* tp_iternext */ + Walker_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* 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 */ +}; From ad4edf1b85f6984b1f18c286a476373f2124141f Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 25 May 2012 14:23:25 +0200 Subject: [PATCH 0179/2237] added pygit2.utils module refactored package integration to be able to have pygit2.utils module --- pygit2/__init__.py | 29 +++++++++++++++++++++++++++++ pygit2/utils.py | 29 +++++++++++++++++++++++++++++ setup.py | 3 ++- src/pygit2.c | 10 +++++----- src/pygit2/blob.c | 2 +- src/pygit2/commit.c | 2 +- src/pygit2/index.c | 6 +++--- src/pygit2/object.c | 2 +- src/pygit2/reference.c | 2 +- src/pygit2/repository.c | 2 +- src/pygit2/signature.c | 2 +- src/pygit2/tag.c | 2 +- src/pygit2/tree.c | 8 ++++---- src/pygit2/walker.c | 2 +- 14 files changed, 80 insertions(+), 21 deletions(-) create mode 100644 pygit2/__init__.py create mode 100644 pygit2/utils.py diff --git a/pygit2/__init__.py b/pygit2/__init__.py new file mode 100644 index 000000000..cd219c836 --- /dev/null +++ b/pygit2/__init__.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2012 Nico von Geyso +# +# 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. + +from _pygit2 import * +import pygit2.utils diff --git a/pygit2/utils.py b/pygit2/utils.py new file mode 100644 index 000000000..8ebf533cd --- /dev/null +++ b/pygit2/utils.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2012 Nico von Geyso +# +# 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. + + +# feel free to add utils functions here diff --git a/setup.py b/setup.py index 9a6db3129..bc6bb7729 100644 --- a/setup.py +++ b/setup.py @@ -142,8 +142,9 @@ def run(self): maintainer='J. David Ibáñez', maintainer_email='jdavid.ibp@gmail.com', long_description=long_description, + package = ['pygit2'], ext_modules=[ - Extension('pygit2', pygit2_exts, + Extension('_pygit2', pygit2_exts, include_dirs=[libgit2_include, 'include'], library_dirs=[libgit2_lib], libraries=['git2']), diff --git a/src/pygit2.c b/src/pygit2.c index 2f7fe0bfa..fe4631663 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -116,7 +116,7 @@ moduleinit(PyObject* m) if (m == NULL) return NULL; - GitError = PyErr_NewException("pygit2.GitError", NULL, NULL); + GitError = PyErr_NewException("_pygit2.GitError", NULL, NULL); RepositoryType.tp_new = PyType_GenericNew; if (PyType_Ready(&RepositoryType) < 0) @@ -231,17 +231,17 @@ moduleinit(PyObject* m) #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC - initpygit2(void) + init_pygit2(void) { PyObject* m; - m = Py_InitModule3("pygit2", module_methods, + m = Py_InitModule3("_pygit2", module_methods, "Python bindings for libgit2."); moduleinit(m); } #else struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, - "pygit2", /* m_name */ + "_pygit2", /* m_name */ "Python bindings for libgit2.", /* m_doc */ -1, /* m_size */ module_methods, /* m_methods */ @@ -252,7 +252,7 @@ moduleinit(PyObject* m) }; PyMODINIT_FUNC - PyInit_pygit2(void) + PyInit__pygit2(void) { PyObject* m; m = PyModule_Create(&moduledef); diff --git a/src/pygit2/blob.c b/src/pygit2/blob.c index f4ac8f443..4b7819946 100644 --- a/src/pygit2/blob.c +++ b/src/pygit2/blob.c @@ -8,7 +8,7 @@ PyGetSetDef Blob_getseters[] = { PyTypeObject BlobType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Blob", /* tp_name */ + "_pygit2.Blob", /* tp_name */ sizeof(Blob), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ diff --git a/src/pygit2/commit.c b/src/pygit2/commit.c index de4c1100f..d5551890d 100644 --- a/src/pygit2/commit.c +++ b/src/pygit2/commit.c @@ -146,7 +146,7 @@ PyGetSetDef Commit_getseters[] = { PyTypeObject CommitType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Commit", /* tp_name */ + "_pygit2.Commit", /* tp_name */ sizeof(Commit), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 256b778b6..475d04c65 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -319,7 +319,7 @@ PyMappingMethods Index_as_mapping = { PyTypeObject IndexType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Index", /* tp_name */ + "_pygit2.Index", /* tp_name */ sizeof(Index), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Index_dealloc, /* tp_dealloc */ @@ -383,7 +383,7 @@ IndexIter_iternext(IndexIter *self) PyTypeObject IndexIterType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.IndexIter", /* tp_name */ + "_pygit2.IndexIter", /* tp_name */ sizeof(IndexIter), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)IndexIter_dealloc , /* tp_dealloc */ @@ -452,7 +452,7 @@ PyGetSetDef IndexEntry_getseters[] = { PyTypeObject IndexEntryType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.IndexEntry", /* tp_name */ + "_pygit2.IndexEntry", /* tp_name */ sizeof(IndexEntry), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)IndexEntry_dealloc, /* tp_dealloc */ diff --git a/src/pygit2/object.c b/src/pygit2/object.c index 44a0d760e..62a5182ef 100644 --- a/src/pygit2/object.c +++ b/src/pygit2/object.c @@ -79,7 +79,7 @@ PyMethodDef Object_methods[] = { PyTypeObject ObjectType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Object", /* tp_name */ + "_pygit2.Object", /* tp_name */ sizeof(Object), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Object_dealloc, /* tp_dealloc */ diff --git a/src/pygit2/reference.c b/src/pygit2/reference.c index 9ce78aada..f1a076d27 100644 --- a/src/pygit2/reference.c +++ b/src/pygit2/reference.c @@ -247,7 +247,7 @@ PyGetSetDef Reference_getseters[] = { PyTypeObject ReferenceType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Reference", /* tp_name */ + "_pygit2.Reference", /* tp_name */ sizeof(Reference), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Reference_dealloc, /* tp_dealloc */ diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 65c862893..a06e5f1d1 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -764,7 +764,7 @@ PyMappingMethods Repository_as_mapping = { PyTypeObject RepositoryType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Repository", /* tp_name */ + "_pygit2.Repository", /* tp_name */ sizeof(Repository), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Repository_dealloc, /* tp_dealloc */ diff --git a/src/pygit2/signature.c b/src/pygit2/signature.c index e750c821d..cc4f8c299 100644 --- a/src/pygit2/signature.c +++ b/src/pygit2/signature.c @@ -123,7 +123,7 @@ PyGetSetDef Signature_getseters[] = { PyTypeObject SignatureType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Signature", /* tp_name */ + "_pygit2.Signature", /* tp_name */ sizeof(Signature), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Signature_dealloc, /* tp_dealloc */ diff --git a/src/pygit2/tag.c b/src/pygit2/tag.c index b1536e262..ab4a2b508 100644 --- a/src/pygit2/tag.c +++ b/src/pygit2/tag.c @@ -62,7 +62,7 @@ PyGetSetDef Tag_getseters[] = { PyTypeObject TagType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Tag", /* tp_name */ + "_pygit2.Tag", /* tp_name */ sizeof(Tag), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index 0445aa19e..436b3f985 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -69,7 +69,7 @@ PyMethodDef TreeEntry_methods[] = { PyTypeObject TreeEntryType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.TreeEntry", /* tp_name */ + "_pygit2.TreeEntry", /* tp_name */ sizeof(TreeEntry), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)TreeEntry_dealloc, /* tp_dealloc */ @@ -245,7 +245,7 @@ PyMappingMethods Tree_as_mapping = { PyTypeObject TreeType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Tree", /* tp_name */ + "_pygit2.Tree", /* tp_name */ sizeof(Tree), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ @@ -371,7 +371,7 @@ PyMethodDef TreeBuilder_methods[] = { PyTypeObject TreeBuilderType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.TreeBuilder", /* tp_name */ + "_pygit2.TreeBuilder", /* tp_name */ sizeof(TreeBuilder), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)TreeBuilder_dealloc, /* tp_dealloc */ @@ -432,7 +432,7 @@ TreeIter_iternext(TreeIter *self) PyTypeObject TreeIterType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.TreeIter", /* tp_name */ + "_pygit2.TreeIter", /* tp_name */ sizeof(TreeIter), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)TreeIter_dealloc , /* tp_dealloc */ diff --git a/src/pygit2/walker.c b/src/pygit2/walker.c index 36db770f4..7b785da21 100644 --- a/src/pygit2/walker.c +++ b/src/pygit2/walker.c @@ -117,7 +117,7 @@ PyMethodDef Walker_methods[] = { PyTypeObject WalkerType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Walker", /* tp_name */ + "_pygit2.Walker", /* tp_name */ sizeof(Walker), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Walker_dealloc, /* tp_dealloc */ From 1347ad6c53cece2e363889036fc1535afee7de27 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 25 May 2012 14:25:28 +0200 Subject: [PATCH 0180/2237] added pygit2 cache files to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index b0d892a21..c1631efa8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,7 @@ dist pygit2.so test/*.pyc test/__pycache__ +pygit2/*.pyc +pygit2/__pycache__ *.egg-info *.swp From 0ac20f2d9da849c317844e92dd43662ed286cac8 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 25 May 2012 14:43:15 +0200 Subject: [PATCH 0181/2237] fixed typo in setup() --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index bc6bb7729..a20a2a512 100644 --- a/setup.py +++ b/setup.py @@ -142,7 +142,7 @@ def run(self): maintainer='J. David Ibáñez', maintainer_email='jdavid.ibp@gmail.com', long_description=long_description, - package = ['pygit2'], + packages = ['pygit2'], ext_modules=[ Extension('_pygit2', pygit2_exts, include_dirs=[libgit2_include, 'include'], From 75fda65f5205fdeb0c1c026d380d7139ea90e62b Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 25 May 2012 14:59:26 +0200 Subject: [PATCH 0182/2237] force length of PyArg_ParseTuple("s#) to be Py_ssize_t rather than int --- src/pygit2/repository.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index a06e5f1d1..763fa48be 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -1,3 +1,6 @@ +// with the following define PY_SSIZE_T_CLEAN +// the length of PyArg_ParseTuple will be Py_ssize_t rather than int +#define PY_SSIZE_T_CLEAN #include #include #include From e402d0e9eb8293a0837ddbf2ed62f7505618ac60 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 28 May 2012 12:41:43 +0200 Subject: [PATCH 0183/2237] added head-getter for Repository You do not have to lookup and resolve the HEAD reference anymore if you simply want the last commit. Repository has now a head-getter which combines all these steps. example: repo = Repository('.') head = repo.head print(head.message) --- include/pygit2/repository.h | 1 + src/pygit2/repository.c | 27 +++++++++++++++++++++++++++ test/test_refs.py | 3 +++ 3 files changed, 31 insertions(+) diff --git a/include/pygit2/repository.h b/include/pygit2/repository.h index 35cf7c0a1..1a1835375 100644 --- a/include/pygit2/repository.h +++ b/include/pygit2/repository.h @@ -13,6 +13,7 @@ int Repository_contains(Repository *self, PyObject *value); git_odb_object* Repository_read_raw(git_repository *repo, const git_oid *oid, size_t len); +PyObject* Repository_head(Repository *self); PyObject* Repository_getitem(Repository *self, PyObject *value); PyObject* Repository_read(Repository *self, PyObject *py_hex); PyObject* Repository_write(Repository *self, PyObject *args); diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 00de0d9a1..7c288e0ec 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -7,6 +7,8 @@ #include #include +extern PyObject *GitError; + extern PyTypeObject IndexType; extern PyTypeObject WalkerType; extern PyTypeObject SignatureType; @@ -155,6 +157,29 @@ Repository_contains(Repository *self, PyObject *value) return exists; } +PyObject * +Repository_head(Repository *self) +{ + git_reference *head; + const git_oid *oid; + int err, len; + + err = git_repository_head(&head, self->repo); + if(err < 0) { + if(err == GIT_ENOTFOUND) + PyErr_SetString(GitError, "head reference does not exist"); + else + Error_set(err); + + return NULL; + } + + oid = git_reference_oid(head); + + return lookup_object(self, oid, GIT_OBJ_COMMIT); +} + + PyObject * Repository_getitem(Repository *self, PyObject *value) { @@ -740,6 +765,8 @@ PyGetSetDef Repository_getseters[] = { {"index", (getter)Repository_get_index, NULL, "index file. ", NULL}, {"path", (getter)Repository_get_path, NULL, "The normalized path to the git repository.", NULL}, + {"head", (getter)Repository_head, NULL, + "Current head reference of the repository.", NULL}, {"workdir", (getter)Repository_get_workdir, NULL, "The normalized path to the working directory of the repository. " "If the repository is bare, None will be returned.", NULL}, diff --git a/test/test_refs.py b/test/test_refs.py index c28609b3c..d3a737838 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -61,6 +61,9 @@ def test_list_all_references(self): self.assertEqual(repo.listall_references(GIT_REF_SYMBOLIC), ('refs/tags/version1', )) + def test_head(self): + head = self.repo.head + self.assertEqual(LAST_COMMIT, self.repo[head.oid].hex) def test_lookup_reference(self): repo = self.repo From 6aba5b65a2b02ec55a78209bcd642a7c3ab39ab9 Mon Sep 17 00:00:00 2001 From: Petr Hosek Date: Mon, 28 May 2012 15:41:49 +0100 Subject: [PATCH 0184/2237] Diff now invoked immediately and its result stored inside object to avoid calls on repeated accesses and allow implementation of other variants Fixed indentation in diff implementation --- include/pygit2/types.h | 7 +-- src/pygit2/diff.c | 125 +++++++++++++++-------------------------- src/pygit2/tree.c | 28 ++++++--- 3 files changed, 65 insertions(+), 95 deletions(-) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 81f302f62..e6335f4c6 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -29,6 +29,7 @@ OBJECT_STRUCT(Blob, git_blob, blob) OBJECT_STRUCT(Tag, git_tag, tag) OBJECT_STRUCT(Index, git_index, index) OBJECT_STRUCT(Walker, git_revwalk, walk) +OBJECT_STRUCT(Diff, git_diff_list, diff) typedef struct { PyObject_HEAD @@ -36,12 +37,6 @@ typedef struct { const git_tree_entry *entry; } TreeEntry; -typedef struct { - PyObject_HEAD - Tree *t0; - Tree *t1; -} Diff; - typedef struct { PyObject_HEAD int old_start; diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index c957b9ca4..a1d5e2e67 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -38,7 +38,7 @@ static int diff_data_cb( if(usage != GIT_DIFF_LINE_ADDITION) PyBytes_Concat(&hunk->old_data, tmp); - return 0; + return 0; } static int diff_hunk_cb( @@ -48,46 +48,46 @@ static int diff_hunk_cb( const char *header, size_t header_len) { - PyObject *hunks; - Hunk *hunk; + PyObject *hunks; + Hunk *hunk; - hunks = PyDict_GetItemString(cb_data, "hunks"); - if(hunks == NULL) { + hunks = PyDict_GetItemString(cb_data, "hunks"); + if(hunks == NULL) { hunks = PyList_New(0); PyDict_SetItemString(cb_data, "hunks", hunks); - } + } - hunk = (Hunk*) PyType_GenericNew(&HunkType, NULL, NULL); + hunk = (Hunk*) PyType_GenericNew(&HunkType, NULL, NULL); - hunk->old_start = range->old_start; - hunk->old_lines = range->old_lines; - hunk->new_start = range->new_start; - hunk->new_lines = range->new_lines; + hunk->old_start = range->old_start; + hunk->old_lines = range->old_lines; + hunk->new_start = range->new_start; + hunk->new_lines = range->new_lines; - int len; - char* old_path, *new_path; + int len; + char* old_path, *new_path; - len = strlen(delta->old_file.path) + 1; - old_path = malloc(sizeof(char) * len); - memcpy(old_path, delta->old_file.path, len); - hunk->old_file = old_path; + len = strlen(delta->old_file.path) + 1; + old_path = malloc(sizeof(char) * len); + memcpy(old_path, delta->old_file.path, len); + hunk->old_file = old_path; - len = strlen(delta->new_file.path) + 1; - new_path = malloc(sizeof(char) * len); - memcpy(new_path, delta->new_file.path, len); - hunk->new_file = new_path; + len = strlen(delta->new_file.path) + 1; + new_path = malloc(sizeof(char) * len); + memcpy(new_path, delta->new_file.path, len); + hunk->new_file = new_path; #if PY_MAJOR_VERSION >= 3 - hunk->old_data = Py_BuildValue("y", ""); - hunk->new_data = Py_BuildValue("y", ""); + hunk->old_data = Py_BuildValue("y", ""); + hunk->new_data = Py_BuildValue("y", ""); #else - hunk->old_data = Py_BuildValue("s", ""); - hunk->new_data = Py_BuildValue("s", ""); + hunk->old_data = Py_BuildValue("s", ""); + hunk->new_data = Py_BuildValue("s", ""); #endif - PyList_Append(hunks, (PyObject*) hunk); + PyList_Append(hunks, (PyObject*) hunk); - return 0; + return 0; }; static int diff_file_cb(void *cb_data, git_diff_delta *delta, float progress) @@ -114,74 +114,40 @@ static int diff_file_cb(void *cb_data, git_diff_delta *delta, float progress) PyObject * Diff_changes(Diff *self) { - git_diff_options opts = {0}; - git_diff_list *changes; - int err; - - err = git_diff_tree_to_tree( - self->t0->repo->repo, - &opts, - self->t0->tree, - self->t1->tree, - &changes); - - if(err < 0) { - Error_set(err); - return NULL; - } - PyObject *payload; payload = PyDict_New(); git_diff_foreach( - changes, - payload, - &diff_file_cb, - &diff_hunk_cb, - &diff_data_cb + self->diff, + payload, + &diff_file_cb, + &diff_hunk_cb, + &diff_data_cb ); - git_diff_list_free(changes); return payload; } static int diff_print_cb( - void *cb_data, - git_diff_delta *delta, - git_diff_range *range, - char usage, - const char *line, - size_t line_len) + void *cb_data, + git_diff_delta *delta, + git_diff_range *range, + char usage, + const char *line, + size_t line_len) { - PyObject *data = PyBytes_FromStringAndSize(line, line_len); - PyBytes_ConcatAndDel((PyObject**) cb_data, data); + PyObject *data = PyBytes_FromStringAndSize(line, line_len); + PyBytes_ConcatAndDel((PyObject**) cb_data, data); - return 0; + return 0; } - PyObject * Diff_patch(Diff *self) { - git_diff_options opts = {0}; - git_diff_list *changes; - int err; - - err = git_diff_tree_to_tree( - self->t0->repo->repo, - &opts, - self->t0->tree, - self->t1->tree, - &changes); - - if(err < 0) { - Error_set(err); - return NULL; - } - PyObject *patch = PyBytes_FromString(""); - git_diff_print_patch(changes, &patch, &diff_print_cb); + git_diff_print_patch(self->diff, &patch, &diff_print_cb); return patch; } @@ -271,23 +237,20 @@ PyTypeObject HunkType = { 0, /* tp_new */ }; - static void Diff_dealloc(Diff *self) { - Py_XDECREF(self->t0); - Py_XDECREF(self->t1); + git_diff_list_free(self->diff); + Py_XDECREF(self->repo); PyObject_Del(self); } - PyGetSetDef Diff_getseters[] = { {"changes", (getter)Diff_changes, NULL, "raw changes", NULL}, {"patch", (getter)Diff_patch, NULL, "patch", NULL}, {NULL} }; - PyTypeObject DiffType = { PyVarObject_HEAD_INIT(NULL, 0) "pygit2.Diff", /* tp_name */ diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index 77c77d189..31abf89b5 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -231,24 +231,36 @@ Tree_getitem(Tree *self, PyObject *value) PyObject * Tree_diff_tree(Tree *self, PyObject *args) { + git_diff_options opts = {0}; + git_diff_list *diff; + int err; + Diff *py_diff; Tree *py_tree; - if (!PyArg_ParseTuple(args, "O!", &TreeType, &py_tree)) { + if (!PyArg_ParseTuple(args, "O!", &TreeType, &py_tree)) return NULL; - } + if (py_tree->repo->repo != self->repo->repo) + return Error_set(GIT_ERROR); + + err = git_diff_tree_to_tree( + self->repo->repo, + &opts, + self->tree, + py_tree->tree, + &diff); + if (err < 0) + return Error_set(err); py_diff = PyObject_New(Diff, &DiffType); if (py_diff) { Py_INCREF(py_diff); - Py_INCREF(py_tree); - Py_INCREF(self); - - py_diff->t0 = self; - py_diff->t1 = py_tree; + Py_INCREF(self->repo); + py_diff->repo = self->repo; + py_diff->diff = diff; } - return (PyObject*) py_diff; + return (PyObject*)py_diff; } PySequenceMethods Tree_as_sequence = { From 6ccb8306d40bcda9d0603bcaa52d601488504748 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 28 May 2012 15:32:37 +0200 Subject: [PATCH 0185/2237] initial travis ci build --- .travis.sh | 12 ++++++++++++ .travis.yml | 14 ++++++++++++++ 2 files changed, 26 insertions(+) create mode 100755 .travis.sh create mode 100644 .travis.yml diff --git a/.travis.sh b/.travis.sh new file mode 100755 index 000000000..16a88d19f --- /dev/null +++ b/.travis.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +cd ~ + +git clone -b master https://github.com/libgit2/libgit2.git +cd libgit2/ + +mkdir build && cd build +cmake .. -DCMAKE_INSTALL_PREFIX=../_install +cmake --build . --target install + +ls -la .. diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..8b630378c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: python + +python: + - "2.7" + - "3.2" + +env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib + +before_install: + - sudo apt-get install cmake + - "./.travis.sh" + +script: + - python setup.py test From 0b82cf0146ddd32251e48074f6c56316ab61364b Mon Sep 17 00:00:00 2001 From: Petr Hosek Date: Tue, 29 May 2012 15:12:32 +0100 Subject: [PATCH 0186/2237] Missing diff constants added --- src/pygit2.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/pygit2.c b/src/pygit2.c index 7e1ad7561..0c37fce01 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -233,6 +233,67 @@ moduleinit(PyObject* m) /* Flags for ignored files */ PyModule_AddIntConstant(m, "GIT_STATUS_IGNORED", GIT_STATUS_IGNORED); + /* Git diff flags */ + PyModule_AddIntConstant(m, "GIT_DIFF_NORMAL", GIT_DIFF_NORMAL); + PyModule_AddIntConstant(m, "GIT_DIFF_REVERSE", GIT_DIFF_REVERSE); + PyModule_AddIntConstant(m, "GIT_DIFF_FORCE_TEXT", GIT_DIFF_FORCE_TEXT); + PyModule_AddIntConstant(m, "GIT_DIFF_IGNORE_WHITESPACE", + GIT_DIFF_IGNORE_WHITESPACE); + PyModule_AddIntConstant(m, "GIT_DIFF_IGNORE_WHITESPACE_CHANGE", + GIT_DIFF_IGNORE_WHITESPACE_CHANGE); + PyModule_AddIntConstant(m, "GIT_DIFF_IGNORE_WHITESPACE_EOL", + GIT_DIFF_IGNORE_WHITESPACE_EOL); + PyModule_AddIntConstant(m, "GIT_DIFF_IGNORE_SUBMODULES", + GIT_DIFF_IGNORE_SUBMODULES); + PyModule_AddIntConstant(m, "GIT_DIFF_PATIENCE", GIT_DIFF_PATIENCE); + PyModule_AddIntConstant(m, "GIT_DIFF_INCLUDE_IGNORED", + GIT_DIFF_INCLUDE_IGNORED); + PyModule_AddIntConstant(m, "GIT_DIFF_INCLUDE_UNTRACKED", + GIT_DIFF_INCLUDE_UNTRACKED); + PyModule_AddIntConstant(m, "GIT_DIFF_INCLUDE_UNMODIFIED", + GIT_DIFF_INCLUDE_UNMODIFIED); + PyModule_AddIntConstant(m, "GIT_DIFF_RECURSE_UNTRACKED_DIRS", + GIT_DIFF_RECURSE_UNTRACKED_DIRS); + + /* Flags for diffed files */ + PyModule_AddIntConstant(m, "GIT_DIFF_FILE_VALID_OID", + GIT_DIFF_FILE_VALID_OID); + PyModule_AddIntConstant(m, "GIT_DIFF_FILE_FREE_PATH", + GIT_DIFF_FILE_FREE_PATH); + PyModule_AddIntConstant(m, "GIT_DIFF_FILE_BINARY", GIT_DIFF_FILE_BINARY); + PyModule_AddIntConstant(m, "GIT_DIFF_FILE_NOT_BINARY", + GIT_DIFF_FILE_NOT_BINARY); + PyModule_AddIntConstant(m, "GIT_DIFF_FILE_FREE_DATA", + GIT_DIFF_FILE_FREE_DATA); + PyModule_AddIntConstant(m, "GIT_DIFF_FILE_UNMAP_DATA", + GIT_DIFF_FILE_UNMAP_DATA); + + /* Flags for diff deltas */ + PyModule_AddIntConstant(m, "GIT_DELTA_UNMODIFIED", GIT_DELTA_UNMODIFIED); + PyModule_AddIntConstant(m, "GIT_DELTA_ADDED", GIT_DELTA_ADDED); + PyModule_AddIntConstant(m, "GIT_DELTA_DELETED", GIT_DELTA_DELETED); + PyModule_AddIntConstant(m, "GIT_DELTA_MODIFIED", GIT_DELTA_MODIFIED); + PyModule_AddIntConstant(m, "GIT_DELTA_RENAMED", GIT_DELTA_RENAMED); + PyModule_AddIntConstant(m, "GIT_DELTA_COPIED", GIT_DELTA_COPIED); + PyModule_AddIntConstant(m, "GIT_DELTA_IGNORED", GIT_DELTA_IGNORED); + PyModule_AddIntConstant(m, "GIT_DELTA_UNTRACKED", GIT_DELTA_UNTRACKED); + + /* Flags for diffed lines origin */ + PyModule_AddIntConstant(m, "GIT_DIFF_LINE_CONTEXT", GIT_DIFF_LINE_CONTEXT); + PyModule_AddIntConstant(m, "GIT_DIFF_LINE_ADDITION", + GIT_DIFF_LINE_ADDITION); + PyModule_AddIntConstant(m, "GIT_DIFF_LINE_DELETION", + GIT_DIFF_LINE_DELETION); + PyModule_AddIntConstant(m, "GIT_DIFF_LINE_ADD_EOFNL", + GIT_DIFF_LINE_ADD_EOFNL); + PyModule_AddIntConstant(m, "GIT_DIFF_LINE_DEL_EOFNL", + GIT_DIFF_LINE_DEL_EOFNL); + PyModule_AddIntConstant(m, "GIT_DIFF_LINE_FILE_HDR", + GIT_DIFF_LINE_FILE_HDR); + PyModule_AddIntConstant(m, "GIT_DIFF_LINE_HUNK_HDR", + GIT_DIFF_LINE_HUNK_HDR); + PyModule_AddIntConstant(m, "GIT_DIFF_LINE_BINARY", GIT_DIFF_LINE_BINARY); + return m; } From c06e10e67e746099b8d454212acc56391199eb31 Mon Sep 17 00:00:00 2001 From: Petr Hosek Date: Tue, 29 May 2012 17:41:07 +0100 Subject: [PATCH 0187/2237] Support for diff merge operation added --- src/pygit2/diff.c | 30 +++++++++++++++-- .../2a/d1d3456c5c4a1c9e40aeeddb9cd20b409623c8 | Bin 0 -> 99 bytes .../2c/dae28389c059815e951d0bb9eed6533f61a46b | 2 ++ .../39/a3001fcc2b9541fdcf4be2d662618a5d213f47 | 1 + .../6a/270c81bc80b59591e0d2e3abd7d03450c0c395 | Bin 0 -> 29 bytes .../72/abb8755b2cc6c4e40fd9f50f54384d973a2f22 | Bin 0 -> 99 bytes .../97/d615e1bc273c40c94a726814e7b93fdb5a1b36 | Bin 0 -> 29 bytes test/data/testrepo.git/refs/heads/master | 2 +- test/test_diff.py | 31 ++++++++++++++++++ 9 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 test/data/testrepo.git/objects/2a/d1d3456c5c4a1c9e40aeeddb9cd20b409623c8 create mode 100644 test/data/testrepo.git/objects/2c/dae28389c059815e951d0bb9eed6533f61a46b create mode 100644 test/data/testrepo.git/objects/39/a3001fcc2b9541fdcf4be2d662618a5d213f47 create mode 100644 test/data/testrepo.git/objects/6a/270c81bc80b59591e0d2e3abd7d03450c0c395 create mode 100644 test/data/testrepo.git/objects/72/abb8755b2cc6c4e40fd9f50f54384d973a2f22 create mode 100644 test/data/testrepo.git/objects/97/d615e1bc273c40c94a726814e7b93fdb5a1b36 diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index a1d5e2e67..da6c514c5 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -53,8 +53,8 @@ static int diff_hunk_cb( hunks = PyDict_GetItemString(cb_data, "hunks"); if(hunks == NULL) { - hunks = PyList_New(0); - PyDict_SetItemString(cb_data, "hunks", hunks); + hunks = PyList_New(0); + PyDict_SetItemString(cb_data, "hunks", hunks); } hunk = (Hunk*) PyType_GenericNew(&HunkType, NULL, NULL); @@ -237,6 +237,24 @@ PyTypeObject HunkType = { 0, /* tp_new */ }; +PyObject * +Diff_merge(Diff *self, PyObject *args) +{ + Diff *py_diff; + int err; + + if (!PyArg_ParseTuple(args, "O!", &DiffType, &py_diff)) + return NULL; + if (py_diff->repo->repo != self->repo->repo) + return Error_set(GIT_ERROR); + + err = git_diff_merge(self->diff, py_diff->diff); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + static void Diff_dealloc(Diff *self) { @@ -251,6 +269,12 @@ PyGetSetDef Diff_getseters[] = { {NULL} }; +static PyMethodDef Diff_methods[] = { + {"merge", (PyCFunction)Diff_merge, METH_VARARGS, + "Merge one diff into another."}, + {NULL, NULL, 0, NULL} +}; + PyTypeObject DiffType = { PyVarObject_HEAD_INIT(NULL, 0) "pygit2.Diff", /* tp_name */ @@ -279,7 +303,7 @@ PyTypeObject DiffType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + Diff_methods, /* tp_methods */ 0, /* tp_members */ Diff_getseters, /* tp_getset */ 0, /* tp_base */ diff --git a/test/data/testrepo.git/objects/2a/d1d3456c5c4a1c9e40aeeddb9cd20b409623c8 b/test/data/testrepo.git/objects/2a/d1d3456c5c4a1c9e40aeeddb9cd20b409623c8 new file mode 100644 index 0000000000000000000000000000000000000000..1c1c60d6d1f77a74ec73a0fb89a3d5228ebfd21e GIT binary patch literal 99 zcmV-p0G$7L0V^p=O;xZkV=y!@Ff%bxNMy)T=V{#2uyyLh2bUhNzJ9?Z;K1RjP{|~Q z>DNRb?oqdKIO$cCA@Y2u{p~1eGZOD +䋡 scjquV:/ ; %ĈH#V~4]K ^7=={7|B~{\rMQHz])ۧST+ ?.mEZ}s] \ No newline at end of file diff --git a/test/data/testrepo.git/objects/39/a3001fcc2b9541fdcf4be2d662618a5d213f47 b/test/data/testrepo.git/objects/39/a3001fcc2b9541fdcf4be2d662618a5d213f47 new file mode 100644 index 000000000..aa5e5ff1c --- /dev/null +++ b/test/data/testrepo.git/objects/39/a3001fcc2b9541fdcf4be2d662618a5d213f47 @@ -0,0 +1 @@ +xO9N1$+:Gz|=B|G{m-s/ RJ6w"p*蝵Q4'CK^b&/NUG1B=4)1)늝g11qBn\AWx9d}[!&FgEϸg` 9p>)cj.4+]̝OW)M]= \ No newline at end of file diff --git a/test/data/testrepo.git/objects/6a/270c81bc80b59591e0d2e3abd7d03450c0c395 b/test/data/testrepo.git/objects/6a/270c81bc80b59591e0d2e3abd7d03450c0c395 new file mode 100644 index 0000000000000000000000000000000000000000..7db6959f002db94ba1c5aed4e6afc421b3f33aa8 GIT binary patch literal 29 lcmb Date: Wed, 30 May 2012 07:27:47 +0200 Subject: [PATCH 0188/2237] do not segfault if error is not set by libgit2 --- src/pygit2/error.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/pygit2/error.c b/src/pygit2/error.c index 45fd02a65..246787c30 100644 --- a/src/pygit2/error.c +++ b/src/pygit2/error.c @@ -33,16 +33,17 @@ PyObject * Error_type(int type) // Critical const git_error* error = giterr_last(); - switch (error->klass) { - case GITERR_NOMEMORY: - return PyExc_MemoryError; - case GITERR_OS: - return PyExc_OSError; - case GITERR_INVALID: - return PyExc_ValueError; - default: - return GitError; + if(error != NULL) { + switch (error->klass) { + case GITERR_NOMEMORY: + return PyExc_MemoryError; + case GITERR_OS: + return PyExc_OSError; + case GITERR_INVALID: + return PyExc_ValueError; + } } + return GitError; } @@ -51,10 +52,13 @@ PyObject* Error_set(int err) assert(err < 0); if(err != GIT_ERROR) { //expected failure - PyErr_SetNone(Error_type(err)); + PyErr_SetNone(Error_type(err)); } else { //critical failure - const git_error* error = giterr_last(); - PyErr_SetString(Error_type(err), error->message); + const git_error* error = giterr_last(); + char* message = (error == NULL) ? + "(No error information given)" : error->message; + + PyErr_SetString(Error_type(err), message); } return NULL; From d14393c1df1e7e3e0f7c9d1fa019ef4d5bfa4eac Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 30 May 2012 07:28:35 +0200 Subject: [PATCH 0189/2237] Diff and Hunk should not inherit from _libgit2.Object --- src/pygit2.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index 7e1ad7561..615357851 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -131,12 +131,6 @@ moduleinit(PyObject* m) CommitType.tp_base = &ObjectType; if (PyType_Ready(&CommitType) < 0) return NULL; - DiffType.tp_base = &ObjectType; - if (PyType_Ready(&DiffType) < 0) - return NULL; - HunkType.tp_base = &ObjectType; - if (PyType_Ready(&HunkType) < 0) - return NULL; TreeType.tp_base = &ObjectType; if (PyType_Ready(&TreeType) < 0) return NULL; @@ -147,6 +141,11 @@ moduleinit(PyObject* m) if (PyType_Ready(&TagType) < 0) return NULL; + if (PyType_Ready(&DiffType) < 0) + return NULL; + if (PyType_Ready(&HunkType) < 0) + return NULL; + TreeEntryType.tp_new = PyType_GenericNew; if (PyType_Ready(&TreeEntryType) < 0) return NULL; From ade2e5812ca74ed493bb2766e397cf40105061fc Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 30 May 2012 07:50:33 +0200 Subject: [PATCH 0190/2237] support for diff against workdir or index Added missing support for a diff against workdir or an index. The latter one differs from `git diff HEAD` because git itself does a merge of workdir and index (see docs of git_diff_workdir_to_tree). --- include/pygit2/diff.h | 5 ++ include/pygit2/types.h | 4 +- src/pygit2/diff.c | 186 ++++++++++++++++++++++++++--------------- src/pygit2/tree.c | 27 ++++-- test/test_diff.py | 48 +++++++++++ 5 files changed, 194 insertions(+), 76 deletions(-) diff --git a/include/pygit2/diff.h b/include/pygit2/diff.h index 306023490..1f1b793d1 100644 --- a/include/pygit2/diff.h +++ b/include/pygit2/diff.h @@ -6,6 +6,11 @@ #include #include +#define DIFF_CHECK_TYPES(_x, _y, _type_x, _type_y) \ + PyObject_TypeCheck(_x, _type_x) && \ + PyObject_TypeCheck(_y, _type_y) + + PyObject* Diff_changes(Diff *self); PyObject* Diff_patch(Diff *self); diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 81f302f62..37edf8914 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -38,8 +38,8 @@ typedef struct { typedef struct { PyObject_HEAD - Tree *t0; - Tree *t1; + PyObject *a; + PyObject *b; } Diff; typedef struct { diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index c957b9ca4..81be39816 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -6,9 +6,60 @@ #include #include +extern PyObject *GitError; + +extern PyTypeObject TreeType; +extern PyTypeObject IndexType; extern PyTypeObject DiffType; extern PyTypeObject HunkType; +int diff_get_list(Diff *diff, git_diff_options* opts, git_diff_list** list) +{ + int err = -1; + + if(diff->b == NULL) { + if(PyObject_TypeCheck(diff->a, &TreeType)) { + err = git_diff_workdir_to_tree( + ((Tree*) diff->a)->repo->repo, + opts, + ((Tree*) diff->a)->tree, + list + ); + + } else { + err = git_diff_workdir_to_index( + ((Index*) diff->a)->repo->repo, + opts, + list + ); + } + } else if(DIFF_CHECK_TYPES(diff->a, diff->b, &TreeType, &TreeType)) { + err = git_diff_tree_to_tree( + ((Tree*) diff->a)->repo->repo, + opts, + ((Tree*) diff->a)->tree, + ((Tree*) diff->b)->tree, + list + ); + } else if(DIFF_CHECK_TYPES(diff->a, diff->b, &IndexType, &TreeType)) { + err = git_diff_index_to_tree( + ((Tree*) diff->b)->repo->repo, + opts, + ((Tree*) diff->b)->tree, + list + ); + } else if(DIFF_CHECK_TYPES(diff->a, diff->b, &TreeType, &IndexType)) { + err = git_diff_index_to_tree( + ((Tree*) diff->a)->repo->repo, + opts, + ((Tree*) diff->a)->tree, + list + ); + } + + return err; +} + static int diff_data_cb( void *cb_data, git_diff_delta *delta, @@ -23,12 +74,12 @@ static int diff_data_cb( hunks = PyDict_GetItemString(cb_data, "hunks"); if(hunks == NULL) - return -1; + return -1; size = PyList_Size(hunks); hunk = (Hunk*) PyList_GetItem(hunks, size-1); if(hunk == NULL) - return -1; + return -1; tmp = PyBytes_FromStringAndSize(line, line_len); @@ -38,7 +89,7 @@ static int diff_data_cb( if(usage != GIT_DIFF_LINE_ADDITION) PyBytes_Concat(&hunk->old_data, tmp); - return 0; + return 0; } static int diff_hunk_cb( @@ -48,65 +99,76 @@ static int diff_hunk_cb( const char *header, size_t header_len) { - PyObject *hunks; - Hunk *hunk; + PyObject *hunks; + Hunk *hunk; - hunks = PyDict_GetItemString(cb_data, "hunks"); - if(hunks == NULL) { - hunks = PyList_New(0); - PyDict_SetItemString(cb_data, "hunks", hunks); - } + hunks = PyDict_GetItemString(cb_data, "hunks"); + if(hunks == NULL) { + hunks = PyList_New(0); + PyDict_SetItemString(cb_data, "hunks", hunks); + } - hunk = (Hunk*) PyType_GenericNew(&HunkType, NULL, NULL); + hunk = (Hunk*) PyType_GenericNew(&HunkType, NULL, NULL); - hunk->old_start = range->old_start; - hunk->old_lines = range->old_lines; - hunk->new_start = range->new_start; - hunk->new_lines = range->new_lines; + hunk->old_start = range->old_start; + hunk->old_lines = range->old_lines; + hunk->new_start = range->new_start; + hunk->new_lines = range->new_lines; - int len; - char* old_path, *new_path; + int len; + char* old_path, *new_path; - len = strlen(delta->old_file.path) + 1; - old_path = malloc(sizeof(char) * len); - memcpy(old_path, delta->old_file.path, len); - hunk->old_file = old_path; + if(delta->old_file.path != NULL) { + len = strlen(delta->old_file.path) + 1; + old_path = malloc(sizeof(char) * len); + memcpy(old_path, delta->old_file.path, len); + hunk->old_file = old_path; + } else { + hunk->old_file = ""; + } - len = strlen(delta->new_file.path) + 1; - new_path = malloc(sizeof(char) * len); - memcpy(new_path, delta->new_file.path, len); - hunk->new_file = new_path; + if(delta->new_file.path != NULL) { + len = strlen(delta->new_file.path) + 1; + new_path = malloc(sizeof(char) * len); + memcpy(new_path, delta->new_file.path, len); + hunk->new_file = new_path; + } else { + hunk->new_file = ""; + } #if PY_MAJOR_VERSION >= 3 - hunk->old_data = Py_BuildValue("y", ""); - hunk->new_data = Py_BuildValue("y", ""); + hunk->old_data = Py_BuildValue("y", ""); + hunk->new_data = Py_BuildValue("y", ""); #else - hunk->old_data = Py_BuildValue("s", ""); - hunk->new_data = Py_BuildValue("s", ""); + hunk->old_data = Py_BuildValue("s", ""); + hunk->new_data = Py_BuildValue("s", ""); #endif - PyList_Append(hunks, (PyObject*) hunk); + PyList_Append(hunks, (PyObject*) hunk); - return 0; + return 0; }; static int diff_file_cb(void *cb_data, git_diff_delta *delta, float progress) { PyObject *files, *file; - files = PyDict_GetItemString(cb_data, "files"); - if(files == NULL) { - files = PyList_New(0); - PyDict_SetItemString(cb_data, "files", files); - } + if(delta->old_file.path != NULL && delta->new_file.path != NULL) { + files = PyDict_GetItemString(cb_data, "files"); - file = Py_BuildValue("(s,s,i)", - delta->old_file.path, - delta->new_file.path, - delta->status - ); + if(files == NULL) { + files = PyList_New(0); + PyDict_SetItemString(cb_data, "files", files); + } - PyList_Append(files, file); + file = Py_BuildValue("(s,s,i)", + delta->old_file.path, + delta->new_file.path, + delta->status + ); + + PyList_Append(files, file); + } return 0; } @@ -116,23 +178,18 @@ Diff_changes(Diff *self) { git_diff_options opts = {0}; git_diff_list *changes; + PyObject *payload; int err; - err = git_diff_tree_to_tree( - self->t0->repo->repo, - &opts, - self->t0->tree, - self->t1->tree, - &changes); + err = diff_get_list(self, &opts, &changes); if(err < 0) { Error_set(err); return NULL; } - - PyObject *payload; - payload = PyDict_New(); + payload = PyDict_New(); + git_diff_foreach( changes, payload, @@ -140,6 +197,7 @@ Diff_changes(Diff *self) &diff_hunk_cb, &diff_data_cb ); + git_diff_list_free(changes); return payload; @@ -153,10 +211,10 @@ static int diff_print_cb( const char *line, size_t line_len) { - PyObject *data = PyBytes_FromStringAndSize(line, line_len); - PyBytes_ConcatAndDel((PyObject**) cb_data, data); + PyObject *data = PyBytes_FromStringAndSize(line, line_len); + PyBytes_ConcatAndDel((PyObject**) cb_data, data); - return 0; + return 0; } @@ -167,13 +225,7 @@ Diff_patch(Diff *self) git_diff_list *changes; int err; - err = git_diff_tree_to_tree( - self->t0->repo->repo, - &opts, - self->t0->tree, - self->t1->tree, - &changes); - + err = diff_get_list(self, &opts, &changes); if(err < 0) { Error_set(err); return NULL; @@ -197,14 +249,14 @@ Hunk_init(Hunk *self, PyObject *args, PyObject *kwds) self->old_data = PyString_FromString(""); if (self->old_data == NULL) { - Py_DECREF(self); - return -1; + Py_XDECREF(self); + return -1; } self->new_data = PyString_FromString(""); if (self->new_data == NULL) { - Py_DECREF(self); - return -1; + Py_XDECREF(self); + return -1; } return 0; @@ -275,8 +327,8 @@ PyTypeObject HunkType = { static void Diff_dealloc(Diff *self) { - Py_XDECREF(self->t0); - Py_XDECREF(self->t1); + Py_XDECREF(self->a); + Py_XDECREF(self->b); PyObject_Del(self); } diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index 77c77d189..3bb671b68 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -9,6 +9,7 @@ extern PyTypeObject TreeType; extern PyTypeObject DiffType; extern PyTypeObject TreeIterType; +extern PyTypeObject IndexType; void TreeEntry_dealloc(TreeEntry *self) @@ -232,20 +233,26 @@ PyObject * Tree_diff_tree(Tree *self, PyObject *args) { Diff *py_diff; - Tree *py_tree; + PyObject* py_obj = NULL; - if (!PyArg_ParseTuple(args, "O!", &TreeType, &py_tree)) { + if (!PyArg_ParseTuple(args, "|O", &py_obj)) return NULL; + + if (py_obj != NULL && !PyObject_TypeCheck(py_obj, &TreeType) && + !PyObject_TypeCheck(py_obj, &IndexType)) { + + PyErr_SetObject(PyExc_TypeError, py_obj); + return NULL; } py_diff = PyObject_New(Diff, &DiffType); if (py_diff) { Py_INCREF(py_diff); - Py_INCREF(py_tree); Py_INCREF(self); + Py_XINCREF(py_obj); - py_diff->t0 = self; - py_diff->t1 = py_tree; + py_diff->a = (PyObject*) self; + py_diff->b = (PyObject*) py_obj; } return (PyObject*) py_diff; @@ -269,8 +276,14 @@ PyMappingMethods Tree_as_mapping = { }; PyMethodDef Tree_methods[] = { - {"diff", (PyCFunction)Tree_diff_tree, METH_VARARGS, - "Diff two trees."}, + { + "diff", (PyCFunction)Tree_diff_tree, METH_VARARGS, + "Get changes between current tree instance with another tree, an " + "index or the working dir.\n\n" + "@param obj : if not given compare diff against working dir. " + "Possible valid arguments are instances of Tree or Index.\n" + "@returns Diff instance" + }, {NULL} }; diff --git a/test/test_diff.py b/test/test_diff.py index fadaff22d..bc88af268 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -39,6 +39,7 @@ COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10' COMMIT_SHA1_2 = 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c' + PATCH = b"""diff --git a/a b/a index 7f129fd..af431f2 100644 --- a/a @@ -55,6 +56,45 @@ -c/d contents """ +DIFF_INDEX_EXPECTED = [ + "staged_changes", + "staged_changes_file_deleted", + "staged_changes_file_modified", + "staged_delete", + "staged_delete_file_modified", + "staged_new", + "staged_new_file_deleted", + "staged_new_file_modified" +] + +DIFF_WORKDIR_EXPECTED = [ + 'file_deleted', + 'modified_file', + 'staged_changes', + 'staged_changes_file_deleted', + 'staged_changes_file_modified', + 'staged_delete', + 'staged_delete_file_modified', + 'subdir/deleted_file', + 'subdir/modified_file' +] + +class DiffDirtyTest(utils.DirtyRepoTestCase): + def test_diff_empty_index(self): + repo = self.repo + head = repo[repo.lookup_reference('HEAD').resolve().oid] + diff = head.tree.diff(repo.index) + + files = [x[1] for x in diff.changes['files']] + self.assertEqual(DIFF_INDEX_EXPECTED, files) + + def test_workdir_to_tree(self): + repo = self.repo + head = repo[repo.lookup_reference('HEAD').resolve().oid] + diff = head.tree.diff() + + files = [x[1] for x in diff.changes['files']] + self.assertEqual(DIFF_WORKDIR_EXPECTED, files) class DiffTest(utils.BareRepoTestCase): @@ -63,6 +103,14 @@ def test_diff_invalid(self): commit_b = self.repo[COMMIT_SHA1_2] self.assertRaises(TypeError, commit_a.tree.diff, commit_b) + def test_diff_empty_index(self): + repo = self.repo + head = repo[repo.lookup_reference('HEAD').resolve().oid] + diff = head.tree.diff(repo.index) + + files = [x[0].split('/')[0] for x in diff.changes['files']] + self.assertEqual([x.name for x in head.tree], files) + def test_diff_tree(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] From eb62331b3675f72f80cbc29ab98889b29aa577b8 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 30 May 2012 08:32:13 +0200 Subject: [PATCH 0191/2237] added missing tests --- src/pygit2/repository.c | 2 +- test/test_repository.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 7c288e0ec..f86337b20 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -162,7 +162,7 @@ Repository_head(Repository *self) { git_reference *head; const git_oid *oid; - int err, len; + int err; err = git_repository_head(&head, self->repo); if(err < 0) { diff --git a/test/test_repository.py b/test/test_repository.py index 8fa47755a..3ca7eedd0 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -35,18 +35,24 @@ from os.path import join, realpath from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, init_repository, \ - discover_repository + discover_repository, Commit from . import utils __author__ = 'dborowitz@google.com (Dave Borowitz)' +HEAD_SHA = '5fe808e8953c12735680c257f56600cb0de44b10' A_HEX_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' A_BIN_SHA = binascii.unhexlify(A_HEX_SHA.encode('ascii')) class RepositoryTest(utils.BareRepoTestCase): + def test_head(self): + head = self.repo.head + self.assertTrue(HEAD_SHA, head.hex) + self.assertTrue(type(head), Commit) + def test_read(self): self.assertRaises(TypeError, self.repo.read, 123) self.assertRaisesWithArg(KeyError, '1' * 40, self.repo.read, '1' * 40) From ce4e41113569936c4bc14e906c8d6740cff79a82 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 30 May 2012 14:39:02 +0200 Subject: [PATCH 0192/2237] added revlog --- include/pygit2/types.h | 8 +++ src/pygit2.c | 3 + src/pygit2/reference.c | 129 +++++++++++++++++++++++++++++++++++++++++ test/test_revwalk.py | 17 ++++++ 4 files changed, 157 insertions(+) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 81f302f62..264a9399f 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -76,6 +76,14 @@ typedef struct { git_reference *reference; } Reference; +typedef struct { + PyObject_HEAD + PyObject *oid_old; + PyObject *oid_new; + PyObject *committer; + char *msg; +} ReferenceLogEntry; + typedef struct { PyObject_HEAD Object *obj; diff --git a/src/pygit2.c b/src/pygit2.c index 7e1ad7561..d2b1ba8b5 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -53,6 +53,7 @@ PyTypeObject IndexEntryType; PyTypeObject IndexIterType; PyTypeObject WalkerType; PyTypeObject ReferenceType; +PyTypeObject ReferenceLogEntryType; PyTypeObject SignatureType; @@ -165,6 +166,8 @@ moduleinit(PyObject* m) ReferenceType.tp_new = PyType_GenericNew; if (PyType_Ready(&ReferenceType) < 0) return NULL; + if (PyType_Ready(&ReferenceLogEntryType) < 0) + return NULL; SignatureType.tp_new = PyType_GenericNew; if (PyType_Ready(&SignatureType) < 0) return NULL; diff --git a/src/pygit2/reference.c b/src/pygit2/reference.c index 37f24c4c1..8f8e6a924 100644 --- a/src/pygit2/reference.c +++ b/src/pygit2/reference.c @@ -1,13 +1,18 @@ #define PY_SSIZE_T_CLEAN #include +#include +#include #include #include #include #include +#include #include extern PyObject *GitError; +extern PyTypeObject ReferenceLogEntryType; + void Reference_dealloc(Reference *self) { @@ -221,6 +226,128 @@ Reference_get_type(Reference *self) return PyInt_FromLong(c_type); } +PyObject * +Reference_log(Reference *self) +{ + ReferenceLogEntry *py_entry; + PyObject *py_list; + git_reflog *reflog; + ssize_t i, size; + + CHECK_REFERENCE(self); + + git_reflog_read(&reflog, self->reference); + + size = git_reflog_entrycount(reflog); + + py_list = PyList_New(size); + + for(i = 0; i < size; ++i) { + char oid_old[40], oid_new[40]; + git_signature *signature = NULL; + + const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, i); + + py_entry = (ReferenceLogEntry*) PyType_GenericNew( + &ReferenceLogEntryType, NULL, NULL + ); + + git_oid_fmt(oid_old, git_reflog_entry_oidold(entry)); + git_oid_fmt(oid_new, git_reflog_entry_oidnew(entry)); + + py_entry->oid_new = PyUnicode_FromStringAndSize(oid_new, 40); + py_entry->oid_old = PyUnicode_FromStringAndSize(oid_old, 40); + + py_entry->msg = strdup(git_reflog_entry_msg(entry)); + + signature = git_signature_dup( + git_reflog_entry_committer(entry) + ); + + if(signature != NULL) + py_entry->committer = build_signature( + (PyObject*)py_entry, signature, "utf-8" + ); + + PyList_SetItem(py_list, i, (PyObject*) py_entry); + } + + git_reflog_free(reflog); + + return py_list; +} + +static int +ReferenceLogEntry_init(ReferenceLogEntry *self, PyObject *args, PyObject *kwds) +{ + self->oid_old = Py_None; + self->oid_new = Py_None; + self->msg = ""; + self->committer = Py_None; + + return 0; +} + + +static void +ReferenceLogEntry_dealloc(ReferenceLogEntry *self) +{ + Py_XDECREF(self->oid_old); + Py_XDECREF(self->oid_new); + Py_XDECREF(self->committer); + free(self->msg); + PyObject_Del(self); +} + +PyMemberDef ReferenceLogEntry_members[] = { + {"oid_new", T_OBJECT, offsetof(ReferenceLogEntry, oid_new), 0, "new oid"}, + {"oid_old", T_OBJECT, offsetof(ReferenceLogEntry, oid_old), 0, "old oid"}, + {"message", T_STRING, offsetof(ReferenceLogEntry, msg), 0, "message"}, + {"committer", T_OBJECT, offsetof(ReferenceLogEntry, committer), 0, "committer"}, + {NULL} +}; + +PyTypeObject ReferenceLogEntryType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.ReferenceLogEntry", /* tp_name */ + sizeof(ReferenceLogEntry), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)ReferenceLogEntry_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "ReferenceLog object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + ReferenceLogEntry_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)ReferenceLogEntry_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + PyMethodDef Reference_methods[] = { {"delete", (PyCFunction)Reference_delete, METH_NOARGS, "Delete this reference. It will no longer be valid!"}, @@ -230,6 +357,8 @@ PyMethodDef Reference_methods[] = { "Reload the reference from the file-system."}, {"resolve", (PyCFunction)Reference_resolve, METH_NOARGS, "Resolve a symbolic reference and return a direct reference."}, + {"log", (PyCFunction)Reference_log, METH_NOARGS, + "Retrieves the current reference log."}, {NULL} }; diff --git a/test/test_revwalk.py b/test/test_revwalk.py index 9cfb0d22b..47058ac63 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -46,6 +46,23 @@ '6aaa262e655dd54252e5813c8e5acd7780ed097d', 'acecd5ea2924a4b900e7e149496e1f4b57976e51'] +REVLOGS = [ + ('J. David Ibañez', 'commit (initial): First commit'), + ('J. David Ibañez', 'checkout: moving from master to i18n'), + ('J. David Ibañez', 'commit: Say hello in Spanish'), + ('J. David Ibañez', 'commit: Say hello in French'), + ('J. David Ibañez', 'checkout: moving from i18n to master'), + ('J. David Ibañez', 'commit: Add .gitignore file'), + ('J. David Ibañez', 'merge i18n: Merge made by recursive.') +] + + +class RevlogTestTest(utils.RepoTestCase): + def test_log(self): + ref = self.repo.lookup_reference('HEAD') + for i,entry in enumerate(ref.log()): + self.assertEqual(entry.committer.name, REVLOGS[i][0]) + self.assertEqual(entry.message, REVLOGS[i][1]) class WalkerTest(utils.RepoTestCase): From e7d7fcdae642ef6b99ddcfc95a1b30e539709028 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 30 May 2012 14:39:27 +0200 Subject: [PATCH 0193/2237] use _libgit2 instead of libgit2 --- src/pygit2/diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index c957b9ca4..47aae4692 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -232,7 +232,7 @@ PyMemberDef Hunk_members[] = { PyTypeObject HunkType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Hunk", /* tp_name */ + "_pygit2.Hunk", /* tp_name */ sizeof(Hunk), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Hunk_dealloc, /* tp_dealloc */ @@ -290,7 +290,7 @@ PyGetSetDef Diff_getseters[] = { PyTypeObject DiffType = { PyVarObject_HEAD_INIT(NULL, 0) - "pygit2.Diff", /* tp_name */ + "_pygit2.Diff", /* tp_name */ sizeof(Diff), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Diff_dealloc, /* tp_dealloc */ From a8a0ac8290fc0bfd582afc3cff8e3aeb0472ab51 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 30 May 2012 15:57:35 +0200 Subject: [PATCH 0194/2237] use generator instead of python list --- include/pygit2/types.h | 11 ++- src/pygit2.c | 5 +- src/pygit2/reference.c | 162 +++++++++++++++++++++++++++-------------- 3 files changed, 120 insertions(+), 58 deletions(-) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 72d851ee6..a8e1ef8be 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -82,7 +82,16 @@ typedef struct { PyObject *oid_new; PyObject *committer; char *msg; -} ReferenceLogEntry; +} RefLogEntry; + +typedef struct { + PyObject_HEAD + Reference *reference; + git_reflog *reflog; + int i; + int size; +} RefLogIter; + typedef struct { PyObject_HEAD diff --git a/src/pygit2.c b/src/pygit2.c index 70b0550e5..bbe89aeaa 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -53,7 +53,8 @@ PyTypeObject IndexEntryType; PyTypeObject IndexIterType; PyTypeObject WalkerType; PyTypeObject ReferenceType; -PyTypeObject ReferenceLogEntryType; +PyTypeObject RefLogIterType; +PyTypeObject RefLogEntryType; PyTypeObject SignatureType; @@ -165,7 +166,7 @@ moduleinit(PyObject* m) ReferenceType.tp_new = PyType_GenericNew; if (PyType_Ready(&ReferenceType) < 0) return NULL; - if (PyType_Ready(&ReferenceLogEntryType) < 0) + if (PyType_Ready(&RefLogEntryType) < 0) return NULL; SignatureType.tp_new = PyType_GenericNew; if (PyType_Ready(&SignatureType) < 0) diff --git a/src/pygit2/reference.c b/src/pygit2/reference.c index 8f8e6a924..60da959ce 100644 --- a/src/pygit2/reference.c +++ b/src/pygit2/reference.c @@ -9,9 +9,92 @@ #include #include + extern PyObject *GitError; +extern PyTypeObject RefLogEntryType; + + +void RefLogIter_dealloc(RefLogIter *self) +{ + Py_XDECREF(self->reference); + git_reflog_free(self->reflog); + PyObject_GC_Del(self); +} + +PyObject* RefLogIter_iternext(PyObject *self) +{ + RefLogIter *p = (RefLogIter *) self; + + if (p->i < p->size) { + char oid_old[40], oid_new[40]; + RefLogEntry *py_entry; + git_signature *signature; + + const git_reflog_entry *entry = git_reflog_entry_byindex(p->reflog, p->i); + + py_entry = (RefLogEntry*) PyType_GenericNew( + &RefLogEntryType, NULL, NULL + ); + + git_oid_fmt(oid_old, git_reflog_entry_oidold(entry)); + git_oid_fmt(oid_new, git_reflog_entry_oidnew(entry)); + + py_entry->oid_new = PyUnicode_FromStringAndSize(oid_new, 40); + py_entry->oid_old = PyUnicode_FromStringAndSize(oid_old, 40); + + py_entry->msg = strdup(git_reflog_entry_msg(entry)); + + signature = git_signature_dup( + git_reflog_entry_committer(entry) + ); + + if(signature != NULL) + py_entry->committer = build_signature( + (Object*)py_entry, signature, "utf-8" + ); + + ++(p->i); + + return (PyObject*) py_entry; + + } + + PyErr_SetNone(PyExc_StopIteration); + return NULL; +} -extern PyTypeObject ReferenceLogEntryType; +PyTypeObject RefLogIterType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_libgit2.RefLogIter", /*tp_name*/ + sizeof(RefLogIter), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)RefLogIter_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 */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + /* tp_flags: Py_TPFLAGS_HAVE_ITER tells python to + use tp_iter and tp_iternext fields. */ + "Internal reflog iterator object.", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter: __iter__() method */ + (iternextfunc) RefLogIter_iternext /* tp_iternext: next() method */ +}; void Reference_dealloc(Reference *self) @@ -229,56 +312,25 @@ Reference_get_type(Reference *self) PyObject * Reference_log(Reference *self) { - ReferenceLogEntry *py_entry; - PyObject *py_list; - git_reflog *reflog; - ssize_t i, size; + RefLogIter *iter; CHECK_REFERENCE(self); - git_reflog_read(&reflog, self->reference); - - size = git_reflog_entrycount(reflog); - - py_list = PyList_New(size); - - for(i = 0; i < size; ++i) { - char oid_old[40], oid_new[40]; - git_signature *signature = NULL; - - const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, i); - - py_entry = (ReferenceLogEntry*) PyType_GenericNew( - &ReferenceLogEntryType, NULL, NULL - ); + iter = PyObject_New(RefLogIter, &RefLogIterType); + if (iter) { + iter->reference = self; + git_reflog_read(&iter->reflog, self->reference); + iter->size = git_reflog_entrycount(iter->reflog); + iter->i = 0; - git_oid_fmt(oid_old, git_reflog_entry_oidold(entry)); - git_oid_fmt(oid_new, git_reflog_entry_oidnew(entry)); - - py_entry->oid_new = PyUnicode_FromStringAndSize(oid_new, 40); - py_entry->oid_old = PyUnicode_FromStringAndSize(oid_old, 40); - - py_entry->msg = strdup(git_reflog_entry_msg(entry)); - - signature = git_signature_dup( - git_reflog_entry_committer(entry) - ); - - if(signature != NULL) - py_entry->committer = build_signature( - (PyObject*)py_entry, signature, "utf-8" - ); - - PyList_SetItem(py_list, i, (PyObject*) py_entry); + Py_INCREF(self); + Py_INCREF(iter); } - - git_reflog_free(reflog); - - return py_list; + return (PyObject*)iter; } static int -ReferenceLogEntry_init(ReferenceLogEntry *self, PyObject *args, PyObject *kwds) +RefLogEntry_init(RefLogEntry *self, PyObject *args, PyObject *kwds) { self->oid_old = Py_None; self->oid_new = Py_None; @@ -290,7 +342,7 @@ ReferenceLogEntry_init(ReferenceLogEntry *self, PyObject *args, PyObject *kwds) static void -ReferenceLogEntry_dealloc(ReferenceLogEntry *self) +RefLogEntry_dealloc(RefLogEntry *self) { Py_XDECREF(self->oid_old); Py_XDECREF(self->oid_new); @@ -299,20 +351,20 @@ ReferenceLogEntry_dealloc(ReferenceLogEntry *self) PyObject_Del(self); } -PyMemberDef ReferenceLogEntry_members[] = { - {"oid_new", T_OBJECT, offsetof(ReferenceLogEntry, oid_new), 0, "new oid"}, - {"oid_old", T_OBJECT, offsetof(ReferenceLogEntry, oid_old), 0, "old oid"}, - {"message", T_STRING, offsetof(ReferenceLogEntry, msg), 0, "message"}, - {"committer", T_OBJECT, offsetof(ReferenceLogEntry, committer), 0, "committer"}, +PyMemberDef RefLogEntry_members[] = { + {"oid_new", T_OBJECT, offsetof(RefLogEntry, oid_new), 0, "new oid"}, + {"oid_old", T_OBJECT, offsetof(RefLogEntry, oid_old), 0, "old oid"}, + {"message", T_STRING, offsetof(RefLogEntry, msg), 0, "message"}, + {"committer", T_OBJECT, offsetof(RefLogEntry, committer), 0, "committer"}, {NULL} }; -PyTypeObject ReferenceLogEntryType = { +PyTypeObject RefLogEntryType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.ReferenceLogEntry", /* tp_name */ - sizeof(ReferenceLogEntry), /* tp_basicsize */ + "_pygit2.RefLogEntry", /* tp_name */ + sizeof(RefLogEntry), /* tp_basicsize */ 0, /* tp_itemsize */ - (destructor)ReferenceLogEntry_dealloc, /* tp_dealloc */ + (destructor)RefLogEntry_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -336,14 +388,14 @@ PyTypeObject ReferenceLogEntryType = { 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ - ReferenceLogEntry_members, /* tp_members */ + RefLogEntry_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)ReferenceLogEntry_init, /* tp_init */ + (initproc)RefLogEntry_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; From 785108e771d0b3666337c5ca30a0be8b7d777a31 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 31 May 2012 14:45:46 +0200 Subject: [PATCH 0195/2237] updated README (diff and RefLog) --- README.rst | 67 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/README.rst b/README.rst index 9333513f1..d70d7cbe6 100644 --- a/README.rst +++ b/README.rst @@ -55,9 +55,7 @@ called *objects*, there are four types (commits, trees, blobs and tags), for each type pygit2 has a Python class:: # Get the last commit - >>> head = repo.lookup_reference('HEAD') - >>> head = head.resolve() - >>> commit = repo[head.oid] + >>> head = repo.head # Show commits and trees >>> commit @@ -95,7 +93,8 @@ Objects can not be modified once they have been created. Commits ----------------- -:: +A commit is a snapshot of the working dir with meta informations like author, +committer and others.:: Commit.author -- the author of the commit Commit.committer -- the committer of the commit @@ -103,6 +102,7 @@ Commits Commit.tree -- the tree object attached to the commit Commit.parents -- the list of parent commits + Signatures ............. @@ -162,17 +162,49 @@ This is the interface of a tree entry:: TreeEntry.attributes -- the Unix file attributes TreeEntry.to_object() -- returns the git object (equivalent to repo[entry.oid]) + +Diff +----------------- + +A diff shows the changes between trees, an index or the working dir:: + + # Diff two trees + >>> t0 = repo.head.tree + >>> t1 = repo.head.parents[0] + >>> diff = t0.diff(t1) + >>> diff + + # Diff a tree with the index + >>> tree = repo.head.tree + >>> diff = tree.diff(repo.index) + + # Diff a tree with the current working dir + >>> tree = repo.head.tree + >>> diff = tree.diff() + +The interface for a diff:: + + Diff.changes -- Dict of 'files' and 'hunks' for every change + Diff.patch -- a patch for every changeset + Diff.merge -- Merge two Diffs + + Blobs ----------------- -A blob is equivalent to a file in a file system:: +A blob is equivalent to a file in a file system.:: + + # create a blob out of memory + >>> oid = repo.create_blob('foo bar') + >>> blob = repo[oid] Blob.data -- the contents of the blob, a byte string Tags ----------------- -XXX +A tag is a static label for a commit. See references for more information. + References @@ -180,18 +212,32 @@ References Reference lookup:: + >>> all_refs = repo.listall_references() >>> master_ref = repo.lookup_reference("refs/heads/master") >>> commit = repo[master_ref.oid] +Reference log:: + + >>> head = repo.lookup_reference('refs/heads/master') + >>> for entry in head.log(): + ... print(entry.message) + +The interface for RefLogEntry:: + + RefLogEntry.committer -- Signature of Committer + RefLogEntry.message -- the message of the RefLogEntry + RefLogEntry.oid_old -- oid of old reference + RefLogEntry.oid_new -- oid of new reference + Revision walking ================= -:: +You can iterate through the revision history with repo.walk:: >>> from pygit2 import GIT_SORT_TIME >>> for commit in repo.walk(oid, GIT_SORT_TIME): - ... print commit.hex + ... print(commit.hex) The index file ================= @@ -236,15 +282,14 @@ for the topic), send a pull request. TODO ---------------- -XXX - +See issues AUTHORS ============== * David Borowitz -* J. David Ibáñez +* J David Ibáñez LICENSE From 872f4dbd6d02faecee1a07cfe39a1c06096057d2 Mon Sep 17 00:00:00 2001 From: pistacchio Date: Thu, 31 May 2012 15:42:28 +0200 Subject: [PATCH 0196/2237] solved issue 100 of the main repository --- src/pygit2.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index 57e8f15b8..eede3160d 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -37,25 +37,25 @@ extern PyObject *GitError; -PyTypeObject RepositoryType; -PyTypeObject ObjectType; -PyTypeObject CommitType; -PyTypeObject DiffType; -PyTypeObject HunkType; -PyTypeObject TreeType; -PyTypeObject TreeBuilderType; -PyTypeObject TreeEntryType; -PyTypeObject TreeIterType; -PyTypeObject BlobType; -PyTypeObject TagType; -PyTypeObject IndexType; -PyTypeObject IndexEntryType; -PyTypeObject IndexIterType; -PyTypeObject WalkerType; -PyTypeObject ReferenceType; -PyTypeObject RefLogIterType; -PyTypeObject RefLogEntryType; -PyTypeObject SignatureType; +extern PyTypeObject RepositoryType; +extern PyTypeObject ObjectType; +extern PyTypeObject CommitType; +extern PyTypeObject DiffType; +extern PyTypeObject HunkType; +extern PyTypeObject TreeType; +extern PyTypeObject TreeBuilderType; +extern PyTypeObject TreeEntryType; +extern PyTypeObject TreeIterType; +extern PyTypeObject BlobType; +extern PyTypeObject TagType; +extern PyTypeObject IndexType; +extern PyTypeObject IndexEntryType; +extern PyTypeObject IndexIterType; +extern PyTypeObject WalkerType; +extern PyTypeObject ReferenceType; +extern PyTypeObject RefLogIterType; +extern PyTypeObject RefLogEntryType; +extern PyTypeObject SignatureType; PyObject * From c4a1e2ac005d2158fb56d999bf7bbcc1f08deb04 Mon Sep 17 00:00:00 2001 From: Christian Boos Date: Tue, 5 Jun 2012 10:17:31 +0200 Subject: [PATCH 0197/2237] fix windows VC9 build Move variable declarations to the top of the functions. --- src/pygit2/diff.c | 6 +++--- src/pygit2/error.c | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index 38bb5f499..777ba766d 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -55,6 +55,9 @@ static int diff_hunk_cb( { PyObject *hunks; Hunk *hunk; + int len; + char* old_path, *new_path; + hunks = PyDict_GetItemString(cb_data, "hunks"); if (hunks == NULL) { @@ -71,9 +74,6 @@ static int diff_hunk_cb( hunk->new_start = range->new_start; hunk->new_lines = range->new_lines; - int len; - char* old_path, *new_path; - if (delta->old_file.path != NULL) { len = strlen(delta->old_file.path) + 1; old_path = malloc(sizeof(char) * len); diff --git a/src/pygit2/error.c b/src/pygit2/error.c index 246787c30..fccf8a893 100644 --- a/src/pygit2/error.c +++ b/src/pygit2/error.c @@ -4,6 +4,7 @@ PyObject *GitError; PyObject * Error_type(int type) { + const git_error* error; // Expected switch (type) { /** Input does not exist in the scope searched. */ @@ -32,7 +33,7 @@ PyObject * Error_type(int type) } // Critical - const git_error* error = giterr_last(); + error = giterr_last(); if(error != NULL) { switch (error->klass) { case GITERR_NOMEMORY: @@ -66,13 +67,14 @@ PyObject* Error_set(int err) PyObject* Error_set_str(int err, const char *str) { + const git_error* error; if (err == GIT_ENOTFOUND) { /* KeyError expects the arg to be the missing key. */ PyErr_SetString(PyExc_KeyError, str); return NULL; } - const git_error* error = giterr_last(); + error = giterr_last(); return PyErr_Format(Error_type(err), "%s: %s", str, error->message); } From 38d5bf03ad8cbd7549200b9531dada6ebe06e2ec Mon Sep 17 00:00:00 2001 From: Christian Boos Date: Wed, 6 Jun 2012 15:58:07 +0200 Subject: [PATCH 0198/2237] tests: new fancy asserts are for Python 2.7 only --- test/test_diff.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/test/test_diff.py b/test/test_diff.py index ab495e6e1..893cb8e6b 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -118,8 +118,10 @@ def test_diff_tree(self): diff = commit_a.tree.diff(commit_b.tree) - self.assertIsNotNone(diff) - self.assertIn(('a','a', 3), diff.changes['files']) + # self.assertIsNotNone is 2.7 only + self.assertTrue(diff is not None) + # self.assertIn is 2.7 only + self.assertTrue(('a','a', 3) in diff.changes['files']) self.assertEqual(2, len(diff.changes['hunks'])) hunk = diff.changes['hunks'][0] @@ -140,17 +142,21 @@ def test_diff_merge(self): commit_c = self.repo[COMMIT_SHA1_3] diff_b = commit_a.tree.diff(commit_b.tree) - self.assertIsNotNone(diff_b) + # self.assertIsNotNone is 2.7 only + self.assertTrue(diff_b is not None) diff_c = commit_b.tree.diff(commit_c.tree) - self.assertIsNotNone(diff_c) + # self.assertIsNotNone is 2.7 only + self.assertTrue(diff_c is not None) - self.assertNotIn(('b','b', 3), diff_b.changes['files']) - self.assertIn(('b','b', 3), diff_c.changes['files']) + # assertIn / assertNotIn are 2.7 only + self.assertTrue(('b','b', 3) not in diff_b.changes['files']) + self.assertTrue(('b','b', 3) in diff_c.changes['files']) diff_b.merge(diff_c) - self.assertIn(('b','b', 3), diff_b.changes['files']) + # assertIn is 2.7 only + self.assertTrue(('b','b', 3) in diff_b.changes['files']) hunk = diff_b.changes['hunks'][1] self.assertEqual(hunk.old_start, 1) From 6859e4b02aa8b2b8d6f3ee074ba381225e0e4ddf Mon Sep 17 00:00:00 2001 From: Christian Boos Date: Wed, 6 Jun 2012 21:52:13 +0200 Subject: [PATCH 0199/2237] ci: add all Python versions listed as supported in README.rst --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8b630378c..84418b1f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,9 @@ language: python python: + - "2.6" - "2.7" + - "3.1" - "3.2" env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib From c4d17b759be2a3e740031a4c2d4690882351ab1a Mon Sep 17 00:00:00 2001 From: Martin Lenders Date: Fri, 8 Jun 2012 12:17:37 +0200 Subject: [PATCH 0200/2237] Create Config type --- include/pygit2/config.h | 8 ++++ include/pygit2/types.h | 1 + src/pygit2.c | 7 +++ src/pygit2/config.c | 97 +++++++++++++++++++++++++++++++++++++++++ test/__init__.py | 4 +- test/test_config.py | 50 +++++++++++++++++++++ 6 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 include/pygit2/config.h create mode 100644 src/pygit2/config.c create mode 100644 test/test_config.py diff --git a/include/pygit2/config.h b/include/pygit2/config.h new file mode 100644 index 000000000..29e56ad72 --- /dev/null +++ b/include/pygit2/config.h @@ -0,0 +1,8 @@ +#ifndef INCLUDE_pygit2_config_h +#define INCLUDE_pygit2_config_h + +#define PY_SSIZE_T_CLEAN +#include +#include + +#endif diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 44e4f317d..5393f3c5a 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -30,6 +30,7 @@ OBJECT_STRUCT(Tag, git_tag, tag) OBJECT_STRUCT(Index, git_index, index) OBJECT_STRUCT(Walker, git_revwalk, walk) OBJECT_STRUCT(Diff, git_diff_list, diff) +OBJECT_STRUCT(Config, git_config, config) typedef struct { PyObject_HEAD diff --git a/src/pygit2.c b/src/pygit2.c index eede3160d..8ecc0d2f8 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -52,6 +52,7 @@ extern PyTypeObject IndexType; extern PyTypeObject IndexEntryType; extern PyTypeObject IndexIterType; extern PyTypeObject WalkerType; +extern PyTypeObject ConfigType; extern PyTypeObject ReferenceType; extern PyTypeObject RefLogIterType; extern PyTypeObject RefLogEntryType; @@ -160,6 +161,9 @@ moduleinit(PyObject* m) TreeBuilderType.tp_new = PyType_GenericNew; if (PyType_Ready(&TreeBuilderType) < 0) return NULL; + ConfigType.tp_new = PyType_GenericNew; + if (PyType_Ready(&ConfigType) < 0) + return NULL; WalkerType.tp_new = PyType_GenericNew; if (PyType_Ready(&WalkerType) < 0) return NULL; @@ -190,6 +194,9 @@ moduleinit(PyObject* m) Py_INCREF(&TreeType); PyModule_AddObject(m, "Tree", (PyObject *)&TreeType); + Py_INCREF(&ConfigType); + PyModule_AddObject(m, "Config", (PyObject *)&ConfigType); + Py_INCREF(&BlobType); PyModule_AddObject(m, "Blob", (PyObject *)&BlobType); diff --git a/src/pygit2/config.c b/src/pygit2/config.c new file mode 100644 index 000000000..a0deadf1b --- /dev/null +++ b/src/pygit2/config.c @@ -0,0 +1,97 @@ +#define PY_SSIZE_T_CLEAN +#include +#include +#include +#include +#include + +extern PyTypeObject ConfigType; + +int +Config_init(Config *self, PyObject *args, PyObject *kwds) +{ + char *path; + int err; + + if (kwds) { + PyErr_SetString(PyExc_TypeError, + "Repository takes no keyword arguments"); + return -1; + } + + if (PySequence_Length(args) > 0) { + if (!PyArg_ParseTuple(args, "s", &path)) { + return -1; + } + err = git_config_open_ondisk(&self->config, path); + if (err < 0) { + Error_set_str(err, path); + return -1; + } + } else { + err = git_config_new(&self->config); + if (err < 0) { + Error_set(err); + return -1; + } + } + return 0; +} + +void +Config_dealloc(Config *self) +{ + PyObject_GC_UnTrack(self); + Py_XDECREF(self->repo); + git_config_free(self->config); + PyObject_GC_Del(self); +} + +int +Config_traverse(Config *self, visitproc visit, void *arg) +{ + Py_VISIT(self->repo); + return 0; +} + +PyTypeObject ConfigType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.Config", /* tp_name */ + sizeof(Config), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Config_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_HAVE_GC, /* tp_flags */ + "Configuration management", /* tp_doc */ + (traverseproc)Config_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Config_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; diff --git a/test/__init__.py b/test/__init__.py index fcb82c572..2cf6bb991 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -35,8 +35,8 @@ import unittest -names = ['blob', 'commit', 'index', 'refs', 'repository', 'revwalk', 'tag', - 'tree', 'signature', 'status', 'treebuilder', 'diff'] +names = ['blob', 'commit', 'config', 'index', 'refs', 'repository', 'revwalk', + 'tag', 'tree', 'signature', 'status', 'treebuilder'] def test_suite(): modules = ['test.test_%s' % n for n in names] return unittest.defaultTestLoader.loadTestsFromNames(modules) diff --git a/test/test_config.py b/test/test_config.py new file mode 100644 index 000000000..cc2623c8b --- /dev/null +++ b/test/test_config.py @@ -0,0 +1,50 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2012 elego +# +# 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. + +"""Tests for Index files.""" + +import os +import unittest + +import pygit2 +from . import utils + + +__author__ = 'mlenders@elegosoft.com (M. Lenders)' + +config_filename = "test_config" + +class ConfigTest(utils.RepoTestCase): + + def test_new(self): + config_write = pygit2.Config(config_filename) + + self.assertNotEqual(config_write, None) + + +if __name__ == '__main__': + unittest.main() From 068b833c87c2909bb8988fb22960db10be2f1867 Mon Sep 17 00:00:00 2001 From: Martin Lenders Date: Fri, 8 Jun 2012 12:40:03 +0200 Subject: [PATCH 0201/2237] Add getter for global and system config --- include/pygit2/config.h | 3 +++ src/pygit2/config.c | 60 ++++++++++++++++++++++++++++++++++++++++- test/test_config.py | 12 +++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/include/pygit2/config.h b/include/pygit2/config.h index 29e56ad72..33b8465ca 100644 --- a/include/pygit2/config.h +++ b/include/pygit2/config.h @@ -5,4 +5,7 @@ #include #include +PyObject* Config_get_global_config(void); +PyObject* Config_get_system_config(void); + #endif diff --git a/src/pygit2/config.c b/src/pygit2/config.c index a0deadf1b..57378aff8 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -54,6 +54,64 @@ Config_traverse(Config *self, visitproc visit, void *arg) return 0; } +PyObject * +Config_open(char *c_path) { + PyObject *py_path = Py_BuildValue("(s)", c_path); + Config *config = PyObject_GC_New(Config, &ConfigType); + + Config_init(config, py_path, NULL); + + Py_INCREF(config); + + return (PyObject *)config; +} + +PyObject * +Config_get_global_config(void) +{ + char path[GIT_PATH_MAX]; + int err; + + err = git_config_find_global(path, GIT_PATH_MAX); + if (err < 0) { + if (err == GIT_ENOTFOUND) { + PyErr_SetString(PyExc_IOError, "Global config file not found."); + return NULL; + } + return Error_set(err); + } + + return Config_open(path); +} + +PyObject * +Config_get_system_config(void) +{ + char path[GIT_PATH_MAX]; + int err; + + err = git_config_find_system(path, GIT_PATH_MAX); + if (err < 0) { + if (err == GIT_ENOTFOUND) { + PyErr_SetString(PyExc_IOError, "System config file not found."); + return NULL; + } + return Error_set(err); + } + + return Config_open(path); +} + +PyMethodDef Config_methods[] = { + {"get_system_config", (PyCFunction)Config_get_system_config, + METH_NOARGS | METH_STATIC, + "Return an object representing the system configuration file."}, + {"get_global_config", (PyCFunction)Config_get_global_config, + METH_NOARGS | METH_STATIC, + "Return an object representing the global configuration file."}, + {NULL} +}; + PyTypeObject ConfigType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Config", /* tp_name */ @@ -83,7 +141,7 @@ PyTypeObject ConfigType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + Config_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ diff --git a/test/test_config.py b/test/test_config.py index cc2623c8b..cda8dead2 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -40,6 +40,18 @@ class ConfigTest(utils.RepoTestCase): + def test_global_config(self): + try: + self.assertNotEqual(None, pygit2.Config.get_global_config()) + except IOError: + pass + + def test_system_config(self): + try: + self.assertNotEqual(None, pygit2.Config.get_system_config()) + except IOError: + pass + def test_new(self): config_write = pygit2.Config(config_filename) From fcacea310bf38b2edf5660f09a8c9bee2ba76035 Mon Sep 17 00:00:00 2001 From: Martin Lenders Date: Fri, 8 Jun 2012 16:00:59 +0200 Subject: [PATCH 0202/2237] Implement addition of files to Config --- include/pygit2/config.h | 1 + src/pygit2/config.c | 21 +++++++++++++++++++++ test/test_config.py | 13 +++++++++++++ 3 files changed, 35 insertions(+) diff --git a/include/pygit2/config.h b/include/pygit2/config.h index 33b8465ca..d281902a9 100644 --- a/include/pygit2/config.h +++ b/include/pygit2/config.h @@ -7,5 +7,6 @@ PyObject* Config_get_global_config(void); PyObject* Config_get_system_config(void); +PyObject* Config_add_file(Config *self, PyObject *args); #endif diff --git a/src/pygit2/config.c b/src/pygit2/config.c index 57378aff8..d05f1a752 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -102,6 +102,25 @@ Config_get_system_config(void) return Config_open(path); } +PyObject * +Config_add_file(Config *self, PyObject *args) +{ + int err; + char *path; + int priority; + + if (!PyArg_ParseTuple(args, "si", &path, &priority)) + return NULL; + + err = git_config_add_file_ondisk(self->config, path, priority); + if (err < 0) { + Error_set_str(err, path); + return NULL; + } + + Py_RETURN_NONE; +} + PyMethodDef Config_methods[] = { {"get_system_config", (PyCFunction)Config_get_system_config, METH_NOARGS | METH_STATIC, @@ -109,6 +128,8 @@ PyMethodDef Config_methods[] = { {"get_global_config", (PyCFunction)Config_get_global_config, METH_NOARGS | METH_STATIC, "Return an object representing the global configuration file."}, + {"add_file", (PyCFunction)Config_add_file, METH_VARARGS, + "Add a config file instance to an existing config."}, {NULL} }; diff --git a/test/test_config.py b/test/test_config.py index cda8dead2..e3ef10e23 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -57,6 +57,19 @@ def test_new(self): self.assertNotEqual(config_write, None) + def test_add(self): + config = pygit2.Config.get_global_config() + + new_file = open(config_filename, "w") + new_file.write("[this]\n\tthat = true\n") + new_file.write("[something \"other\"]\n\there = false") + new_file.close() + + config.add_file(config_filename, 0) + + os.remove(config_filename) + + if __name__ == '__main__': unittest.main() From e77d248713e1b92df0d326e6c402cfef3d198caa Mon Sep 17 00:00:00 2001 From: Martin Lenders Date: Fri, 8 Jun 2012 16:10:33 +0200 Subject: [PATCH 0203/2237] Add back reference from Repository to Config --- include/pygit2/repository.h | 1 + include/pygit2/types.h | 1 + src/pygit2/repository.c | 37 +++++++++++++++++++++++++++++++++++++ test/test_config.py | 3 +++ 4 files changed, 42 insertions(+) diff --git a/include/pygit2/repository.h b/include/pygit2/repository.h index 1a1835375..8ea07207b 100644 --- a/include/pygit2/repository.h +++ b/include/pygit2/repository.h @@ -20,6 +20,7 @@ PyObject* Repository_write(Repository *self, PyObject *args); PyObject* Repository_get_index(Repository *self, void *closure); PyObject* Repository_get_path(Repository *self, void *closure); PyObject* Repository_get_workdir(Repository *self, void *closure); +PyObject* Repository_get_config(Repository *self, void *closure); PyObject* Repository_walk(Repository *self, PyObject *args); PyObject* Repository_create_blob(Repository *self, PyObject *args); PyObject* Repository_create_commit(Repository *self, PyObject *args); diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 5393f3c5a..10cab836a 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -10,6 +10,7 @@ typedef struct { PyObject_HEAD git_repository *repo; PyObject *index; /* It will be None for a bare repository */ + PyObject *config; } Repository; /* The structs for some of the object subtypes are identical except for diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 21506fd38..d4165801f 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -17,6 +17,7 @@ extern PyTypeObject CommitType; extern PyTypeObject BlobType; extern PyTypeObject TagType; extern PyTypeObject TreeBuilderType; +extern PyTypeObject ConfigType; extern PyTypeObject DiffType; git_otype @@ -326,6 +327,37 @@ Repository_get_workdir(Repository *self, void *closure) return to_path(c_path); } +PyObject * +Repository_get_config(Repository *self, void *closure) +{ + int err; + git_config *config; + Config *py_config; + + assert(self->repo); + + if (self->config == NULL) { + err = git_repository_config(&config, self->repo); + if (err < 0) + return Error_set(err); + + py_config = PyObject_GC_New(Config, &ConfigType); + if (!py_config) { + git_config_free(config); + return NULL; + } + + Py_INCREF(self); + py_config->repo = self; + py_config->config = config; + PyObject_GC_Track(py_config); + self->config = (PyObject*)py_config; + } + + Py_INCREF(self->config); + return self->config; +} + PyObject * Repository_walk(Repository *self, PyObject *args) { @@ -768,6 +800,11 @@ PyGetSetDef Repository_getseters[] = { "The normalized path to the git repository.", NULL}, {"head", (getter)Repository_head, NULL, "Current head reference of the repository.", NULL}, + {"config", (getter)Repository_get_config, NULL, + "Get the configuration file for this repository.\n\n" + "If a configuration file has not been set, the default " + "config set for the repository will be returned, including " + "global and system configurations (if they are available).", NULL}, {"workdir", (getter)Repository_get_workdir, NULL, "The normalized path to the working directory of the repository. " "If the repository is bare, None will be returned.", NULL}, diff --git a/test/test_config.py b/test/test_config.py index e3ef10e23..92b7b6027 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -40,6 +40,9 @@ class ConfigTest(utils.RepoTestCase): + def test_config(self): + self.assertNotEqual(None, self.repo.config) + def test_global_config(self): try: self.assertNotEqual(None, pygit2.Config.get_global_config()) From e126b09380c44d87967a284bca3427450990bd2d Mon Sep 17 00:00:00 2001 From: Martin Lenders Date: Fri, 8 Jun 2012 17:11:10 +0200 Subject: [PATCH 0204/2237] Implement dictionary-like behaviour for Config --- include/pygit2/config.h | 2 + src/pygit2/config.c | 105 +++++++++++++++++++++++++++++++++- test/data/testrepo.git/config | 1 + test/data/testrepo.tar | Bin 61440 -> 61440 bytes test/test_config.py | 62 ++++++++++++++++++++ 5 files changed, 168 insertions(+), 2 deletions(-) diff --git a/include/pygit2/config.h b/include/pygit2/config.h index d281902a9..9d6017f5d 100644 --- a/include/pygit2/config.h +++ b/include/pygit2/config.h @@ -8,5 +8,7 @@ PyObject* Config_get_global_config(void); PyObject* Config_get_system_config(void); PyObject* Config_add_file(Config *self, PyObject *args); +PyObject* Config_getitem(Config *self, PyObject *key); +int Config_setitem(Config *self, PyObject *key, PyObject *value); #endif diff --git a/src/pygit2/config.c b/src/pygit2/config.c index d05f1a752..b18977a72 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -102,6 +102,90 @@ Config_get_system_config(void) return Config_open(path); } +int +Config_contains(Config *self, PyObject *py_key) { + int err; + const char *c_value; + const char *c_key; + + if (!(c_key = py_str_to_c_str(py_key,NULL))) + return -1; + + err = git_config_get_string(&c_value, self->config, c_key); + + if (err == GIT_ENOTFOUND) + return 0; + if (err < 0) { + Error_set(err); + return -1; + } + + return 1; +} + +PyObject * +Config_getitem(Config *self, PyObject *py_key) +{ + int err; + int64_t c_intvalue; + int c_boolvalue; + const char *c_charvalue; + const char *c_key; + + if (!(c_key = py_str_to_c_str(py_key,NULL))) + return NULL; + + err = git_config_get_int64(&c_intvalue, self->config, c_key); + if (err == GIT_OK) { + return PyInt_FromLong((long)c_intvalue); + } + + err = git_config_get_bool(&c_boolvalue, self->config, c_key); + if (err == GIT_OK) { + return PyBool_FromLong((long)c_boolvalue); + } + + err = git_config_get_string(&c_charvalue, self->config, c_key); + if (err < 0) { + if (err == GIT_ENOTFOUND) { + PyErr_SetObject(PyExc_KeyError, py_key); + return NULL; + } + return Error_set(err); + } + + return PyUnicode_FromString(c_charvalue); +} + +int +Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) +{ + int err; + const char *c_key; + + if (!(c_key = py_str_to_c_str(py_key,NULL))) + return -1; + + if (!py_value) { + err = git_config_delete(self->config, c_key); + } else if (PyBool_Check(py_value)) { + err = git_config_set_bool(self->config, c_key, + (int)PyObject_IsTrue(py_value)); + } else if (PyInt_Check(py_value)) { + err = git_config_set_int64(self->config, c_key, + (int64_t)PyInt_AsLong(py_value)); + } else { + py_value = PyObject_Str(py_value); + err = git_config_set_string(self->config, c_key, + py_str_to_c_str(py_value,NULL)); + } + if (err < 0) { + Error_set(err); + return -1; + } + return 0; +} + PyObject * Config_add_file(Config *self, PyObject *args) { @@ -133,6 +217,23 @@ PyMethodDef Config_methods[] = { {NULL} }; +PySequenceMethods Config_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)Config_contains,/* sq_contains */ +}; + +PyMappingMethods Config_as_mapping = { + 0, /* mp_length */ + (binaryfunc)Config_getitem, /* mp_subscript */ + (objobjargproc)Config_setitem, /* mp_ass_subscript */ +}; + PyTypeObject ConfigType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Config", /* tp_name */ @@ -145,8 +246,8 @@ PyTypeObject ConfigType = { 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + &Config_as_sequence, /* tp_as_sequence */ + &Config_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ diff --git a/test/data/testrepo.git/config b/test/data/testrepo.git/config index 83142d2a1..6a442c4cb 100644 --- a/test/data/testrepo.git/config +++ b/test/data/testrepo.git/config @@ -2,5 +2,6 @@ repositoryformatversion = 0 filemode = true bare = true + editor = 'ed' [gc] auto = no diff --git a/test/data/testrepo.tar b/test/data/testrepo.tar index 1b613a8d20cadb866c663c2fb87361d0f0fff861..a064aa21bd9b68483d83340d58b587ada87eccd9 100644 GIT binary patch delta 219 zcmZp8z})bFc>`aq2} delta 170 zcmZp8z})bFc>`a^gr%YyK d{6Fbf%w|K5C5(#_QZ_3bdca5zVzLAC0syUjN0k5o diff --git a/test/test_config.py b/test/test_config.py index 92b7b6027..b9e6aaeee 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -60,6 +60,17 @@ def test_new(self): self.assertNotEqual(config_write, None) + config_write['core.bare'] = False + config_write['core.editor'] = 'ed' + + config_read = pygit2.Config(config_filename) + self.assertTrue('core.bare' in config_write) + self.assertFalse(config_write['core.bare']) + self.assertTrue('core.editor' in config_write) + self.assertEqual(config_write['core.editor'], 'ed') + + os.remove(config_filename) + def test_add(self): config = pygit2.Config.get_global_config() @@ -69,9 +80,60 @@ def test_add(self): new_file.close() config.add_file(config_filename, 0) + self.assertTrue('this.that' in config) + self.assertTrue(config['this.that']) + self.assertTrue('something.other.here' in config) + self.assertFalse(config['something.other.here']) os.remove(config_filename) + def test_read(self): + config = self.repo.config + + self.assertRaises(TypeError, lambda: config[()]) + self.assertRaises(TypeError, lambda: config[-4]) + self.assertRaisesWithArg(pygit2.GitError, + "Invalid variable name: 'abc'", lambda: config['abc']) + self.assertRaisesWithArg(KeyError, 'abc.def', lambda: config['abc.def']) + + self.assertTrue('core.bare' in config) + self.assertFalse(config['core.bare']) + self.assertTrue('core.editor' in config) + self.assertEqual(config['core.editor'], 'ed') + self.assertTrue('core.repositoryformatversion' in config) + self.assertEqual(config['core.repositoryformatversion'], 0) + + new_file = open(config_filename, "w") + new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n") + new_file.close() + + def test_write(self): + config = self.repo.config + + with self.assertRaises(TypeError): + config[()] = 'This should not work' + + self.assertFalse('core.dummy1' in config) + config['core.dummy1'] = 42 + self.assertTrue('core.dummy1' in config) + self.assertEqual(config['core.dummy1'], 42) + + self.assertFalse('core.dummy2' in config) + config['core.dummy2'] = 'foobar' + self.assertTrue('core.dummy2' in config) + self.assertEqual(config['core.dummy2'], 'foobar') + + self.assertFalse('core.dummy3' in config) + config['core.dummy3'] = True + self.assertTrue('core.dummy3' in config) + self.assertTrue(config['core.dummy3']) + + del config['core.dummy1'] + self.assertFalse('core.dummy1' in config) + del config['core.dummy2'] + self.assertFalse('core.dummy2' in config) + del config['core.dummy3'] + self.assertFalse('core.dummy3' in config) if __name__ == '__main__': From 1acd488c68b9f4b419aee2e177875e4b705f2375 Mon Sep 17 00:00:00 2001 From: Martin Lenders Date: Fri, 8 Jun 2012 17:13:48 +0200 Subject: [PATCH 0205/2237] Wrap git_config_foreach in Config --- include/pygit2/config.h | 1 + src/pygit2/config.c | 56 +++++++++++++++++++++++++++++++++++++++++ test/test_config.py | 10 ++++++++ 3 files changed, 67 insertions(+) diff --git a/include/pygit2/config.h b/include/pygit2/config.h index 9d6017f5d..3ae9d4bff 100644 --- a/include/pygit2/config.h +++ b/include/pygit2/config.h @@ -9,6 +9,7 @@ PyObject* Config_get_global_config(void); PyObject* Config_get_system_config(void); PyObject* Config_add_file(Config *self, PyObject *args); PyObject* Config_getitem(Config *self, PyObject *key); +PyObject* Config_foreach(Config *self, PyObject *args); int Config_setitem(Config *self, PyObject *key, PyObject *value); #endif diff --git a/src/pygit2/config.c b/src/pygit2/config.c index b18977a72..ab18962ba 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -186,6 +186,56 @@ Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) return 0; } +int +Config_foreach_callback_wrapper(const char *c_name, const char *c_value, + void *c_payload) +{ + PyObject *args = (PyObject *)c_payload; + PyObject *py_callback = NULL; + PyObject *py_payload = NULL; + PyObject *py_result = NULL; + int c_result; + + if (!PyArg_ParseTuple(args, "O|O", &py_callback, &py_payload)) + return 0; + + if (py_payload) + args = Py_BuildValue("ssO", c_name, c_value, py_payload); + else + args = Py_BuildValue("ss", c_name, c_value); + + if (!(py_result = PyObject_CallObject(py_callback,args))) + return 0; + + if (!(c_result = PyLong_AsLong(py_result))) + return 0; + + return c_result; +} + +PyObject * +Config_foreach(Config *self, PyObject *args) +{ + int ret; + PyObject *py_callback; + PyObject *py_payload; + + + if (!PyArg_ParseTuple(args, "O|O", &py_callback, &py_payload)) + return NULL; + + + if (!PyCallable_Check(py_callback)) { + PyErr_SetString(PyExc_TypeError,"Argument 'callback' is not callable"); + return NULL; + } + + ret = git_config_foreach(self->config, Config_foreach_callback_wrapper, + (void *)args); + + return PyInt_FromLong((long)ret); +} + PyObject * Config_add_file(Config *self, PyObject *args) { @@ -212,6 +262,12 @@ PyMethodDef Config_methods[] = { {"get_global_config", (PyCFunction)Config_get_global_config, METH_NOARGS | METH_STATIC, "Return an object representing the global configuration file."}, + {"foreach", (PyCFunction)Config_foreach, METH_VARARGS, + "Perform an operation on each config variable.\n\n" + "The callback must be of type Callable and receives the normalized name " + "and value of each variable in the config backend, and an optional " + "payload passed to this method. As soon as one of the callbacks returns " + "an integer other than 0, this function returns that value."}, {"add_file", (PyCFunction)Config_add_file, METH_VARARGS, "Add a config file instance to an existing config."}, {NULL} diff --git a/test/test_config.py b/test/test_config.py index b9e6aaeee..328217ed8 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -38,6 +38,10 @@ config_filename = "test_config" +def foreach_test_wrapper(key, name, lst): + lst[key] = name + return 1 + class ConfigTest(utils.RepoTestCase): def test_config(self): @@ -135,6 +139,12 @@ def test_write(self): del config['core.dummy3'] self.assertFalse('core.dummy3' in config) + def test_foreach(self): + config = self.repo.config + lst = {} + config.foreach(foreach_test_wrapper, lst) + self.assertTrue('core.bare' in lst) + self.assertTrue(lst['core.bare']) if __name__ == '__main__': unittest.main() From 3df090c761533f429b320053823197dbe522f176 Mon Sep 17 00:00:00 2001 From: Martin Lenders Date: Fri, 8 Jun 2012 17:53:57 +0200 Subject: [PATCH 0206/2237] Implement getting and setting of multivars --- include/pygit2/config.h | 2 ++ src/pygit2/config.c | 65 +++++++++++++++++++++++++++++++++++++++++ test/test_config.py | 22 ++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/include/pygit2/config.h b/include/pygit2/config.h index 3ae9d4bff..bf3e59877 100644 --- a/include/pygit2/config.h +++ b/include/pygit2/config.h @@ -10,6 +10,8 @@ PyObject* Config_get_system_config(void); PyObject* Config_add_file(Config *self, PyObject *args); PyObject* Config_getitem(Config *self, PyObject *key); PyObject* Config_foreach(Config *self, PyObject *args); +PyObject* Config_get_multivar(Config *self, PyObject *args); +PyObject* Config_set_multivar(Config *self, PyObject *args); int Config_setitem(Config *self, PyObject *key, PyObject *value); #endif diff --git a/src/pygit2/config.c b/src/pygit2/config.c index ab18962ba..6183b50d1 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -255,6 +255,64 @@ Config_add_file(Config *self, PyObject *args) Py_RETURN_NONE; } +int +Config_get_multivar_fn_wrapper(const char *value, void *data) +{ + PyObject *list = (PyObject *)data; + PyObject *item = NULL; + + if (!(item = PyUnicode_FromString(value))) + return -2; + + PyList_Append(list, item); + + return 0; +} + +PyObject * +Config_get_multivar(Config *self, PyObject *args) +{ + int err; + PyObject *list = PyList_New(0); + const char *name = NULL; + const char *regex = NULL; + + if (!PyArg_ParseTuple(args, "s|s", &name, ®ex)) + return NULL; + + if ((err = git_config_get_multivar(self->config, name, regex, + Config_get_multivar_fn_wrapper, (void *)list)) < 0) { + if (err == GIT_ENOTFOUND) + Error_set(err); + else + PyErr_SetNone(PyExc_TypeError); + return NULL; + } + + return list; +} + +PyObject * +Config_set_multivar(Config *self, PyObject *args) +{ + int err; + const char *name; + const char *regex; + const char *value; + + if (!PyArg_ParseTuple(args, "sss", &name, ®ex, &value)) + return NULL; + if ((err = git_config_set_multivar(self->config, name, regex, value)) < 0) { + if (err == GIT_ENOTFOUND) + Error_set(err); + else + PyErr_SetNone(PyExc_TypeError); + return NULL; + } + + Py_RETURN_NONE; +} + PyMethodDef Config_methods[] = { {"get_system_config", (PyCFunction)Config_get_system_config, METH_NOARGS | METH_STATIC, @@ -270,6 +328,13 @@ PyMethodDef Config_methods[] = { "an integer other than 0, this function returns that value."}, {"add_file", (PyCFunction)Config_add_file, METH_VARARGS, "Add a config file instance to an existing config."}, + {"get_multivar", (PyCFunction)Config_get_multivar, METH_VARARGS, + "Get each value of a multivar ''name'' as a list. The optional ''regex'' " + "parameter is expected to be a regular expression to filter the which " + "variables we're interested in."}, + {"set_multivar", (PyCFunction)Config_set_multivar, METH_VARARGS, + "Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression " + "to indicate which values to replace"}, {NULL} }; diff --git a/test/test_config.py b/test/test_config.py index 328217ed8..c2ab641e3 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -111,6 +111,13 @@ def test_read(self): new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n") new_file.close() + config.add_file(config_filename, 0) + self.assertTrue('this.that' in config) + self.assertEqual(len(config.get_multivar('this.that')), 2) + l = config.get_multivar('this.that', 'bar') + self.assertEqual(len(l),1) + self.assertEqual(l[0], 'foobar') + def test_write(self): config = self.repo.config @@ -139,6 +146,21 @@ def test_write(self): del config['core.dummy3'] self.assertFalse('core.dummy3' in config) + new_file = open(config_filename, "w") + new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n") + new_file.close() + + config.add_file(config_filename, 0) + self.assertTrue('this.that' in config) + config.set_multivar('this.that', '^.*beer', 'fool') + l = config.get_multivar('this.that', 'fool') + self.assertEqual(len(l),1) + self.assertEqual(l[0], 'fool') + config.set_multivar('this.that', 'foo.*', '123456') + l = config.get_multivar('this.that', 'foo.*') + for i in l: + self.assertEqual(i, '123456') + def test_foreach(self): config = self.repo.config lst = {} From 87827a6d92b1cef36ce02d68f00211edb5dd7d06 Mon Sep 17 00:00:00 2001 From: Christian Boos Date: Sat, 9 Jun 2012 15:49:33 +0200 Subject: [PATCH 0207/2237] windows: detailed build instructions The default build of libgit2 with `__stdcall` calling convention is inadequate. We need to use a more standard `__cdecl` build, which can be obtained by specifying `-DSTDCALL=OFF` while configuring libgit2. Also mention the LIBGIT2 environment variable (2b4e5504). --- README.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.rst b/README.rst index d70d7cbe6..fb8b37c76 100644 --- a/README.rst +++ b/README.rst @@ -25,6 +25,28 @@ When those are installed, you can install pygit2:: $ python setup.py install $ python setup.py test +Building on Windows +------------------- + +pygit2 expects to find the libgit2 installed files in the directory specified +in the ``LIBGIT2`` environment variable. + +In addition, make sure that libgit2 is build in "__cdecl" mode. +The following recipe shows you how to do it, assuming you're working +from a bash shell:: + + $ export LIBGIT2=C:/Dev/libgit2 + $ git clone git://github.com/libgit2/libgit2.git + $ cd libgit2 + $ mkdir build + $ cd build + $ cmake .. -DSTDCALL=OFF -DCMAKE_INSTALL_PREFIX=$LIBGIT2 -G "Visual Studio 9 2008" + $ cmake --build . --config release --target install + $ ctest -v + +At this point, you're ready to execute the generic pygit2 installation +steps described above. + The repository ================= From e31089bf56ec773b65c7ff873d2403e79af86585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Veres-Szentkir=C3=A1lyi?= Date: Wed, 13 Jun 2012 11:20:46 +0200 Subject: [PATCH 0208/2237] documented `create_commit` in README --- README.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.rst b/README.rst index fb8b37c76..f8b7b4c52 100644 --- a/README.rst +++ b/README.rst @@ -142,6 +142,25 @@ This is their interface:: Signature.offset -- offset from utc in minutes +Creating commits +................ + +Commits can be created by calling the ``create_commit`` method of the +repository with the following parameters:: + + >>> from time import time + >>> author = Signature('Alice Author', 'alice@authors.tld', time(), 0) + >>> committer = Signature('Cecil Committer', 'cecil@committers.tld', time(), 0) + >>> tree = repo.TreeBuilder().write() + >>> repo.create_commit( + ... 'refs/heads/master', # the name of the reference to update + ... author, committer, 'one line commit message\n\ndetailed commit message', + ... tree, # binary string representing the tree object ID + ... [] # list of binary strings representing parents of the new commit + ... ) + '#\xe4 Date: Fri, 15 Jun 2012 14:14:22 +0200 Subject: [PATCH 0209/2237] Fixed bug in test_add. --- test/test_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_config.py b/test/test_config.py index c2ab641e3..28e1d56f1 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -76,7 +76,7 @@ def test_new(self): os.remove(config_filename) def test_add(self): - config = pygit2.Config.get_global_config() + config = pygit2.Config() new_file = open(config_filename, "w") new_file.write("[this]\n\tthat = true\n") From d2a27eddf19706b49331f72a6ad211901fc6a488 Mon Sep 17 00:00:00 2001 From: authmillenon Date: Fri, 15 Jun 2012 16:31:38 +0200 Subject: [PATCH 0210/2237] Initialize variables in Config_set_multivar with NULL --- src/pygit2/config.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pygit2/config.c b/src/pygit2/config.c index 6183b50d1..5226010ae 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -296,9 +296,9 @@ PyObject * Config_set_multivar(Config *self, PyObject *args) { int err; - const char *name; - const char *regex; - const char *value; + const char *name = NULL; + const char *regex = NULL; + const char *value = NULL; if (!PyArg_ParseTuple(args, "sss", &name, ®ex, &value)) return NULL; From 9ba2324535259e4c5a5b181ade4e32001330815d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 18 Jun 2012 11:08:46 -0300 Subject: [PATCH 0211/2237] Fix unit tests for Python 2.6 --- test/test_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_config.py b/test/test_config.py index 28e1d56f1..e5274e4d3 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -121,8 +121,8 @@ def test_read(self): def test_write(self): config = self.repo.config - with self.assertRaises(TypeError): - config[()] = 'This should not work' + self.assertRaises(TypeError, config.__setitem__, + (), 'This should not work') self.assertFalse('core.dummy1' in config) config['core.dummy1'] = 42 From d27cf0b687ec4c65fbad65ceaf45e0429943b415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 18 Jun 2012 11:22:56 -0300 Subject: [PATCH 0212/2237] style: remove trailing white-spaces --- .travis.yml | 2 +- setup.py | 2 +- src/pygit2/error.c | 4 ++-- src/pygit2/repository.c | 4 ++-- test/test_revwalk.py | 9 +++++---- test/test_tag.py | 2 +- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 84418b1f6..a1cdbec6d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,5 +12,5 @@ before_install: - sudo apt-get install cmake - "./.travis.sh" -script: +script: - python setup.py test diff --git a/setup.py b/setup.py index a20a2a512..6eaee92dc 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ class TestCommand(Command): """Command for running unittests without install.""" user_options = [("args=", None, '''The command args string passed to - unittest framework, such as + unittest framework, such as --args="-v -f"''')] def initialize_options(self): diff --git a/src/pygit2/error.c b/src/pygit2/error.c index fccf8a893..c8e0af5d0 100644 --- a/src/pygit2/error.c +++ b/src/pygit2/error.c @@ -56,9 +56,9 @@ PyObject* Error_set(int err) PyErr_SetNone(Error_type(err)); } else { //critical failure const git_error* error = giterr_last(); - char* message = (error == NULL) ? + char* message = (error == NULL) ? "(No error information given)" : error->message; - + PyErr_SetString(Error_type(err), message); } diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index d4165801f..4375964d4 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -165,7 +165,7 @@ Repository_head(Repository *self) git_reference *head; const git_oid *oid; int err; - + err = git_repository_head(&head, self->repo); if(err < 0) { if(err == GIT_ENOTFOUND) @@ -416,7 +416,7 @@ Repository_create_blob(Repository *self, PyObject *args) if (!PyArg_ParseTuple(args, "s#", &raw, &size)) return NULL; - + err = git_blob_create_frombuffer(&oid, self->repo, (const void*)raw, size); if (err < 0) diff --git a/test/test_revwalk.py b/test/test_revwalk.py index 47058ac63..8add64a21 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -59,10 +59,11 @@ class RevlogTestTest(utils.RepoTestCase): def test_log(self): - ref = self.repo.lookup_reference('HEAD') - for i,entry in enumerate(ref.log()): - self.assertEqual(entry.committer.name, REVLOGS[i][0]) - self.assertEqual(entry.message, REVLOGS[i][1]) + ref = self.repo.lookup_reference('HEAD') + for i,entry in enumerate(ref.log()): + self.assertEqual(entry.committer.name, REVLOGS[i][0]) + self.assertEqual(entry.message, REVLOGS[i][1]) + class WalkerTest(utils.RepoTestCase): diff --git a/test/test_tag.py b/test/test_tag.py index ca115693e..f60a43538 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -65,7 +65,7 @@ def test_new_tag(self): target_prefix = target[:5] too_short_prefix = target[:3] - self.assertRaises(ValueError, self.repo.create_tag, name, + self.assertRaises(ValueError, self.repo.create_tag, name, too_short_prefix, pygit2.GIT_OBJ_BLOB, tagger, message) sha = self.repo.create_tag(name, target_prefix, pygit2.GIT_OBJ_BLOB, From 1f354149a36b3bb0f3393f617e94e89e54e8102a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 18 Jun 2012 11:28:19 -0300 Subject: [PATCH 0213/2237] tests: don't leave test files behind --- test/test_config.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/test_config.py b/test/test_config.py index e5274e4d3..b05943a50 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -44,6 +44,12 @@ def foreach_test_wrapper(key, name, lst): class ConfigTest(utils.RepoTestCase): + def tearDown(self): + try: + os.remove(config_filename) + except OSError: + pass + def test_config(self): self.assertNotEqual(None, self.repo.config) @@ -73,8 +79,6 @@ def test_new(self): self.assertTrue('core.editor' in config_write) self.assertEqual(config_write['core.editor'], 'ed') - os.remove(config_filename) - def test_add(self): config = pygit2.Config() @@ -89,8 +93,6 @@ def test_add(self): self.assertTrue('something.other.here' in config) self.assertFalse(config['something.other.here']) - os.remove(config_filename) - def test_read(self): config = self.repo.config @@ -168,5 +170,6 @@ def test_foreach(self): self.assertTrue('core.bare' in lst) self.assertTrue(lst['core.bare']) + if __name__ == '__main__': unittest.main() From ec8047a8c58a7b28a7b62704add4b5abd5b61668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 18 Jun 2012 11:36:27 -0300 Subject: [PATCH 0214/2237] Fix errors detected by pyflakes --- test/test_config.py | 8 ++++---- test/test_diff.py | 1 - test/test_refs.py | 4 ++-- test/test_treebuilder.py | 6 +++--- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/test/test_config.py b/test/test_config.py index b05943a50..df05448be 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -74,10 +74,10 @@ def test_new(self): config_write['core.editor'] = 'ed' config_read = pygit2.Config(config_filename) - self.assertTrue('core.bare' in config_write) - self.assertFalse(config_write['core.bare']) - self.assertTrue('core.editor' in config_write) - self.assertEqual(config_write['core.editor'], 'ed') + self.assertTrue('core.bare' in config_read) + self.assertFalse(config_read['core.bare']) + self.assertTrue('core.editor' in config_read) + self.assertEqual(config_read['core.editor'], 'ed') def test_add(self): config = pygit2.Config() diff --git a/test/test_diff.py b/test/test_diff.py index 893cb8e6b..d18d8f441 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -31,7 +31,6 @@ from __future__ import unicode_literals import unittest -import pygit2 from . import utils __author__ = 'Nico.Geyso@FU-Berlin.de (Nico von Geyso)' diff --git a/test/test_refs.py b/test/test_refs.py index d3a737838..64b1e7744 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -51,8 +51,8 @@ def test_list_all_references(self): ['refs/heads/i18n', 'refs/heads/master']) # We add a symbolic reference - reference = repo.create_symbolic_reference('refs/tags/version1', - 'refs/heads/master') + repo.create_symbolic_reference('refs/tags/version1', + 'refs/heads/master') self.assertEqual(sorted(repo.listall_references()), ['refs/heads/i18n', 'refs/heads/master', 'refs/tags/version1']) diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index 455f4f4ac..6d5418f85 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -29,10 +29,8 @@ from __future__ import absolute_import from __future__ import unicode_literals -import os import unittest -import pygit2 from . import utils @@ -41,8 +39,9 @@ TREE_SHA = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' class TreeBuilderTest(utils.BareRepoTestCase): + def test_new_empty_treebuilder(self): - bld = self.repo.TreeBuilder() + self.repo.TreeBuilder() def test_noop_treebuilder(self): tree = self.repo[TREE_SHA] @@ -65,5 +64,6 @@ def test_rebuild_treebuilder(self): result = bld.write() self.assertEqual(tree.oid, result) + if __name__ == '__main__': unittest.main() From 7ec660a2522d60a949b25d81d2b3cf90969676fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 18 Jun 2012 12:07:30 -0300 Subject: [PATCH 0215/2237] Add .mailmap file --- .mailmap | 1 + 1 file changed, 1 insertion(+) create mode 100644 .mailmap diff --git a/.mailmap b/.mailmap new file mode 100644 index 000000000..bdcbdd2cf --- /dev/null +++ b/.mailmap @@ -0,0 +1 @@ +J. David Ibáñez From bcf0bbd46f2427987bf0dbae1c29216728e12280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 17 Jul 2012 15:44:37 +0200 Subject: [PATCH 0216/2237] Update FSF address (issue #114) --- COPYING | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/COPYING b/COPYING index c36f4cf1e..3de06906b 100644 --- a/COPYING +++ b/COPYING @@ -14,15 +14,15 @@ ---------------------------------------------------------------------- - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public @@ -31,7 +31,7 @@ software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to +the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not @@ -71,8 +71,8 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - - GNU GENERAL PUBLIC LICENSE + + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains @@ -126,7 +126,7 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -184,7 +184,7 @@ access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - + 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -241,7 +241,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - + 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -271,7 +271,7 @@ make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - NO WARRANTY + NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN @@ -293,9 +293,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it @@ -319,10 +319,9 @@ the "copyright" line and a pointer to where the full notice is found. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. @@ -352,5 +351,5 @@ necessary. Here is a sample; alter the names: This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General +library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. From c7a9d4ca42389131b8a9d59c6596376dc830a4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 18 Jul 2012 11:09:33 +0200 Subject: [PATCH 0217/2237] Add an AUTHORS file --- .mailmap | 1 + AUTHORS | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 AUTHORS diff --git a/.mailmap b/.mailmap index bdcbdd2cf..f566e646f 100644 --- a/.mailmap +++ b/.mailmap @@ -1 +1,2 @@ J. David Ibáñez +Martin Lenders diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..64fa00dba --- /dev/null +++ b/AUTHORS @@ -0,0 +1,30 @@ +The following people have contributed at least one patch to the +pygit2 project (sorted alphabetically): + +Amit Bakshi +András Veres-Szentkirályi +Benjamin Kircher +Bryan O'Sullivan +Carlos Martín Nieto +Christian Boos +Dave Borowitz +David Versmisse +Erik van Zijst +Han-Wen Nienhuys +Hugh Cole-Baker +J. David Ibáñez +Jared Flatow +John Szakmeister +Josh Bleecher Snyder +Julien Miotte +Martin Lenders +Nico von Geyso +Petr Hosek +Petr Viktorin +Rui Abreu Ferreira +Sarath Lakshman +Sebastian Thiel +Vicent Marti +Yonggang Luo +Zoran Zaric +pistacchio From 6195491596b42ff29e9afcfa78b1687a1de9b304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 18 Jul 2012 12:07:17 +0200 Subject: [PATCH 0218/2237] Add/update copyright header of each file And update my email address. --- README.rst | 2 +- include/pygit2/commit.h | 27 +++++++++++++++++++++++++++ include/pygit2/config.h | 27 +++++++++++++++++++++++++++ include/pygit2/diff.h | 27 +++++++++++++++++++++++++++ include/pygit2/error.h | 27 +++++++++++++++++++++++++++ include/pygit2/index.h | 27 +++++++++++++++++++++++++++ include/pygit2/object.h | 27 +++++++++++++++++++++++++++ include/pygit2/oid.h | 27 +++++++++++++++++++++++++++ include/pygit2/reference.h | 27 +++++++++++++++++++++++++++ include/pygit2/repository.h | 27 +++++++++++++++++++++++++++ include/pygit2/signature.h | 27 +++++++++++++++++++++++++++ include/pygit2/tag.h | 27 +++++++++++++++++++++++++++ include/pygit2/tree.h | 27 +++++++++++++++++++++++++++ include/pygit2/types.h | 27 +++++++++++++++++++++++++++ include/pygit2/utils.h | 27 +++++++++++++++++++++++++++ include/pygit2/walker.h | 27 +++++++++++++++++++++++++++ pygit2/__init__.py | 2 +- pygit2/utils.py | 2 +- setup.py | 3 +-- src/pygit2.c | 3 +-- src/pygit2/blob.c | 27 +++++++++++++++++++++++++++ src/pygit2/commit.c | 27 +++++++++++++++++++++++++++ src/pygit2/config.c | 27 +++++++++++++++++++++++++++ src/pygit2/diff.c | 27 +++++++++++++++++++++++++++ src/pygit2/error.c | 27 +++++++++++++++++++++++++++ src/pygit2/index.c | 27 +++++++++++++++++++++++++++ src/pygit2/object.c | 27 +++++++++++++++++++++++++++ src/pygit2/oid.c | 27 +++++++++++++++++++++++++++ src/pygit2/reference.c | 27 +++++++++++++++++++++++++++ src/pygit2/repository.c | 27 +++++++++++++++++++++++++++ src/pygit2/signature.c | 27 +++++++++++++++++++++++++++ src/pygit2/tag.c | 27 +++++++++++++++++++++++++++ src/pygit2/tree.c | 27 +++++++++++++++++++++++++++ src/pygit2/utils.c | 27 +++++++++++++++++++++++++++ src/pygit2/walker.c | 27 +++++++++++++++++++++++++++ test/__init__.py | 2 +- test/test_blob.py | 2 +- test/test_commit.py | 2 +- test/test_config.py | 2 +- test/test_diff.py | 2 +- test/test_index.py | 4 ++-- test/test_refs.py | 2 +- test/test_repository.py | 2 +- test/test_revwalk.py | 4 ++-- test/test_signature.py | 2 +- test/test_status.py | 2 +- test/test_tag.py | 2 +- test/test_tree.py | 2 +- test/test_treebuilder.py | 2 +- test/utils.py | 2 +- 50 files changed, 832 insertions(+), 24 deletions(-) diff --git a/README.rst b/README.rst index f8b7b4c52..a958465a0 100644 --- a/README.rst +++ b/README.rst @@ -330,7 +330,7 @@ AUTHORS ============== * David Borowitz -* J David Ibáñez +* J David Ibáñez LICENSE diff --git a/include/pygit2/commit.h b/include/pygit2/commit.h index 7930122c3..b9b907034 100644 --- a/include/pygit2/commit.h +++ b/include/pygit2/commit.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_commit_h #define INCLUDE_pygit2_commit_h diff --git a/include/pygit2/config.h b/include/pygit2/config.h index bf3e59877..b07849e2b 100644 --- a/include/pygit2/config.h +++ b/include/pygit2/config.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_config_h #define INCLUDE_pygit2_config_h diff --git a/include/pygit2/diff.h b/include/pygit2/diff.h index 1f1b793d1..7f1d0be4d 100644 --- a/include/pygit2/diff.h +++ b/include/pygit2/diff.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_diff_h #define INCLUDE_pygit2_diff_h diff --git a/include/pygit2/error.h b/include/pygit2/error.h index 587eb8978..38df75eae 100644 --- a/include/pygit2/error.h +++ b/include/pygit2/error.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_error_h #define INCLUDE_pygit2_error_h diff --git a/include/pygit2/index.h b/include/pygit2/index.h index 6f30d4f94..2082cc777 100644 --- a/include/pygit2/index.h +++ b/include/pygit2/index.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_index_h #define INCLUDE_pygit2_index_h diff --git a/include/pygit2/object.h b/include/pygit2/object.h index aeba9d354..5255996ba 100644 --- a/include/pygit2/object.h +++ b/include/pygit2/object.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_object_h #define INCLUDE_pygit2_object_h diff --git a/include/pygit2/oid.h b/include/pygit2/oid.h index 9d6845946..66def6974 100644 --- a/include/pygit2/oid.h +++ b/include/pygit2/oid.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_oid_h #define INCLUDE_pygit2_oid_h diff --git a/include/pygit2/reference.h b/include/pygit2/reference.h index a0d692593..40e7e53cc 100644 --- a/include/pygit2/reference.h +++ b/include/pygit2/reference.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_reference_h #define INCLUDE_pygit2_reference_h diff --git a/include/pygit2/repository.h b/include/pygit2/repository.h index 8ea07207b..10895ab23 100644 --- a/include/pygit2/repository.h +++ b/include/pygit2/repository.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_repository_h #define INCLUDE_pygit2_repository_h diff --git a/include/pygit2/signature.h b/include/pygit2/signature.h index 67bf003ab..10fbe7e9b 100644 --- a/include/pygit2/signature.h +++ b/include/pygit2/signature.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_signature_h #define INCLUDE_pygit2_signature_h diff --git a/include/pygit2/tag.h b/include/pygit2/tag.h index 8c13903a6..8caa6eeb5 100644 --- a/include/pygit2/tag.h +++ b/include/pygit2/tag.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_tag_h #define INCLUDE_pygit2_tag_h diff --git a/include/pygit2/tree.h b/include/pygit2/tree.h index d4af1956b..e4b086faf 100644 --- a/include/pygit2/tree.h +++ b/include/pygit2/tree.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_tree_h #define INCLUDE_pygit2_tree_h diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 10cab836a..3c42ef68a 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_objects_h #define INCLUDE_pygit2_objects_h diff --git a/include/pygit2/utils.h b/include/pygit2/utils.h index ee6e7b02f..b7fd1b93f 100644 --- a/include/pygit2/utils.h +++ b/include/pygit2/utils.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_utils_h #define INCLUDE_pygit2_utils_h diff --git a/include/pygit2/walker.h b/include/pygit2/walker.h index 0c6837367..82024c711 100644 --- a/include/pygit2/walker.h +++ b/include/pygit2/walker.h @@ -1,3 +1,30 @@ +/* + * 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. + */ + #ifndef INCLUDE_pygit2_walker_h #define INCLUDE_pygit2_walker_h diff --git a/pygit2/__init__.py b/pygit2/__init__.py index cd219c836..151fcafcf 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2012 Nico von Geyso +# 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, diff --git a/pygit2/utils.py b/pygit2/utils.py index 8ebf533cd..d1909b5f4 100644 --- a/pygit2/utils.py +++ b/pygit2/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2012 Nico von Geyso +# 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, diff --git a/setup.py b/setup.py index 6eaee92dc..7c2c32a6f 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,7 @@ # -*- coding: UTF-8 -*- # coding: UTF-8 # -# Copyright 2010 Google, Inc. -# Copyright 2011 Itaapy +# 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, diff --git a/src/pygit2.c b/src/pygit2.c index 8ecc0d2f8..e4eea03a8 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -1,6 +1,5 @@ /* - * Copyright 2010 Google, Inc. - * Copyright 2011 Itaapy + * 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, diff --git a/src/pygit2/blob.c b/src/pygit2/blob.c index 2a9d123b0..c69c103f3 100644 --- a/src/pygit2/blob.c +++ b/src/pygit2/blob.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/src/pygit2/commit.c b/src/pygit2/commit.c index 15d9ae650..6abc92a3e 100644 --- a/src/pygit2/commit.c +++ b/src/pygit2/commit.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/src/pygit2/config.c b/src/pygit2/config.c index 5226010ae..d850ba8bc 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index 777ba766d..1c0956e24 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/src/pygit2/error.c b/src/pygit2/error.c index c8e0af5d0..11129a3a7 100644 --- a/src/pygit2/error.c +++ b/src/pygit2/error.c @@ -1,3 +1,30 @@ +/* + * 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. + */ + #include PyObject *GitError; diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 8b600d74f..7dec03090 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/src/pygit2/object.c b/src/pygit2/object.c index cf48483ec..12a085580 100644 --- a/src/pygit2/object.c +++ b/src/pygit2/object.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/src/pygit2/oid.c b/src/pygit2/oid.c index 6ac86aab0..e5641da8d 100644 --- a/src/pygit2/oid.c +++ b/src/pygit2/oid.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/src/pygit2/reference.c b/src/pygit2/reference.c index 60da959ce..75c937e9d 100644 --- a/src/pygit2/reference.c +++ b/src/pygit2/reference.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 4375964d4..6bac3b54a 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/src/pygit2/signature.c b/src/pygit2/signature.c index 666dd3ba1..d2ea1ce83 100644 --- a/src/pygit2/signature.c +++ b/src/pygit2/signature.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/src/pygit2/tag.c b/src/pygit2/tag.c index fb739db58..c34dbc004 100644 --- a/src/pygit2/tag.c +++ b/src/pygit2/tag.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index 08f841b8a..827b1c009 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/src/pygit2/utils.c b/src/pygit2/utils.c index d4dd39977..9aa46ef9c 100644 --- a/src/pygit2/utils.c +++ b/src/pygit2/utils.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/src/pygit2/walker.c b/src/pygit2/walker.c index 6a0bb2bfd..878b506ea 100644 --- a/src/pygit2/walker.c +++ b/src/pygit2/walker.c @@ -1,3 +1,30 @@ +/* + * 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 diff --git a/test/__init__.py b/test/__init__.py index 2cf6bb991..eb9312750 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010 Google, Inc. +# 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, diff --git a/test/test_blob.py b/test/test_blob.py index 86608cca6..88e107046 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010 Google, Inc. +# 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, diff --git a/test/test_commit.py b/test/test_commit.py index 526d7d1a8..8d6f2d7fa 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010 Google, Inc. +# 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, diff --git a/test/test_config.py b/test/test_config.py index df05448be..257846974 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2012 elego +# 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, diff --git a/test/test_diff.py b/test/test_diff.py index d18d8f441..b6a33477c 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2012 Nico von Geyso +# 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, diff --git a/test/test_index.py b/test/test_index.py index 39fb01538..6524efdfb 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2011 Itaapy +# 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, @@ -36,7 +36,7 @@ from . import utils -__author__ = 'jdavid@itaapy.com (J. David Ibáñez)' +__author__ = 'jdavid.ibp@gmail.com (J. David Ibáñez)' class IndexBareTest(utils.BareRepoTestCase): diff --git a/test/test_refs.py b/test/test_refs.py index 64b1e7744..59e29b253 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2011 Itaapy +# 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, diff --git a/test/test_repository.py b/test/test_repository.py index 3ca7eedd0..8f056cca8 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010 Google, Inc. +# 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, diff --git a/test/test_revwalk.py b/test/test_revwalk.py index 8add64a21..65fe25452 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2011 Itaapy +# 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, @@ -35,7 +35,7 @@ from . import utils -__author__ = 'jdavid@itaapy.com (J. David Ibáñez)' +__author__ = 'jdavid.ibp@gmail.com (J. David Ibáñez)' # In the order given by git log diff --git a/test/test_signature.py b/test/test_signature.py index fb9d89185..2ad73486e 100644 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2011 J. David Ibáñez +# 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, diff --git a/test/test_status.py b/test/test_status.py index 39d646259..3eb6b5688 100644 --- a/test/test_status.py +++ b/test/test_status.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2011 Julien Miotte +# 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, diff --git a/test/test_tag.py b/test/test_tag.py index f60a43538..caa28150e 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010 Google, Inc. +# 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, diff --git a/test/test_tree.py b/test/test_tree.py index c9f41ccd2..f45928aeb 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010 Google, Inc. +# 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, diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index 6d5418f85..9dba68a7d 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2012 Carlos Martín Nieto +# 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, diff --git a/test/utils.py b/test/utils.py index 8219bebd5..356a27556 100644 --- a/test/utils.py +++ b/test/utils.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010 Google, Inc. +# 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, From edcd803e7e4757cf18e17b7b5e78ca4cc1742ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 18 Jul 2012 12:12:04 +0200 Subject: [PATCH 0219/2237] Remove the __authors__ var from py files --- test/test_blob.py | 2 -- test/test_commit.py | 2 -- test/test_config.py | 2 -- test/test_diff.py | 2 -- test/test_index.py | 3 --- test/test_refs.py | 2 -- test/test_repository.py | 2 -- test/test_revwalk.py | 3 --- test/test_signature.py | 4 ---- test/test_status.py | 2 -- test/test_tag.py | 2 -- test/test_tree.py | 2 -- test/test_treebuilder.py | 2 -- test/utils.py | 2 -- 14 files changed, 32 deletions(-) diff --git a/test/test_blob.py b/test/test_blob.py index 88e107046..c7f5cf6b7 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -35,8 +35,6 @@ from . import utils -__author__ = 'dborowitz@google.com (Dave Borowitz)' - BLOB_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' BLOB_NEW_CONTENT = b'foo bar\n' diff --git a/test/test_commit.py b/test/test_commit.py index 8d6f2d7fa..0884364d2 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -35,8 +35,6 @@ from . import utils -__author__ = 'dborowitz@google.com (Dave Borowitz)' - COMMIT_SHA = '5fe808e8953c12735680c257f56600cb0de44b10' diff --git a/test/test_config.py b/test/test_config.py index 257846974..5403bc3b1 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -34,8 +34,6 @@ from . import utils -__author__ = 'mlenders@elegosoft.com (M. Lenders)' - config_filename = "test_config" def foreach_test_wrapper(key, name, lst): diff --git a/test/test_diff.py b/test/test_diff.py index b6a33477c..ddfe39863 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -33,8 +33,6 @@ from . import utils -__author__ = 'Nico.Geyso@FU-Berlin.de (Nico von Geyso)' - COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10' COMMIT_SHA1_2 = 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c' diff --git a/test/test_index.py b/test/test_index.py index 6524efdfb..da7784063 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -36,9 +36,6 @@ from . import utils -__author__ = 'jdavid.ibp@gmail.com (J. David Ibáñez)' - - class IndexBareTest(utils.BareRepoTestCase): def test_bare(self): diff --git a/test/test_refs.py b/test/test_refs.py index 59e29b253..ca582c2e9 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -35,8 +35,6 @@ from . import utils -__author__ = 'david.versmisse@itaapy.com (David Versmisse)' - LAST_COMMIT = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' diff --git a/test/test_repository.py b/test/test_repository.py index 8f056cca8..27a674acb 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -39,8 +39,6 @@ from . import utils -__author__ = 'dborowitz@google.com (Dave Borowitz)' - HEAD_SHA = '5fe808e8953c12735680c257f56600cb0de44b10' A_HEX_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' A_BIN_SHA = binascii.unhexlify(A_HEX_SHA.encode('ascii')) diff --git a/test/test_revwalk.py b/test/test_revwalk.py index 65fe25452..f7e56c66a 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -35,9 +35,6 @@ from . import utils -__author__ = 'jdavid.ibp@gmail.com (J. David Ibáñez)' - - # In the order given by git log log = [ '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98', diff --git a/test/test_signature.py b/test/test_signature.py index 2ad73486e..1ee7e2bd5 100644 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -33,10 +33,6 @@ from .utils import NoRepoTestCase -__author__ = 'jdavid.ibp@gmail.com (J. David Ibáñez)' - - - class SignatureTest(NoRepoTestCase): def test_default(self): diff --git a/test/test_status.py b/test/test_status.py index 3eb6b5688..e6780a482 100644 --- a/test/test_status.py +++ b/test/test_status.py @@ -35,8 +35,6 @@ from . import utils -__author__ = 'mike.perdide@gmail.com (Julien Miotte)' - EXPECTED = { "current_file": pygit2.GIT_STATUS_CURRENT, "file_deleted": pygit2.GIT_STATUS_WT_DELETED, diff --git a/test/test_tag.py b/test/test_tag.py index caa28150e..884784556 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -35,8 +35,6 @@ from . import utils -__author__ = 'dborowitz@google.com (Dave Borowitz)' - TAG_SHA = '3d2962987c695a29f1f80b6c3aa4ec046ef44369' diff --git a/test/test_tree.py b/test/test_tree.py index f45928aeb..4ec22a998 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -36,8 +36,6 @@ from . import utils -__author__ = 'dborowitz@google.com (Dave Borowitz)' - TREE_SHA = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' SUBTREE_SHA = '614fd9a3094bf618ea938fffc00e7d1a54f89ad0' diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index 9dba68a7d..8dbea7724 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -34,8 +34,6 @@ from . import utils -__author__ = 'carlos@cmartin.tk (Carlos Martín Nieto)' - TREE_SHA = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' class TreeBuilderTest(utils.BareRepoTestCase): diff --git a/test/utils.py b/test/utils.py index 356a27556..56f84ea80 100644 --- a/test/utils.py +++ b/test/utils.py @@ -39,8 +39,6 @@ import pygit2 -__author__ = 'dborowitz@google.com (Dave Borowitz)' - def force_rm_handle(remove_path, path, excinfo): os.chmod( path, From 0285470f45d43e0080b32e50bb2025aa430db14a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 18 Jul 2012 12:21:24 +0200 Subject: [PATCH 0220/2237] Add the list of authors to the readme file --- README.rst | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index a958465a0..d81c0b2bf 100644 --- a/README.rst +++ b/README.rst @@ -329,8 +329,36 @@ See issues AUTHORS ============== -* David Borowitz -* J David Ibáñez +The following people have contributed at least one patch to the +pygit2 project (sorted alphabetically): + +- Amit Bakshi +- András Veres-Szentkirályi +- Benjamin Kircher +- Bryan O'Sullivan +- Carlos Martín Nieto +- Christian Boos +- David Borowitz (*Original author*) +- David Versmisse +- Erik van Zijst +- Han-Wen Nienhuys +- Hugh Cole-Baker +- J David Ibáñez (*Current maintainer*) +- Jared Flatow +- John Szakmeister +- Josh Bleecher Snyder +- Julien Miotte +- Martin Lenders +- Nico von Geyso +- Petr Hosek +- Petr Viktorin +- Rui Abreu Ferreira +- Sarath Lakshman +- Sebastian Thiel +- Vicent Marti +- Yonggang Luo +- Zoran Zaric +- pistacchio LICENSE From a1716a3a923faa5d955529a8d1f509c7f7c4d1b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 18 Jul 2012 12:24:09 +0200 Subject: [PATCH 0221/2237] Release v0.17.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New features: - Support for diffs - Support for reflog - Support for config files - Add shortcut 'Repository.head' Other changes: - Improved support for Windows - Improved documentation - Refactoring: split the code into smaller files - Refactoring: allow to add helper code written in Python - Use Travis Continous-Integration service http://travis-ci.org/#!/libgit2/pygit2 Thanks to András Veres-Szentkirályi, Christian Boos, Martin Lenders, Nico von Geyso, Petr Hosek and pistacchio. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7c2c32a6f..719d2a538 100644 --- a/setup.py +++ b/setup.py @@ -134,7 +134,7 @@ def run(self): setup(name='pygit2', description='Python bindings for libgit2.', keywords='git', - version='0.17.0', + version='0.17.1', url='http://github.com/libgit2/pygit2', classifiers=classifiers, license='GPLv2', From 5f4d98d7dce0acd0f9cfc37ca1bfe938c7d91ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 23 Jul 2012 17:21:55 +0200 Subject: [PATCH 0222/2237] Fix the manifest file (issue #115) The include directory was missing, so pygit2 could not be built. Test data files were also missing. The current fix is a hack, just generated the MANIFEST file using "git ls-files", excluding hidden files, and committed that. For easiest maintenance, the MANIFEST file should be generated by setup.py using "git ls-files". --- MANIFEST | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++ MANIFEST.in | 1 - TODO.txt | 1 + 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 MANIFEST delete mode 100644 MANIFEST.in diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 000000000..4f992610a --- /dev/null +++ b/MANIFEST @@ -0,0 +1,75 @@ +AUTHORS +COPYING +README.rst +TODO.txt +include/pygit2/commit.h +include/pygit2/config.h +include/pygit2/diff.h +include/pygit2/error.h +include/pygit2/index.h +include/pygit2/object.h +include/pygit2/oid.h +include/pygit2/reference.h +include/pygit2/repository.h +include/pygit2/signature.h +include/pygit2/tag.h +include/pygit2/tree.h +include/pygit2/types.h +include/pygit2/utils.h +include/pygit2/walker.h +pygit2/__init__.py +pygit2/utils.py +setup.py +src/pygit2.c +src/pygit2/blob.c +src/pygit2/commit.c +src/pygit2/config.c +src/pygit2/diff.c +src/pygit2/error.c +src/pygit2/index.c +src/pygit2/object.c +src/pygit2/oid.c +src/pygit2/reference.c +src/pygit2/repository.c +src/pygit2/signature.c +src/pygit2/tag.c +src/pygit2/tree.c +src/pygit2/utils.c +src/pygit2/walker.c +test/__init__.py +test/data/dirtyrepo.tar +test/data/testrepo.git/HEAD +test/data/testrepo.git/config +test/data/testrepo.git/objects/29/7efb891a47de80be0cfe9c639e4b8c9b450989 +test/data/testrepo.git/objects/2a/d1d3456c5c4a1c9e40aeeddb9cd20b409623c8 +test/data/testrepo.git/objects/2c/dae28389c059815e951d0bb9eed6533f61a46b +test/data/testrepo.git/objects/39/a3001fcc2b9541fdcf4be2d662618a5d213f47 +test/data/testrepo.git/objects/3d/2962987c695a29f1f80b6c3aa4ec046ef44369 +test/data/testrepo.git/objects/5f/e808e8953c12735680c257f56600cb0de44b10 +test/data/testrepo.git/objects/61/4fd9a3094bf618ea938fffc00e7d1a54f89ad0 +test/data/testrepo.git/objects/6a/270c81bc80b59591e0d2e3abd7d03450c0c395 +test/data/testrepo.git/objects/72/abb8755b2cc6c4e40fd9f50f54384d973a2f22 +test/data/testrepo.git/objects/7f/129fd57e31e935c6d60a0c794efe4e6927664b +test/data/testrepo.git/objects/96/7fce8df97cc71722d3c2a5930ef3e6f1d27b12 +test/data/testrepo.git/objects/97/d615e1bc273c40c94a726814e7b93fdb5a1b36 +test/data/testrepo.git/objects/info/packs +test/data/testrepo.git/objects/pack/pack-822653eb59791a6df714f8aa5fbf9f1c1951478e.idx +test/data/testrepo.git/objects/pack/pack-822653eb59791a6df714f8aa5fbf9f1c1951478e.pack +test/data/testrepo.git/packed-refs +test/data/testrepo.git/refs/heads/master +test/data/testrepo.git/refs/tags/root +test/data/testrepo.tar +test/test_blob.py +test/test_commit.py +test/test_config.py +test/test_diff.py +test/test_index.py +test/test_refs.py +test/test_repository.py +test/test_revwalk.py +test/test_signature.py +test/test_status.py +test/test_tag.py +test/test_tree.py +test/test_treebuilder.py +test/utils.py diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index bb37a2723..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -include *.rst diff --git a/TODO.txt b/TODO.txt index 4a48a284c..6b8cffa1b 100644 --- a/TODO.txt +++ b/TODO.txt @@ -18,3 +18,4 @@ Other PyObject_Del or similar) if the type is subclassable. So, go through the code and switch to tp_free, or make the type not subclassable, on a case by case basis. +- Automatically generate the MANIFEST file using "git ls-files" From 06969ba8745bb16b8e97f4eed1fe4480bd83a503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 24 Jul 2012 12:28:56 +0200 Subject: [PATCH 0223/2237] Generate the MANIFEST file from git --- .gitignore | 1 + MANIFEST | 75 ------------------------------------------------------ TODO.txt | 1 - setup.py | 31 +++++++++++++++++++++- 4 files changed, 31 insertions(+), 77 deletions(-) delete mode 100644 MANIFEST diff --git a/.gitignore b/.gitignore index c1631efa8..5111fd0ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +MANIFEST build dist pygit2.so diff --git a/MANIFEST b/MANIFEST deleted file mode 100644 index 4f992610a..000000000 --- a/MANIFEST +++ /dev/null @@ -1,75 +0,0 @@ -AUTHORS -COPYING -README.rst -TODO.txt -include/pygit2/commit.h -include/pygit2/config.h -include/pygit2/diff.h -include/pygit2/error.h -include/pygit2/index.h -include/pygit2/object.h -include/pygit2/oid.h -include/pygit2/reference.h -include/pygit2/repository.h -include/pygit2/signature.h -include/pygit2/tag.h -include/pygit2/tree.h -include/pygit2/types.h -include/pygit2/utils.h -include/pygit2/walker.h -pygit2/__init__.py -pygit2/utils.py -setup.py -src/pygit2.c -src/pygit2/blob.c -src/pygit2/commit.c -src/pygit2/config.c -src/pygit2/diff.c -src/pygit2/error.c -src/pygit2/index.c -src/pygit2/object.c -src/pygit2/oid.c -src/pygit2/reference.c -src/pygit2/repository.c -src/pygit2/signature.c -src/pygit2/tag.c -src/pygit2/tree.c -src/pygit2/utils.c -src/pygit2/walker.c -test/__init__.py -test/data/dirtyrepo.tar -test/data/testrepo.git/HEAD -test/data/testrepo.git/config -test/data/testrepo.git/objects/29/7efb891a47de80be0cfe9c639e4b8c9b450989 -test/data/testrepo.git/objects/2a/d1d3456c5c4a1c9e40aeeddb9cd20b409623c8 -test/data/testrepo.git/objects/2c/dae28389c059815e951d0bb9eed6533f61a46b -test/data/testrepo.git/objects/39/a3001fcc2b9541fdcf4be2d662618a5d213f47 -test/data/testrepo.git/objects/3d/2962987c695a29f1f80b6c3aa4ec046ef44369 -test/data/testrepo.git/objects/5f/e808e8953c12735680c257f56600cb0de44b10 -test/data/testrepo.git/objects/61/4fd9a3094bf618ea938fffc00e7d1a54f89ad0 -test/data/testrepo.git/objects/6a/270c81bc80b59591e0d2e3abd7d03450c0c395 -test/data/testrepo.git/objects/72/abb8755b2cc6c4e40fd9f50f54384d973a2f22 -test/data/testrepo.git/objects/7f/129fd57e31e935c6d60a0c794efe4e6927664b -test/data/testrepo.git/objects/96/7fce8df97cc71722d3c2a5930ef3e6f1d27b12 -test/data/testrepo.git/objects/97/d615e1bc273c40c94a726814e7b93fdb5a1b36 -test/data/testrepo.git/objects/info/packs -test/data/testrepo.git/objects/pack/pack-822653eb59791a6df714f8aa5fbf9f1c1951478e.idx -test/data/testrepo.git/objects/pack/pack-822653eb59791a6df714f8aa5fbf9f1c1951478e.pack -test/data/testrepo.git/packed-refs -test/data/testrepo.git/refs/heads/master -test/data/testrepo.git/refs/tags/root -test/data/testrepo.tar -test/test_blob.py -test/test_commit.py -test/test_config.py -test/test_diff.py -test/test_index.py -test/test_refs.py -test/test_repository.py -test/test_revwalk.py -test/test_signature.py -test/test_status.py -test/test_tag.py -test/test_tree.py -test/test_treebuilder.py -test/utils.py diff --git a/TODO.txt b/TODO.txt index 6b8cffa1b..4a48a284c 100644 --- a/TODO.txt +++ b/TODO.txt @@ -18,4 +18,3 @@ Other PyObject_Del or similar) if the type is subclassable. So, go through the code and switch to tp_free, or make the type not subclassable, on a case by case basis. -- Automatically generate the MANIFEST file using "git ls-files" diff --git a/setup.py b/setup.py index 719d2a538..6fa1c5bbd 100644 --- a/setup.py +++ b/setup.py @@ -28,12 +28,17 @@ """Setup file for pygit2.""" +from __future__ import print_function + import os +from subprocess import Popen, PIPE import sys from distutils.core import setup, Extension, Command from distutils.command.build import build +from distutils.command.sdist import sdist from distutils import log + # Use environment variable LIBGIT2 to set your own libgit2 configuration. libgit2_path = os.getenv("LIBGIT2") if libgit2_path is None: @@ -117,7 +122,31 @@ def run(self): self.copy_file(s, d) -cmdclass = {'test': TestCommand} +class sdist_files_from_git(sdist): + def get_file_list(self): + popen = Popen(['git', 'ls-files'], stdout=PIPE, stderr=PIPE) + stdoutdata, stderrdata = popen.communicate() + if popen.returncode != 0: + print(stderrdata) + sys.exit() + + for line in stdoutdata.splitlines(): + # Skip hidden files at the root + if line[0] == '.': + continue + self.filelist.append(line) + + # Ok + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() + + + +cmdclass = { + 'test': TestCommand, + 'sdist': sdist_files_from_git} + if os.name == 'nt': # BuildWithDLLs can copy external DLLs into source directory. cmdclass['build'] = BuildWithDLLs From 0821e7bf00c97f6a627dd1dae01d33019348c050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 24 Jul 2012 12:40:26 +0200 Subject: [PATCH 0224/2237] Release v0.17.2 Now the MANIFEST file is automatically built from Git. This fixes the source distribution. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6fa1c5bbd..4722427ed 100644 --- a/setup.py +++ b/setup.py @@ -163,7 +163,7 @@ def get_file_list(self): setup(name='pygit2', description='Python bindings for libgit2.', keywords='git', - version='0.17.1', + version='0.17.2', url='http://github.com/libgit2/pygit2', classifiers=classifiers, license='GPLv2', From 398e717ec6fca32f02e21b3694516db39ec648f0 Mon Sep 17 00:00:00 2001 From: Ben Davis Date: Fri, 3 Aug 2012 01:09:45 -0500 Subject: [PATCH 0225/2237] Implemented git_tree_entry_bypath within Tree.__getitem__ --- src/pygit2/tree.c | 11 ++++++++++- test/test_tree.py | 3 +++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index 827b1c009..d6cc580d5 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -26,6 +26,7 @@ */ #define PY_SSIZE_T_CLEAN +#include #include #include #include @@ -247,7 +248,15 @@ Tree_getitem(Tree *self, PyObject *value) name = py_path_to_c_str(value); if (name == NULL) return NULL; - entry = git_tree_entry_byname(self->tree, name); + + if (strchr(name, '/') != NULL) { + /* Case 2a: path string */ + git_tree_entry_bypath(&entry, self->tree, name); + } else { + /* Case 2b: base name */ + entry = git_tree_entry_byname(self->tree, name); + } + free(name); if (!entry) { PyErr_SetObject(PyExc_KeyError, value); diff --git a/test/test_tree.py b/test/test_tree.py index 4ec22a998..0f70644fc 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -68,6 +68,9 @@ def test_read_tree(self): self.assertTreeEntryEqual(tree[-2], sha, 'b', 0o0100644) self.assertTreeEntryEqual(tree['b'], sha, 'b', 0o0100644) + sha = '297efb891a47de80be0cfe9c639e4b8c9b450989' + self.assertTreeEntryEqual(tree['c/d'], sha, 'd', 0o0100644) + def test_read_subtree(self): tree = self.repo[TREE_SHA] subtree_entry = tree['c'] From 578054b97b2e50a489b43dc28e827d702e0c87dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 6 Aug 2012 17:54:26 +0200 Subject: [PATCH 0226/2237] config test: the loop should return 0 Returning anything else makes the loop stop, which stops us from getting to 'core.bare' which the test is looking for. --- test/test_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_config.py b/test/test_config.py index 5403bc3b1..c2bd9f21a 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -38,7 +38,7 @@ def foreach_test_wrapper(key, name, lst): lst[key] = name - return 1 + return 0 class ConfigTest(utils.RepoTestCase): From f94fe00673772c05203bc0b86b15ed68e8f46731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 6 Aug 2012 18:09:10 +0200 Subject: [PATCH 0227/2237] travis: do a shallow clone of libgit2 and don't build the tests This just adds time to the tests. We're not testing the library, but the bindings, so do a shallow clone and stop cmake from building clar. --- .travis.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.sh b/.travis.sh index 16a88d19f..26985cf4c 100755 --- a/.travis.sh +++ b/.travis.sh @@ -2,11 +2,11 @@ cd ~ -git clone -b master https://github.com/libgit2/libgit2.git +git clone --depth=1 -b master https://github.com/libgit2/libgit2.git cd libgit2/ mkdir build && cd build -cmake .. -DCMAKE_INSTALL_PREFIX=../_install +cmake .. -DCMAKE_INSTALL_PREFIX=../_install -DBUILD_CLAR=OFF cmake --build . --target install ls -la .. From 689412d0a28c7203bc6d5cf8639cb61c3b95e8ae Mon Sep 17 00:00:00 2001 From: Ben Davis Date: Sat, 11 Aug 2012 16:24:30 -0500 Subject: [PATCH 0228/2237] Added error checking when calling git_tree_entry_bypath --- src/pygit2/tree.c | 9 ++++++++- test/test_tree.py | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index d6cc580d5..0cf929965 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -239,6 +239,7 @@ Tree_getitem(Tree *self, PyObject *value) { char *name; const git_tree_entry *entry; + int err; /* Case 1: integer */ if (PyInt_Check(value)) @@ -251,13 +252,19 @@ Tree_getitem(Tree *self, PyObject *value) if (strchr(name, '/') != NULL) { /* Case 2a: path string */ - git_tree_entry_bypath(&entry, self->tree, name); + err = git_tree_entry_bypath(&entry, self->tree, name); + if (err == GIT_ENOTFOUND) + entry = NULL; + else if (err < 0) + return Error_set(err); + } else { /* Case 2b: base name */ entry = git_tree_entry_byname(self->tree, name); } free(name); + if (!entry) { PyErr_SetObject(PyExc_KeyError, value); return NULL; diff --git a/test/test_tree.py b/test/test_tree.py index 0f70644fc..44e85c603 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -70,6 +70,7 @@ def test_read_tree(self): sha = '297efb891a47de80be0cfe9c639e4b8c9b450989' self.assertTreeEntryEqual(tree['c/d'], sha, 'd', 0o0100644) + self.assertRaisesWithArg(KeyError, 'ab/cd', lambda: tree['ab/cd']) def test_read_subtree(self): tree = self.repo[TREE_SHA] From db35bda2195401d93ebbb668384feb24ea66ad3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 15 Aug 2012 19:54:59 +0200 Subject: [PATCH 0229/2237] Run the diff tests --- test/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/__init__.py b/test/__init__.py index eb9312750..21ea01481 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -36,7 +36,7 @@ names = ['blob', 'commit', 'config', 'index', 'refs', 'repository', 'revwalk', - 'tag', 'tree', 'signature', 'status', 'treebuilder'] + 'tag', 'tree', 'signature', 'status', 'treebuilder', 'diff'] def test_suite(): modules = ['test.test_%s' % n for n in names] return unittest.defaultTestLoader.loadTestsFromNames(modules) From 11d0c9a30f0a2d5e2510acbf5671a9b6df2eff1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 15 Aug 2012 19:58:31 +0200 Subject: [PATCH 0230/2237] diff: expose the hunk header We can rebuild it from the data we already have, but this will allow us to get the function headers in it once the library supports it. --- include/pygit2/types.h | 1 + src/pygit2/diff.c | 11 +++++++++++ test/test_diff.py | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 3c42ef68a..d6311d249 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -68,6 +68,7 @@ typedef struct { typedef struct { PyObject_HEAD + char *header; int old_start; int old_lines; char* old_file; diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index 1c0956e24..c28db3758 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -101,6 +101,16 @@ static int diff_hunk_cb( hunk->new_start = range->new_start; hunk->new_lines = range->new_lines; + if (header) { + hunk->header = malloc(header_len+1); + + if (hunk->header == NULL) + return -1; + + memcpy(hunk->header, header, header_len); + hunk->header[header_len] = '\0'; + } + if (delta->old_file.path != NULL) { len = strlen(delta->old_file.path) + 1; old_path = malloc(sizeof(char) * len); @@ -219,6 +229,7 @@ Hunk_dealloc(Hunk *self) } PyMemberDef Hunk_members[] = { + {"header", T_STRING, offsetof(Hunk, header), 0, "header"}, {"old_start", T_INT, offsetof(Hunk, old_start), 0, "old start"}, {"old_lines", T_INT, offsetof(Hunk, old_lines), 0, "old lines"}, {"old_file", T_STRING, offsetof(Hunk, old_file), 0, "old file"}, diff --git a/test/test_diff.py b/test/test_diff.py index ddfe39863..7cf552989 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -174,6 +174,12 @@ def test_diff_patch(self): diff = commit_a.tree.diff(commit_b.tree) self.assertEqual(diff.patch, PATCH) + def test_diff_header(self): + commit_a = self.repo[COMMIT_SHA1_1] + commit_b = self.repo[COMMIT_SHA1_2] + diff = commit_a.tree.diff(commit_b.tree) + + self.assertEqual(diff.changes['hunks'][0].header, "@@ -1 +1 @@\n") if __name__ == '__main__': unittest.main() From 1138949a69f4967e9d333643ef08b21887232b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 15 Aug 2012 20:13:32 +0200 Subject: [PATCH 0231/2237] diff: make sure malloc succeeds --- src/pygit2/diff.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index c28db3758..4d3b3e44b 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -114,6 +114,11 @@ static int diff_hunk_cb( if (delta->old_file.path != NULL) { len = strlen(delta->old_file.path) + 1; old_path = malloc(sizeof(char) * len); + if (old_path == NULL) { + free(hunk->header); + return -1; + } + memcpy(old_path, delta->old_file.path, len); hunk->old_file = old_path; } else { @@ -123,6 +128,12 @@ static int diff_hunk_cb( if (delta->new_file.path != NULL) { len = strlen(delta->new_file.path) + 1; new_path = malloc(sizeof(char) * len); + if (new_path == NULL) { + free(hunk->header); + free(old_path); + return -1; + } + memcpy(new_path, delta->new_file.path, len); hunk->new_file = new_path; } else { From f3dab28082c819674724bd05c414cf056ea3b813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 15 Aug 2012 20:28:33 +0200 Subject: [PATCH 0232/2237] diff: add old and new OID values to Hunk These can be important, and are necessary to create a patch from this data. --- include/pygit2/types.h | 2 ++ src/pygit2/diff.c | 8 ++++++++ test/test_diff.py | 7 +++++++ 3 files changed, 17 insertions(+) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index d6311d249..59d4055f0 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -71,9 +71,11 @@ typedef struct { char *header; int old_start; int old_lines; + PyObject *old_oid; char* old_file; int new_start; int new_lines; + PyObject *new_oid; char* new_file; PyObject *data; } Hunk; diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index 4d3b3e44b..5207b9122 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -84,6 +84,7 @@ static int diff_hunk_cb( Hunk *hunk; int len; char* old_path, *new_path; + char oid[GIT_OID_HEXSZ]; hunks = PyDict_GetItemString(cb_data, "hunks"); @@ -101,6 +102,11 @@ static int diff_hunk_cb( hunk->new_start = range->new_start; hunk->new_lines = range->new_lines; + git_oid_fmt(oid, &delta->old_file.oid); + hunk->old_oid = PyUnicode_FromStringAndSize(oid, GIT_OID_HEXSZ); + git_oid_fmt(oid, &delta->new_file.oid); + hunk->new_oid = PyUnicode_FromStringAndSize(oid, GIT_OID_HEXSZ); + if (header) { hunk->header = malloc(header_len+1); @@ -244,9 +250,11 @@ PyMemberDef Hunk_members[] = { {"old_start", T_INT, offsetof(Hunk, old_start), 0, "old start"}, {"old_lines", T_INT, offsetof(Hunk, old_lines), 0, "old lines"}, {"old_file", T_STRING, offsetof(Hunk, old_file), 0, "old file"}, + {"old_oid", T_OBJECT, offsetof(Hunk, old_oid), 0, "old_oid"}, {"new_start", T_INT, offsetof(Hunk, new_start), 0, "new start"}, {"new_lines", T_INT, offsetof(Hunk, new_lines), 0, "new lines"}, {"new_file", T_STRING, offsetof(Hunk, new_file), 0, "old file"}, + {"new_oid", T_OBJECT, offsetof(Hunk, new_oid), 0, "new_oid"}, {"data", T_OBJECT, offsetof(Hunk, data), 0, "data"}, {NULL} }; diff --git a/test/test_diff.py b/test/test_diff.py index 7cf552989..7133bb026 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -181,5 +181,12 @@ def test_diff_header(self): self.assertEqual(diff.changes['hunks'][0].header, "@@ -1 +1 @@\n") + def test_diff_oids(self): + commit_a = self.repo[COMMIT_SHA1_1] + commit_b = self.repo[COMMIT_SHA1_2] + diff = commit_a.tree.diff(commit_b.tree) + self.assertEqual(diff.changes['hunks'][0].old_oid, '7f129fd57e31e935c6d60a0c794efe4e6927664b') + self.assertEqual(diff.changes['hunks'][0].new_oid, 'af431f20fc541ed6d5afede3e2dc7160f6f01f16') + if __name__ == '__main__': unittest.main() From 6e06863722210425984d7e77b383198885b34e01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 15 Aug 2012 21:05:12 +0200 Subject: [PATCH 0233/2237] diff: expose the filemode through Hunk --- include/pygit2/types.h | 2 ++ src/pygit2/diff.c | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 59d4055f0..a2c7f560d 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -72,10 +72,12 @@ typedef struct { int old_start; int old_lines; PyObject *old_oid; + int old_mode; char* old_file; int new_start; int new_lines; PyObject *new_oid; + int new_mode; char* new_file; PyObject *data; } Hunk; diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index 5207b9122..4fc44eb83 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -102,6 +102,9 @@ static int diff_hunk_cb( hunk->new_start = range->new_start; hunk->new_lines = range->new_lines; + hunk->old_mode = delta->old_file.mode; + hunk->new_mode = delta->new_file.mode; + git_oid_fmt(oid, &delta->old_file.oid); hunk->old_oid = PyUnicode_FromStringAndSize(oid, GIT_OID_HEXSZ); git_oid_fmt(oid, &delta->new_file.oid); @@ -249,10 +252,12 @@ PyMemberDef Hunk_members[] = { {"header", T_STRING, offsetof(Hunk, header), 0, "header"}, {"old_start", T_INT, offsetof(Hunk, old_start), 0, "old start"}, {"old_lines", T_INT, offsetof(Hunk, old_lines), 0, "old lines"}, + {"old_mode", T_INT, offsetof(Hunk, old_mode), 0, "old mode"}, {"old_file", T_STRING, offsetof(Hunk, old_file), 0, "old file"}, {"old_oid", T_OBJECT, offsetof(Hunk, old_oid), 0, "old_oid"}, {"new_start", T_INT, offsetof(Hunk, new_start), 0, "new start"}, {"new_lines", T_INT, offsetof(Hunk, new_lines), 0, "new lines"}, + {"new_mode", T_INT, offsetof(Hunk, new_mode), 0, "new mode"}, {"new_file", T_STRING, offsetof(Hunk, new_file), 0, "old file"}, {"new_oid", T_OBJECT, offsetof(Hunk, new_oid), 0, "new_oid"}, {"data", T_OBJECT, offsetof(Hunk, data), 0, "data"}, From f60b8a35b583a2899c02a0a05968989a9b704612 Mon Sep 17 00:00:00 2001 From: Ferengee Date: Mon, 20 Aug 2012 18:26:57 +0300 Subject: [PATCH 0234/2237] git_index_read_tree now accepts a stats structure Indexer stats may be NULL but the method still needs three arguments --- src/pygit2/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 7dec03090..d0de48e7d 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -331,7 +331,7 @@ Index_read_tree(Index *self, PyObject *value) if (err < 0) return Error_set(err); - err = git_index_read_tree(self->index, tree); + err = git_index_read_tree(self->index, tree, NULL); if (err < 0) return Error_set(err); From 7e3b21ba55885d8dfaa1823a34a953c44b62d82c Mon Sep 17 00:00:00 2001 From: Alex Chamberlain Date: Thu, 23 Aug 2012 12:47:12 +0100 Subject: [PATCH 0235/2237] Added Repository.create_blob_fromfile --- include/pygit2/repository.h | 1 + src/pygit2/repository.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/include/pygit2/repository.h b/include/pygit2/repository.h index 10895ab23..32780e0b4 100644 --- a/include/pygit2/repository.h +++ b/include/pygit2/repository.h @@ -50,6 +50,7 @@ PyObject* Repository_get_workdir(Repository *self, void *closure); PyObject* Repository_get_config(Repository *self, void *closure); PyObject* Repository_walk(Repository *self, PyObject *args); PyObject* Repository_create_blob(Repository *self, PyObject *args); +PyObject* Repository_create_blob_fromfile(Repository *self, PyObject *args); PyObject* Repository_create_commit(Repository *self, PyObject *args); PyObject* Repository_create_tag(Repository *self, PyObject *args); PyObject* Repository_listall_references(Repository *self, PyObject *args); diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 6bac3b54a..765038854 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -452,6 +452,24 @@ Repository_create_blob(Repository *self, PyObject *args) return git_oid_to_python(oid.id); } +PyObject * +Repository_create_blob_fromfile(Repository *self, PyObject *args) +{ + git_oid oid; + const char* path; + int err; + + if (!PyArg_ParseTuple(args, "s", &path)) + return NULL; + + err = git_blob_create_fromfile(&oid, self->repo, path); + + if (err < 0) + return Error_set(err); + + return git_oid_to_python(oid.id); +} + PyObject * Repository_create_commit(Repository *self, PyObject *args) { @@ -801,6 +819,9 @@ PyMethodDef Repository_methods[] = { {"create_blob", (PyCFunction)Repository_create_blob, METH_VARARGS, "Create a new blob from memory"}, + {"create_blob_fromfile", (PyCFunction)Repository_create_blob_fromfile, + METH_VARARGS, + "Create a new blob from file"}, {"create_reference", (PyCFunction)Repository_create_reference, METH_VARARGS, "Create a new reference \"name\" that points to the object given by its " From 1a2dd955755d04caf937c9f17f5980b6c5be4e1d Mon Sep 17 00:00:00 2001 From: Alex Chamberlain Date: Fri, 24 Aug 2012 10:47:12 +0100 Subject: [PATCH 0236/2237] Added unit test for Repository.create_blob_fromfile --- test/test_blob.py | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/test/test_blob.py b/test/test_blob.py index c7f5cf6b7..0c0c3e002 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -30,16 +30,23 @@ from __future__ import absolute_import from __future__ import unicode_literals import unittest +import tempfile +import os import pygit2 from . import utils -BLOB_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' +BLOB_SHA = 'a520c24d85fbfc815d385957eed41406ca5a860b' +BLOB_CONTENT = """hello world +hola mundo +bonjour le monde +""".encode() BLOB_NEW_CONTENT = b'foo bar\n' +BLOB_FILE_CONTENT = b'bye world\n' -class BlobTest(utils.BareRepoTestCase): +class BlobTest(utils.RepoTestCase): def test_read_blob(self): blob = self.repo[BLOB_SHA] @@ -48,8 +55,8 @@ def test_read_blob(self): self.assertEqual(sha, BLOB_SHA) self.assertTrue(isinstance(blob, pygit2.Blob)) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) - self.assertEqual(b'a contents\n', blob.data) - self.assertEqual(b'a contents\n', blob.read_raw()) + self.assertEqual(BLOB_CONTENT, blob.data) + self.assertEqual(BLOB_CONTENT, blob.read_raw()) def test_create_blob(self): blob_oid = self.repo.create_blob(BLOB_NEW_CONTENT) @@ -67,6 +74,22 @@ def test_create_blob(self): self.assertEqual(BLOB_NEW_CONTENT, blob.data) self.assertEqual(BLOB_NEW_CONTENT, blob.read_raw()) + def test_create_blob_fromfile(self): + + blob_oid = self.repo.create_blob_fromfile("bye.txt") + blob = self.repo[blob_oid] + + self.assertTrue(isinstance(blob, pygit2.Blob)) + self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) + + self.assertEqual(blob_oid, blob.oid) + self.assertEqual( + utils.gen_blob_sha1(BLOB_FILE_CONTENT), + utils.oid_to_hex(blob_oid) + ) + + self.assertEqual(BLOB_FILE_CONTENT, blob.data) + self.assertEqual(BLOB_FILE_CONTENT, blob.read_raw()) if __name__ == '__main__': unittest.main() From fb4999064029d868213b8d2b9120607314515f3c Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Sat, 25 Aug 2012 20:20:40 -0700 Subject: [PATCH 0237/2237] docs: Fix 'Diff' example t1 should point to a tree, not a commit. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d81c0b2bf..5df53c805 100644 --- a/README.rst +++ b/README.rst @@ -211,7 +211,7 @@ A diff shows the changes between trees, an index or the working dir:: # Diff two trees >>> t0 = repo.head.tree - >>> t1 = repo.head.parents[0] + >>> t1 = repo.head.parents[0].tree >>> diff = t0.diff(t1) >>> diff From 32ff474dc18475d64e674b6b379fe925bfae4525 Mon Sep 17 00:00:00 2001 From: Eric Schrijver Date: Wed, 5 Sep 2012 03:03:54 +0300 Subject: [PATCH 0238/2237] More obvious diff example By diffing HEAD~1 with HEAD, we show the effect of the latest commit. The provided example diffed HEAD with HEAD~1, which shows a diff that goes back in time. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 5df53c805..afdeeb893 100644 --- a/README.rst +++ b/README.rst @@ -212,7 +212,7 @@ A diff shows the changes between trees, an index or the working dir:: # Diff two trees >>> t0 = repo.head.tree >>> t1 = repo.head.parents[0].tree - >>> diff = t0.diff(t1) + >>> diff = t1.diff(t0) >>> diff # Diff a tree with the index From 97aba6b14b7c15d9ac53d6951d78a40da0c3b6ba Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 7 Sep 2012 20:52:37 +0200 Subject: [PATCH 0239/2237] Make time and offset arguments to Signature optional If these are left blank, use git_signature_now to insert the current time. Specifying one but not the other causes a TypeError. Test inlcluded. --- src/pygit2/signature.c | 11 +++++++++-- test/test_signature.py | 12 ++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/pygit2/signature.c b/src/pygit2/signature.c index d2ea1ce83..430f881f1 100644 --- a/src/pygit2/signature.c +++ b/src/pygit2/signature.c @@ -49,7 +49,7 @@ Signature_init(Signature *self, PyObject *args, PyObject *kwds) return -1; } - if (!PyArg_ParseTuple(args, "OsLi|s", + if (!PyArg_ParseTuple(args, "Os|Lis", &py_name, &email, &time, &offset, &encoding)) return -1; @@ -57,7 +57,14 @@ Signature_init(Signature *self, PyObject *args, PyObject *kwds) if (name == NULL) return -1; - err = git_signature_new(&signature, name, email, time, offset); + if (PySequence_Length(args) == 2) { + err = git_signature_now(&signature, name, email); + } else if (PySequence_Length(args) == 3) { + PyErr_SetString(PyExc_TypeError, "offset must be specified with time"); + return -1; + } else { + err = git_signature_new(&signature, name, email, time, offset); + } free(name); if (err < 0) { Error_set(err); diff --git a/test/test_signature.py b/test/test_signature.py index 1ee7e2bd5..2b59c2964 100644 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -28,6 +28,7 @@ from __future__ import absolute_import from __future__ import unicode_literals import unittest +import time from pygit2 import Signature from .utils import NoRepoTestCase @@ -50,6 +51,17 @@ def test_latin1(self): self.assertEqual(signature.name, signature._name.decode(encoding)) self.assertEqual(signature.name.encode(encoding), signature._name) + def test_now(self): + self.assertRaises(TypeError, Signature, 'Foo Ibáñez', + 'foo@example.com', 1322174594) + signature = Signature('Foo Ibáñez', 'foo@example.com') + encoding = signature._encoding + self.assertEqual(encoding, 'utf-8') + self.assertEqual(encoding, signature._encoding) + self.assertEqual(signature.name, signature._name.decode(encoding)) + self.assertEqual(signature.name.encode(encoding), signature._name) + self.assertTrue(abs(signature.time - time.time()) < 5) + if __name__ == '__main__': unittest.main() From 32753a6d1782eca4e435cbf18b9536e4a4ca6a1d Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Sat, 8 Sep 2012 15:17:52 +0200 Subject: [PATCH 0240/2237] Add GIT_FILEMODE_* constants These are useful when putting files in the treebuilder --- src/pygit2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/pygit2.c b/src/pygit2.c index e4eea03a8..e41015a65 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -303,6 +303,15 @@ moduleinit(PyObject* m) GIT_DIFF_LINE_HUNK_HDR); PyModule_AddIntConstant(m, "GIT_DIFF_LINE_BINARY", GIT_DIFF_LINE_BINARY); + /* Valid modes for index and tree entries. */ + PyModule_AddIntConstant(m, "GIT_FILEMODE_NEW", GIT_FILEMODE_NEW); + PyModule_AddIntConstant(m, "GIT_FILEMODE_TREE", GIT_FILEMODE_TREE); + PyModule_AddIntConstant(m, "GIT_FILEMODE_BLOB", GIT_FILEMODE_BLOB); + PyModule_AddIntConstant(m, "GIT_FILEMODE_BLOB_EXECUTABLE", + GIT_FILEMODE_BLOB_EXECUTABLE); + PyModule_AddIntConstant(m, "GIT_FILEMODE_LINK", GIT_FILEMODE_LINK); + PyModule_AddIntConstant(m, "GIT_FILEMODE_COMMIT", GIT_FILEMODE_COMMIT); + return m; } From 91e211d4f9b528943ef7bf157fe89c2035934aa9 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 13 Sep 2012 13:47:57 -0400 Subject: [PATCH 0241/2237] error: replace `GIT_REVWALKOVER` with `GIT_ITEROVER`. This catches up with changes in libgit2: commit f335ecd6e126aa9dea28786522c0e6ce71596e91 Author: Russell Belfer Date: Thu Aug 30 14:24:16 2012 -0700 --- src/pygit2/error.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/error.c b/src/pygit2/error.c index 11129a3a7..dd4138dfa 100644 --- a/src/pygit2/error.c +++ b/src/pygit2/error.c @@ -55,7 +55,7 @@ PyObject * Error_type(int type) return GitError; /** No entries left in ref walker */ - case GIT_REVWALKOVER: + case GIT_ITEROVER: return PyExc_StopIteration; } From 8e1910281523e28dad4c1eb39c6b6ab92919437d Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 13 Sep 2012 13:53:37 -0400 Subject: [PATCH 0242/2237] index: Add NULL `stats` argument to git_index_read_tree call. This catches up with changes in libgit2: commit 4bf5115642b64851f9a32a8157010b588bf44103 Author: Ben Straub Date: Mon Jul 30 14:52:46 2012 -0700 Enable stats on git_index_read_tree. Replace with the contents of git_index_read_tree_with_stats() and improve documentation comments. --- src/pygit2/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 7dec03090..d0de48e7d 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -331,7 +331,7 @@ Index_read_tree(Index *self, PyObject *value) if (err < 0) return Error_set(err); - err = git_index_read_tree(self->index, tree); + err = git_index_read_tree(self->index, tree, NULL); if (err < 0) return Error_set(err); From 651aa6a830edbbe9b2f472e25bdceda30a869dd9 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 13 Sep 2012 16:14:24 -0400 Subject: [PATCH 0243/2237] tree: rename Tree.attributes to Tree.filemode. This catches up with changes in libgit2: commit 9d7ac675d06dab2e000ad32f9248631af0191f85 Author: nulltoken Date: Tue Aug 21 11:45:16 2012 +0200 tree entry: rename git_tree_entry_attributes() into git_tree_entry_filemode() --- README.rst | 2 +- include/pygit2/tree.h | 2 +- src/pygit2/tree.c | 6 +++--- test/test_tree.py | 6 +++--- test/test_treebuilder.py | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index afdeeb893..bdf622519 100644 --- a/README.rst +++ b/README.rst @@ -200,7 +200,7 @@ This is the interface of a tree entry:: TreeEntry.name -- name of the tree entry TreeEntry.oid -- the id of the git object TreeEntry.hex -- hexadecimal representation of the oid - TreeEntry.attributes -- the Unix file attributes + TreeEntry.filemode -- the Unix file attributes TreeEntry.to_object() -- returns the git object (equivalent to repo[entry.oid]) diff --git a/include/pygit2/tree.h b/include/pygit2/tree.h index e4b086faf..68218f75c 100644 --- a/include/pygit2/tree.h +++ b/include/pygit2/tree.h @@ -33,7 +33,7 @@ #include #include -PyObject* TreeEntry_get_attributes(TreeEntry *self); +PyObject* TreeEntry_get_filemode(TreeEntry *self); PyObject* TreeEntry_get_name(TreeEntry *self); PyObject* TreeEntry_get_oid(TreeEntry *self); PyObject* TreeEntry_get_hex(TreeEntry *self); diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index 827b1c009..62db68255 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -46,9 +46,9 @@ TreeEntry_dealloc(TreeEntry *self) } PyObject * -TreeEntry_get_attributes(TreeEntry *self) +TreeEntry_get_filemode(TreeEntry *self) { - return PyInt_FromLong(git_tree_entry_attributes(self->entry)); + return PyInt_FromLong(git_tree_entry_filemode(self->entry)); } PyObject * @@ -84,7 +84,7 @@ TreeEntry_to_object(TreeEntry *self) } PyGetSetDef TreeEntry_getseters[] = { - {"attributes", (getter)TreeEntry_get_attributes, NULL, "attributes", NULL}, + {"filemode", (getter)TreeEntry_get_filemode, NULL, "filemode", NULL}, {"name", (getter)TreeEntry_get_name, NULL, "name", NULL}, {"oid", (getter)TreeEntry_get_oid, NULL, "object id", NULL}, {"hex", (getter)TreeEntry_get_hex, NULL, "hex oid", NULL}, diff --git a/test/test_tree.py b/test/test_tree.py index 4ec22a998..eceb7f28b 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -42,11 +42,11 @@ class TreeTest(utils.BareRepoTestCase): - def assertTreeEntryEqual(self, entry, sha, name, attributes): + def assertTreeEntryEqual(self, entry, sha, name, filemode): self.assertEqual(entry.hex, sha) self.assertEqual(entry.name, name) - self.assertEqual(entry.attributes, attributes, - '0%o != 0%o' % (entry.attributes, attributes)) + self.assertEqual(entry.filemode, filemode, + '0%o != 0%o' % (entry.filemode, filemode)) def test_read_tree(self): tree = self.repo[TREE_SHA] diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index 8dbea7724..de6e85d17 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -57,7 +57,7 @@ def test_rebuild_treebuilder(self): tree = self.repo[TREE_SHA] bld = self.repo.TreeBuilder() for e in tree: - bld.insert(e.name, e.hex, e.attributes) + bld.insert(e.name, e.hex, e.filemode) result = bld.write() self.assertEqual(tree.oid, result) From 477e48ef6c9a4329fdcd42e06554544b83654fbc Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 13 Sep 2012 16:30:21 -0400 Subject: [PATCH 0244/2237] test_diff: hunk.old_lines should be 1 in test_diff_tree and test_diff_merge. In both cases, the file contents change from 'X contents\n' to 'X contents 2\n' (where 'X' is 'a' or 'b'). This means were removing one line and adding another. I'm not sure how this test was passing against libgit2 v0.17.0, so I may be missing something important. --- test/test_diff.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_diff.py b/test/test_diff.py index 7133bb026..5f61b0948 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -123,9 +123,9 @@ def test_diff_tree(self): hunk = diff.changes['hunks'][0] self.assertEqual(hunk.old_start, 1) - self.assertEqual(hunk.old_lines, 0) + self.assertEqual(hunk.old_lines, 1) self.assertEqual(hunk.new_start, 1) - self.assertEqual(hunk.new_lines, 0) + self.assertEqual(hunk.new_lines, 1) self.assertEqual(hunk.old_file, 'a') self.assertEqual(hunk.new_file, 'a') @@ -157,9 +157,9 @@ def test_diff_merge(self): hunk = diff_b.changes['hunks'][1] self.assertEqual(hunk.old_start, 1) - self.assertEqual(hunk.old_lines, 0) + self.assertEqual(hunk.old_lines, 1) self.assertEqual(hunk.new_start, 1) - self.assertEqual(hunk.new_lines, 0) + self.assertEqual(hunk.new_lines, 1) self.assertEqual(hunk.old_file, 'b') self.assertEqual(hunk.new_file, 'b') From cc7f137f7b003abb1bd105d7855a8b16d464b451 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 13 Sep 2012 16:41:56 -0400 Subject: [PATCH 0245/2237] object: factor `wrap_object` out of `lookup_object_prefix`. Generating an `Object *` from a `git_object *` may be useful in other functions (e.g. the upcoming `Repository_revparse_single`). --- include/pygit2/object.h | 1 + src/pygit2/object.c | 38 ++++++++++++++++++++++++++++++++++++++ src/pygit2/repository.c | 28 ++-------------------------- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/include/pygit2/object.h b/include/pygit2/object.h index 5255996ba..64bf6128d 100644 --- a/include/pygit2/object.h +++ b/include/pygit2/object.h @@ -37,5 +37,6 @@ PyObject* Object_get_oid(Object *self); PyObject* Object_get_hex(Object *self); PyObject* Object_get_type(Object *self); PyObject* Object_read_raw(Object *self); +PyObject* wrap_object(git_object *c_object, Repository *repo); #endif diff --git a/src/pygit2/object.c b/src/pygit2/object.c index 12a085580..172cf3f8b 100644 --- a/src/pygit2/object.c +++ b/src/pygit2/object.c @@ -34,6 +34,12 @@ #include #include +extern PyTypeObject TreeType; +extern PyTypeObject CommitType; +extern PyTypeObject BlobType; +extern PyTypeObject TagType; + + void Object_dealloc(Object* self) { @@ -145,3 +151,35 @@ PyTypeObject ObjectType = { 0, /* tp_alloc */ 0, /* tp_new */ }; + +PyObject * +wrap_object(git_object *c_object, Repository *repo) +{ + Object *py_obj = NULL; + + switch (git_object_type(c_object)) { + case GIT_OBJ_COMMIT: + py_obj = PyObject_New(Object, &CommitType); + break; + case GIT_OBJ_TREE: + py_obj = PyObject_New(Object, &TreeType); + break; + case GIT_OBJ_BLOB: + py_obj = PyObject_New(Object, &BlobType); + break; + case GIT_OBJ_TAG: + py_obj = PyObject_New(Object, &TagType); + break; + default: + assert(0); + } + + if (py_obj) { + py_obj->obj = c_object; + if (repo) { + py_obj->repo = repo; + Py_INCREF(repo); + } + } + return (PyObject *)py_obj; +} diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 765038854..30f70304f 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -40,9 +41,6 @@ extern PyTypeObject IndexType; extern PyTypeObject WalkerType; extern PyTypeObject SignatureType; extern PyTypeObject TreeType; -extern PyTypeObject CommitType; -extern PyTypeObject BlobType; -extern PyTypeObject TagType; extern PyTypeObject TreeBuilderType; extern PyTypeObject ConfigType; extern PyTypeObject DiffType; @@ -72,29 +70,7 @@ lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, if (err < 0) return Error_set_oid(err, oid, len); - switch (git_object_type(obj)) { - case GIT_OBJ_COMMIT: - py_obj = PyObject_New(Object, &CommitType); - break; - case GIT_OBJ_TREE: - py_obj = PyObject_New(Object, &TreeType); - break; - case GIT_OBJ_BLOB: - py_obj = PyObject_New(Object, &BlobType); - break; - case GIT_OBJ_TAG: - py_obj = PyObject_New(Object, &TagType); - break; - default: - assert(0); - } - - if (py_obj) { - py_obj->obj = obj; - py_obj->repo = repo; - Py_INCREF(repo); - } - return (PyObject*)py_obj; + return wrap_object(obj, repo); } PyObject * From 437c1af33f5f98ead8c455601365c99af3c2d696 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 13 Sep 2012 16:54:07 -0400 Subject: [PATCH 0246/2237] test_repository: fix HEAD_SHA -> 2cdae2 and use assertEqual when testing. The previous test always passed, because bool(HEAD_SHA) is True. The update to HEAD_SHA should have happened in: commit c06e10e67e746099b8d454212acc56391199eb31 Author: Petr Hosek Date: Tue May 29 17:41:07 2012 +0100 Support for diff merge operation added --- test/test_repository.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_repository.py b/test/test_repository.py index 27a674acb..9164b8766 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -39,7 +39,7 @@ from . import utils -HEAD_SHA = '5fe808e8953c12735680c257f56600cb0de44b10' +HEAD_SHA = '2cdae28389c059815e951d0bb9eed6533f61a46b' A_HEX_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' A_BIN_SHA = binascii.unhexlify(A_HEX_SHA.encode('ascii')) @@ -48,7 +48,7 @@ class RepositoryTest(utils.BareRepoTestCase): def test_head(self): head = self.repo.head - self.assertTrue(HEAD_SHA, head.hex) + self.assertEqual(HEAD_SHA, head.hex) self.assertTrue(type(head), Commit) def test_read(self): From 0238fb72dfdf2a2308f2da347717cbaafddc4b83 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 13 Sep 2012 17:09:12 -0400 Subject: [PATCH 0247/2237] repository: add Repository.revparse_single(). This provides access to libgit2's 'git_revparse_single'. --- README.rst | 6 ++++++ src/pygit2/repository.c | 28 ++++++++++++++++++++++++++++ test/test_repository.py | 4 ++++ 3 files changed, 38 insertions(+) diff --git a/README.rst b/README.rst index bdf622519..18ca05195 100644 --- a/README.rst +++ b/README.rst @@ -270,6 +270,12 @@ The interface for RefLogEntry:: RefLogEntry.oid_old -- oid of old reference RefLogEntry.oid_new -- oid of new reference +Revision parsing +================ + +You can use any of the fancy `` forms supported by libgit2:: + + >>> commit = repo.revparse_single('HEAD^') Revision walking ================= diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 30f70304f..8ba37042e 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -198,6 +198,30 @@ Repository_getitem(Repository *self, PyObject *value) return lookup_object_prefix(self, &oid, len, GIT_OBJ_ANY); } +PyObject * +Repository_revparse_single(Repository *self, PyObject *py_spec) +{ + git_object *c_obj; + char *c_spec; + char *encoding = "ascii"; + int err; + + /* 1- Get the C revision spec */ + c_spec = py_str_to_c_str(py_spec, encoding); + if (c_spec == NULL) + return NULL; + + /* 2- Lookup */ + err = git_revparse_single(&c_obj, self->repo, c_spec); + if (err < 0) { + PyObject *err_obj = Error_set_str(err, c_spec); + free(c_spec); + return err_obj; + } + + return wrap_object(c_obj, self); +} + git_odb_object * Repository_read_raw(git_repository *repo, const git_oid *oid, size_t len) { @@ -792,6 +816,10 @@ PyMethodDef Repository_methods[] = { "Return a list with all the references in the repository."}, {"lookup_reference", (PyCFunction)Repository_lookup_reference, METH_O, "Lookup a reference by its name in a repository."}, + {"revparse_single", (PyCFunction)Repository_revparse_single, METH_O, + "Find an object, as specified by a revision string. See " + "`man gitrevisions`, or the documentation for `git rev-parse` for " + "information on the syntax accepted."}, {"create_blob", (PyCFunction)Repository_create_blob, METH_VARARGS, "Create a new blob from memory"}, diff --git a/test/test_repository.py b/test/test_repository.py index 9164b8766..bf74437f9 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -40,6 +40,7 @@ HEAD_SHA = '2cdae28389c059815e951d0bb9eed6533f61a46b' +PARENT_SHA = '5fe808e8953c12735680c257f56600cb0de44b10' # HEAD^ A_HEX_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' A_BIN_SHA = binascii.unhexlify(A_HEX_SHA.encode('ascii')) @@ -127,6 +128,9 @@ def test_get_path(self): def test_get_workdir(self): self.assertEqual(self.repo.workdir, None) + def test_revparse_single(self): + parent = self.repo.revparse_single('HEAD^') + self.assertEqual(parent.hex, PARENT_SHA) class RepositoryTest_II(utils.RepoTestCase): From 430f4dc343fc0fa7c61cb7483b07714396b0b88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 14 Sep 2012 14:04:02 +0200 Subject: [PATCH 0248/2237] Fix typo in repository test --- test/test_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_repository.py b/test/test_repository.py index 9164b8766..bdb4c1a3e 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -49,7 +49,7 @@ class RepositoryTest(utils.BareRepoTestCase): def test_head(self): head = self.repo.head self.assertEqual(HEAD_SHA, head.hex) - self.assertTrue(type(head), Commit) + self.assertEqual(type(head), Commit) def test_read(self): self.assertRaises(TypeError, self.repo.read, 123) From bc0c0e17a8868d48d9e96a8b51adb886780d0214 Mon Sep 17 00:00:00 2001 From: Ridge Kennedy Date: Sun, 16 Sep 2012 20:50:30 +1200 Subject: [PATCH 0249/2237] Add Blob.size --- include/pygit2/blob.h | 38 ++++++++++++++++++++++++++++++++++++++ src/pygit2/blob.c | 10 ++++++++++ test/test_blob.py | 3 +++ 3 files changed, 51 insertions(+) create mode 100644 include/pygit2/blob.h diff --git a/include/pygit2/blob.h b/include/pygit2/blob.h new file mode 100644 index 000000000..a55b8c5c3 --- /dev/null +++ b/include/pygit2/blob.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef INCLUDE_pygit2_blob_h +#define INCLUDE_pygit2_blob_h + +#define PY_SSIZE_T_CLEAN +#include +#include +#include + +PyObject* Blob_get_size(Blob *self); + +#endif diff --git a/src/pygit2/blob.c b/src/pygit2/blob.c index c69c103f3..9ec60ddd1 100644 --- a/src/pygit2/blob.c +++ b/src/pygit2/blob.c @@ -27,10 +27,20 @@ #define PY_SSIZE_T_CLEAN #include +#include #include +#include + +PyObject * +Blob_get_size(Blob *self) +{ + return PyInt_FromLong(git_blob_rawsize(self->blob)); +} + PyGetSetDef Blob_getseters[] = { {"data", (getter)Object_read_raw, NULL, "raw data", NULL}, + {"size", (getter)Blob_get_size, NULL, "size", NULL}, {NULL} }; diff --git a/test/test_blob.py b/test/test_blob.py index 0c0c3e002..133ea7b79 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -56,6 +56,7 @@ def test_read_blob(self): self.assertTrue(isinstance(blob, pygit2.Blob)) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) self.assertEqual(BLOB_CONTENT, blob.data) + self.assertEqual(len(BLOB_CONTENT), blob.size) self.assertEqual(BLOB_CONTENT, blob.read_raw()) def test_create_blob(self): @@ -72,6 +73,7 @@ def test_create_blob(self): ) self.assertEqual(BLOB_NEW_CONTENT, blob.data) + self.assertEqual(len(BLOB_NEW_CONTENT), blob.size) self.assertEqual(BLOB_NEW_CONTENT, blob.read_raw()) def test_create_blob_fromfile(self): @@ -89,6 +91,7 @@ def test_create_blob_fromfile(self): ) self.assertEqual(BLOB_FILE_CONTENT, blob.data) + self.assertEqual(len(BLOB_FILE_CONTENT), blob.size) self.assertEqual(BLOB_FILE_CONTENT, blob.read_raw()) if __name__ == '__main__': From f10d2b7b3f0c67d81b175326035e4813420156bd Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 13 Sep 2012 07:52:22 -0400 Subject: [PATCH 0250/2237] pygit2:version: add 'pygit2.__version__' for easy access from client software. Moved the hardcoded version from setup.py to pygit2/version.py so client software can figure out which version of pygit2 it's using. Having setup.py import pygit2.version.__version__ removes duplication, and also means that setup.py will always use the local version (and not the version of a previously installed pygit2). --- pygit2/__init__.py | 1 + pygit2/version.py | 26 ++++++++++++++++++++++++++ setup.py | 7 ++++++- 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 pygit2/version.py diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 151fcafcf..6a0e24b53 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -25,5 +25,6 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. +from .version import __version__ from _pygit2 import * import pygit2.utils diff --git a/pygit2/version.py b/pygit2/version.py new file mode 100644 index 000000000..e5179fcf1 --- /dev/null +++ b/pygit2/version.py @@ -0,0 +1,26 @@ +# Copyright 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. + +__version__ = '0.17.2' diff --git a/setup.py b/setup.py index 4722427ed..7a8dce883 100644 --- a/setup.py +++ b/setup.py @@ -38,6 +38,11 @@ from distutils.command.sdist import sdist from distutils import log +# read version from local pygit2/version.py without pulling in +# pygit2/__init__.py +sys.path.insert(0, 'pygit2') +from version import __version__ + # Use environment variable LIBGIT2 to set your own libgit2 configuration. libgit2_path = os.getenv("LIBGIT2") @@ -163,7 +168,7 @@ def get_file_list(self): setup(name='pygit2', description='Python bindings for libgit2.', keywords='git', - version='0.17.2', + version=__version__, url='http://github.com/libgit2/pygit2', classifiers=classifiers, license='GPLv2', From 50793b673c704db9f657bb471456a6470c0671d0 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 13 Sep 2012 10:43:55 -0400 Subject: [PATCH 0251/2237] signature: Add keyword argument parsing to Signature(). Now you can specify `encoding` without bothering with `time` or `offset`. --- src/pygit2/signature.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/pygit2/signature.c b/src/pygit2/signature.c index 430f881f1..19a28cb25 100644 --- a/src/pygit2/signature.c +++ b/src/pygit2/signature.c @@ -36,32 +36,26 @@ int Signature_init(Signature *self, PyObject *args, PyObject *kwds) { + const char *keywords[] = { + "name", "email", "time", "offset", "encoding", NULL}; PyObject *py_name; - char *name, *email, *encoding = NULL; - long long time; - int offset; + char *name, *email, *encoding = "ascii"; + long long time = -1; + int offset = 0; int err; git_signature *signature; - if (kwds) { - PyErr_SetString(PyExc_TypeError, - "Signature takes no keyword arguments"); - return -1; - } - - if (!PyArg_ParseTuple(args, "Os|Lis", - &py_name, &email, &time, &offset, &encoding)) + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "Os|Lis", keywords, + &py_name, &email, &time, &offset, &encoding)) return -1; name = py_str_to_c_str(py_name, encoding); if (name == NULL) return -1; - if (PySequence_Length(args) == 2) { + if (time == -1) { err = git_signature_now(&signature, name, email); - } else if (PySequence_Length(args) == 3) { - PyErr_SetString(PyExc_TypeError, "offset must be specified with time"); - return -1; } else { err = git_signature_new(&signature, name, email, time, offset); } From b3b7e98f9247f841b8d844ab5b3c5dac1e653b50 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 13 Sep 2012 10:46:11 -0400 Subject: [PATCH 0252/2237] README: Update Signature examples to use the default time arguments. --- README.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index afdeeb893..081972f8d 100644 --- a/README.rst +++ b/README.rst @@ -148,9 +148,8 @@ Creating commits Commits can be created by calling the ``create_commit`` method of the repository with the following parameters:: - >>> from time import time - >>> author = Signature('Alice Author', 'alice@authors.tld', time(), 0) - >>> committer = Signature('Cecil Committer', 'cecil@committers.tld', time(), 0) + >>> author = Signature('Alice Author', 'alice@authors.tld') + >>> committer = Signature('Cecil Committer', 'cecil@committers.tld') >>> tree = repo.TreeBuilder().write() >>> repo.create_commit( ... 'refs/heads/master', # the name of the reference to update From 36ae908de287022c398c586afa5b71e30139ac56 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 13 Sep 2012 13:27:04 -0400 Subject: [PATCH 0253/2237] test: use Signature(encoding=...) in test_commit and test_signature. Correct tests now that the default name encoding is ASCII. --- test/test_commit.py | 4 +++- test/test_signature.py | 21 ++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/test/test_commit.py b/test/test_commit.py index 0884364d2..35ec65fe3 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -68,7 +68,9 @@ def test_new_commit(self): repo = self.repo message = 'New commit.\n\nMessage with non-ascii chars: ééé.\n' committer = Signature('John Doe', 'jdoe@example.com', 12346, 0) - author = Signature('J. David Ibáñez', 'jdavid@example.com', 12345, 0) + author = Signature( + 'J. David Ibáñez', 'jdavid@example.com', 12345, 0, + encoding='utf-8') tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' tree_prefix = tree[:5] too_short_prefix = tree[:3] diff --git a/test/test_signature.py b/test/test_signature.py index 2b59c2964..2917a4971 100644 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -37,26 +37,29 @@ class SignatureTest(NoRepoTestCase): def test_default(self): - signature = Signature('Foo Ibáñez', 'foo@example.com', 1322174594, 60) + signature = Signature( + 'Foo', 'foo@example.com', 1322174594, 60) encoding = signature._encoding - self.assertEqual(encoding, 'utf-8') + self.assertEqual(encoding, 'ascii') self.assertEqual(signature.name, signature._name.decode(encoding)) self.assertEqual(signature.name.encode(encoding), signature._name) + def test_ascii(self): + self.assertRaises( + UnicodeEncodeError, Signature, 'Foo Ibáñez', 'foo@example.com') + def test_latin1(self): encoding = 'iso-8859-1' - signature = Signature('Foo Ibáñez', 'foo@example.com', 1322174594, 60, - encoding) + signature = Signature( + 'Foo Ibáñez', 'foo@example.com', encoding=encoding) self.assertEqual(encoding, signature._encoding) self.assertEqual(signature.name, signature._name.decode(encoding)) self.assertEqual(signature.name.encode(encoding), signature._name) def test_now(self): - self.assertRaises(TypeError, Signature, 'Foo Ibáñez', - 'foo@example.com', 1322174594) - signature = Signature('Foo Ibáñez', 'foo@example.com') - encoding = signature._encoding - self.assertEqual(encoding, 'utf-8') + encoding = 'utf-8' + signature = Signature( + 'Foo Ibáñez', 'foo@example.com', encoding=encoding) self.assertEqual(encoding, signature._encoding) self.assertEqual(signature.name, signature._name.decode(encoding)) self.assertEqual(signature.name.encode(encoding), signature._name) From 1148c5d73bbe44d96d5ca084145fd157c2748750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 21 Sep 2012 16:30:50 +0200 Subject: [PATCH 0254/2237] Release v0.17.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Features: - New 'Blob.size' getter - New 'Repository.create_blob_fromfile' method - Signature, now the time and offset parameters are optional - Improved diff support Other: - Add 'pygit2.__version__' - Optimize usage of Travis - Various fixes for the unit tests - Various documentation improvements Thanks to Alex Chamberlain, Carlos Martín Nieto, Eric Davis, Eric Schrijver, Petr Viktorin, Ridge Kennedy and W. Trevor King. --- pygit2/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygit2/version.py b/pygit2/version.py index e5179fcf1..115507691 100644 --- a/pygit2/version.py +++ b/pygit2/version.py @@ -23,4 +23,4 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -__version__ = '0.17.2' +__version__ = '0.17.3' From 1c7a18724c45ffacba8a370b2097fa3ef7bb8ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 21 Sep 2012 16:44:13 +0200 Subject: [PATCH 0255/2237] Switch to use libgit2's development branch --- .travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.sh b/.travis.sh index 26985cf4c..1d73b003d 100755 --- a/.travis.sh +++ b/.travis.sh @@ -2,7 +2,7 @@ cd ~ -git clone --depth=1 -b master https://github.com/libgit2/libgit2.git +git clone --depth=1 -b development https://github.com/libgit2/libgit2.git cd libgit2/ mkdir build && cd build From ded3d91eabe3267236208d3071b3dd55386b5765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 21 Sep 2012 23:31:43 +0200 Subject: [PATCH 0256/2237] Fix compilation --- src/pygit2/error.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/error.c b/src/pygit2/error.c index 11129a3a7..dd4138dfa 100644 --- a/src/pygit2/error.c +++ b/src/pygit2/error.c @@ -55,7 +55,7 @@ PyObject * Error_type(int type) return GitError; /** No entries left in ref walker */ - case GIT_REVWALKOVER: + case GIT_ITEROVER: return PyExc_StopIteration; } From 1be3e8301d4ebffd1b710cf2b09c880790712e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 22 Sep 2012 19:07:15 +0200 Subject: [PATCH 0257/2237] Get a useful traceback when importing pygit2 fails --- test/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/__init__.py b/test/__init__.py index 21ea01481..4cd7cc553 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -38,6 +38,10 @@ names = ['blob', 'commit', 'config', 'index', 'refs', 'repository', 'revwalk', 'tag', 'tree', 'signature', 'status', 'treebuilder', 'diff'] def test_suite(): + # Sometimes importing pygit2 fails, we try this first to get an + # informative traceback + import pygit2 + # Go modules = ['test.test_%s' % n for n in names] return unittest.defaultTestLoader.loadTestsFromNames(modules) From a1edbe0b8638ac5842105cfd2975e667193854d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 23 Sep 2012 15:10:05 +0200 Subject: [PATCH 0258/2237] Fix compilation warnings --- src/pygit2/signature.c | 3 +-- src/pygit2/tree.c | 32 ++++++++++++-------------------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/pygit2/signature.c b/src/pygit2/signature.c index 19a28cb25..cd53a32f8 100644 --- a/src/pygit2/signature.c +++ b/src/pygit2/signature.c @@ -36,8 +36,7 @@ int Signature_init(Signature *self, PyObject *args, PyObject *kwds) { - const char *keywords[] = { - "name", "email", "time", "offset", "encoding", NULL}; + char *keywords[] = {"name", "email", "time", "offset", "encoding", NULL}; PyObject *py_name; char *name, *email, *encoding = "ascii"; long long time = -1; diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index 0cf929965..48e539548 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -26,8 +26,8 @@ */ #define PY_SSIZE_T_CLEAN -#include #include +#include #include #include #include @@ -237,8 +237,8 @@ Tree_getitem_by_index(Tree *self, PyObject *py_index) TreeEntry * Tree_getitem(Tree *self, PyObject *value) { - char *name; - const git_tree_entry *entry; + char *path; + git_tree_entry *entry; int err; /* Case 1: integer */ @@ -246,29 +246,21 @@ Tree_getitem(Tree *self, PyObject *value) return Tree_getitem_by_index(self, value); /* Case 2: byte or text string */ - name = py_path_to_c_str(value); - if (name == NULL) + path = py_path_to_c_str(value); + if (path == NULL) return NULL; - if (strchr(name, '/') != NULL) { - /* Case 2a: path string */ - err = git_tree_entry_bypath(&entry, self->tree, name); - if (err == GIT_ENOTFOUND) - entry = NULL; - else if (err < 0) - return Error_set(err); - - } else { - /* Case 2b: base name */ - entry = git_tree_entry_byname(self->tree, name); - } + err = git_tree_entry_bypath(&entry, self->tree, path); + free(path); - free(name); - - if (!entry) { + if (err == GIT_ENOTFOUND) { PyErr_SetObject(PyExc_KeyError, value); return NULL; } + + if (err < 0) + return (TreeEntry*)Error_set(err); + return wrap_tree_entry(entry, self); } From 3e9daa4ae389a4a4797403b91c5ceeff09340a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 23 Sep 2012 15:12:46 +0200 Subject: [PATCH 0259/2237] import pygit2 works again (diff tests still fail) --- src/pygit2/tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index 48e539548..e81dc3c27 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -49,7 +49,7 @@ TreeEntry_dealloc(TreeEntry *self) PyObject * TreeEntry_get_attributes(TreeEntry *self) { - return PyInt_FromLong(git_tree_entry_attributes(self->entry)); + return PyInt_FromLong(git_tree_entry_filemode(self->entry)); } PyObject * From 63e0c0a3ad25e5a2da3fb67c9e98a2ff3dafa739 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 26 Sep 2012 12:14:40 +0200 Subject: [PATCH 0260/2237] added build status image to README --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index cf095a4e1..d7f88d5d1 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,9 @@ pygit2 - libgit2 bindings in Python ===================================== +.. image:: https://secure.travis-ci.org/libgit2/pygit2.png + :target: http://travis-ci.org/libgit2/pygit2 + pygit2 is a set of Python bindings to the libgit2 linkable C Git library. The supported versions of Python are 2.6, 2.7, 3.1 and 3.2 From ad8103bc486e5e8f2ea3f4e81acd2f5deefd4714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 3 Oct 2012 14:08:27 +0200 Subject: [PATCH 0261/2237] Fix reference test case Now git_reference_lookup says "foo" is an invalid reference name (returns GITERR_REFERENCE instead of GIT_ENOTFOUND). --- test/test_refs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_refs.py b/test/test_refs.py index ca582c2e9..ec4b5cc08 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -67,7 +67,7 @@ def test_lookup_reference(self): repo = self.repo # Raise KeyError ? - self.assertRaises(KeyError, repo.lookup_reference, 'foo') + self.assertRaises(KeyError, repo.lookup_reference, 'refs/foo') # Test a lookup reference = repo.lookup_reference('refs/heads/master') From 682fb1fd13b99c7cf86a342be6681ddedc8a9059 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 9 Oct 2012 11:07:10 +0200 Subject: [PATCH 0262/2237] merged create_reference and create_reference_symbolic and added force-Option To be more pythonic: merged methods create_reference and create_reference_symbolic and added force-Option. To make a new symbolic reference you have provide symbolic=True as an additional parameter. If force=True references will although be overridden. Otherwise an exception is raised if the references exists. Examples: # normal reference repo.create_reference('refs/heads/foo', repo.head.hex) # override reference with new value repo.create_reference('refs/heads/foo', repo.head.hex, force=True) # symbolic reference repo.create_reference('refs/tags/foo', 'refs/heads/master', symbolic = True) --- include/pygit2/repository.h | 3 +- src/pygit2/repository.c | 78 +++++++++++++++++++------------------ test/test_refs.py | 24 +++++++++--- 3 files changed, 60 insertions(+), 45 deletions(-) diff --git a/include/pygit2/repository.h b/include/pygit2/repository.h index 32780e0b4..0f6616afa 100644 --- a/include/pygit2/repository.h +++ b/include/pygit2/repository.h @@ -55,8 +55,7 @@ PyObject* Repository_create_commit(Repository *self, PyObject *args); PyObject* Repository_create_tag(Repository *self, PyObject *args); PyObject* Repository_listall_references(Repository *self, PyObject *args); PyObject* Repository_lookup_reference(Repository *self, PyObject *py_name); -PyObject* Repository_create_reference(Repository *self, PyObject *args); -PyObject* Repository_create_symbolic_reference(Repository *self, PyObject *args); +PyObject* Repository_create_reference(Repository *self, PyObject *args, PyObject* keywds); PyObject* Repository_packall_references(Repository *self, PyObject *args); PyObject* Repository_status(Repository *self, PyObject *args); PyObject* Repository_status_file(Repository *self, PyObject *value); diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 8ba37042e..3f3180598 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -641,53 +641,60 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) return wrap_reference(c_reference); } +PyDoc_STRVAR( + Repository_create_reference_doc, + "Create a new reference \"name\" which points to a object or another reference\n\n" + "Arguments: (name, source, force=False, symbolic=False)\n\n" + "With force=True references will be overridden. Otherwise an exception is raised" + "You can create either a normal reference or a symbolic one:\n" + " * normal reference: source has to be a valid sha hash\n" + " * symbolic reference: source has to be a valid existing reference name\n\n" + "Examples:\n" + " repo.create_reference('refs/heads/foo', repo.head.hex)\n" + " repo.create_reference('refs/tags/foo', 'refs/heads/master', symbolic = True)\n" +); + PyObject * -Repository_create_reference(Repository *self, PyObject *args) +Repository_create_reference(Repository *self, PyObject *args, PyObject* keywds) { - PyObject *py_oid; + PyObject *py_obj; git_reference *c_reference; - char *c_name; + char *c_name, *c_target; git_oid oid; - int err; + int err, symbolic = 0, force = 0; - /* 1- Get the C variables */ - if (!PyArg_ParseTuple(args, "sO", &c_name, &py_oid)) - return NULL; + static char *kwlist[] = {"name", "source", "force", "symbolic", NULL}; - err = py_str_to_git_oid_expand(self->repo, py_oid, &oid); - if (err < 0) - return Error_set(err); - - /* 2- Create the reference */ - err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid, 0); - if (err < 0) - return Error_set(err); + if (!PyArg_ParseTupleAndKeywords(args, keywds, "sO|ii", kwlist, + &c_name, &py_obj, &force, &symbolic)) + return NULL; - /* 3- Make an instance of Reference and return it */ - return wrap_reference(c_reference); -} + if(symbolic) { + #if PY_MAJOR_VERSION == 2 + c_target = PyString_AsString(py_obj); + #else + c_target = PyString_AsString(PyUnicode_AsASCIIString(py_obj)); + #endif + if(c_target == NULL) + return NULL; -PyObject * -Repository_create_symbolic_reference(Repository *self, PyObject *args) -{ - git_reference *c_reference; - char *c_name, *c_target; - int err; + err = git_reference_create_symbolic(&c_reference, self->repo, c_name, + c_target, force); + } else { + err = py_str_to_git_oid_expand(self->repo, py_obj, &oid); + if (err < 0) + return Error_set(err); - /* 1- Get the C variables */ - if (!PyArg_ParseTuple(args, "ss", &c_name, &c_target)) - return NULL; + err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid, force); + } - /* 2- Create the reference */ - err = git_reference_create_symbolic(&c_reference, self->repo, c_name, - c_target, 0); if (err < 0) return Error_set(err); - /* 3- Make an instance of Reference and return it */ return wrap_reference(c_reference); } + PyObject * Repository_packall_references(Repository *self, PyObject *args) { @@ -827,13 +834,8 @@ PyMethodDef Repository_methods[] = { METH_VARARGS, "Create a new blob from file"}, {"create_reference", (PyCFunction)Repository_create_reference, - METH_VARARGS, - "Create a new reference \"name\" that points to the object given by its " - "\"sha\"."}, - {"create_symbolic_reference", - (PyCFunction)Repository_create_symbolic_reference, METH_VARARGS, - "Create a new symbolic reference \"name\" that points to the reference\n" - "\"target\"."}, + METH_VARARGS|METH_KEYWORDS, + Repository_create_reference_doc}, {"packall_references", (PyCFunction)Repository_packall_references, METH_NOARGS, "Pack all the loose references in the repository."}, {"status", (PyCFunction)Repository_status, METH_NOARGS, "Reads the " diff --git a/test/test_refs.py b/test/test_refs.py index ec4b5cc08..56e918b82 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -49,8 +49,7 @@ def test_list_all_references(self): ['refs/heads/i18n', 'refs/heads/master']) # We add a symbolic reference - repo.create_symbolic_reference('refs/tags/version1', - 'refs/heads/master') + repo.create_reference('refs/tags/version1','refs/heads/master', symbolic=True) self.assertEqual(sorted(repo.listall_references()), ['refs/heads/i18n', 'refs/heads/master', 'refs/tags/version1']) @@ -145,7 +144,7 @@ def test_rename(self): def test_reload(self): name = 'refs/tags/version1' - ref = self.repo.create_symbolic_reference(name, "refs/heads/master") + ref = self.repo.create_reference(name, "refs/heads/master", symbolic=True) ref2 = self.repo.lookup_reference(name) ref.delete() self.assertEqual(ref2.name, name) @@ -176,12 +175,27 @@ def test_create_reference(self): reference = self.repo.lookup_reference('refs/tags/version1') self.assertEqual(reference.hex, LAST_COMMIT) + self.assertRaises(GitError, self.repo.create_reference, + 'refs/tags/version1', LAST_COMMIT) + + reference = self.repo.create_reference('refs/tags/version1', + LAST_COMMIT, force=True) + self.assertEqual(reference.hex, LAST_COMMIT) + def test_create_symbolic_reference(self): # We add a tag as a new symbolic reference that always points to # "refs/heads/master" - reference = self.repo.create_symbolic_reference('refs/tags/beta', - 'refs/heads/master') + reference = self.repo.create_reference('refs/tags/beta', + 'refs/heads/master', symbolic=True) + self.assertEqual(reference.type, GIT_REF_SYMBOLIC) + self.assertEqual(reference.target, 'refs/heads/master') + + self.assertRaises(GitError, self.repo.create_reference, + 'refs/tags/beta','refs/heads/master', symbolic=True) + + reference = self.repo.create_reference('refs/tags/beta', + 'refs/heads/master', force=True, symbolic=True) self.assertEqual(reference.type, GIT_REF_SYMBOLIC) self.assertEqual(reference.target, 'refs/heads/master') From e1acdb48bcd6327a4e841857b2a2fd5a0a317a9c Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 12 Oct 2012 11:51:04 +0200 Subject: [PATCH 0263/2237] use latest development of libgit2 - changed GitError to ValueError For already existing references a ValueError is raised (see sry/pygit2/error.c:41) --- src/pygit2/repository.c | 17 +++++++++-------- test/test_refs.py | 9 +++++++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 3f3180598..14393fab8 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -661,7 +661,7 @@ Repository_create_reference(Repository *self, PyObject *args, PyObject* keywds) git_reference *c_reference; char *c_name, *c_target; git_oid oid; - int err, symbolic = 0, force = 0; + int err = 0, symbolic = 0, force = 0; static char *kwlist[] = {"name", "source", "force", "symbolic", NULL}; @@ -669,7 +669,14 @@ Repository_create_reference(Repository *self, PyObject *args, PyObject* keywds) &c_name, &py_obj, &force, &symbolic)) return NULL; - if(symbolic) { + if(!symbolic) { + err = py_str_to_git_oid_expand(self->repo, py_obj, &oid); + if (err < 0) { + return Error_set(err); + } + + err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid, force); + } else { #if PY_MAJOR_VERSION == 2 c_target = PyString_AsString(py_obj); #else @@ -680,12 +687,6 @@ Repository_create_reference(Repository *self, PyObject *args, PyObject* keywds) err = git_reference_create_symbolic(&c_reference, self->repo, c_name, c_target, force); - } else { - err = py_str_to_git_oid_expand(self->repo, py_obj, &oid); - if (err < 0) - return Error_set(err); - - err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid, force); } if (err < 0) diff --git a/test/test_refs.py b/test/test_refs.py index 56e918b82..5ba77e1fb 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -175,9 +175,11 @@ def test_create_reference(self): reference = self.repo.lookup_reference('refs/tags/version1') self.assertEqual(reference.hex, LAST_COMMIT) - self.assertRaises(GitError, self.repo.create_reference, + # try to create existing reference + self.assertRaises(ValueError, self.repo.create_reference, 'refs/tags/version1', LAST_COMMIT) + # try to create existing reference with force reference = self.repo.create_reference('refs/tags/version1', LAST_COMMIT, force=True) self.assertEqual(reference.hex, LAST_COMMIT) @@ -191,9 +193,12 @@ def test_create_symbolic_reference(self): self.assertEqual(reference.type, GIT_REF_SYMBOLIC) self.assertEqual(reference.target, 'refs/heads/master') - self.assertRaises(GitError, self.repo.create_reference, + + # try to create existing symbolic reference + self.assertRaises(ValueError, self.repo.create_reference, 'refs/tags/beta','refs/heads/master', symbolic=True) + # try to create existing symbolic reference with force reference = self.repo.create_reference('refs/tags/beta', 'refs/heads/master', force=True, symbolic=True) self.assertEqual(reference.type, GIT_REF_SYMBOLIC) From 53cd040badc32893d35d7ac35505e4e4f5bfc3d0 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 25 Oct 2012 10:17:32 -0400 Subject: [PATCH 0264/2237] config: update Config_get_multivar_fn_wrapper to use git_config_entry *. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes: $ python setup.py build_ext --inplace ... src/pygit2/config.c: In function ‘Config_get_multivar’: src/pygit2/config.c:311:13: warning: passing argument 4 of ‘git_config_get_multivar’ from incompatible pointer type ../libgit2/include/git2/config.h:339:17: note: expected ‘int (*)(const struct git_config_entry *, void *)’ but argument is of type ‘int (*)(const char *, void *)’ error: command 'i686-pc-linux-gnu-gcc' failed with exit status 1 reported by wolftankk in issue #140. Signed-off-by: W. Trevor King --- src/pygit2/config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pygit2/config.c b/src/pygit2/config.c index d850ba8bc..786b7c47a 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -283,12 +283,12 @@ Config_add_file(Config *self, PyObject *args) } int -Config_get_multivar_fn_wrapper(const char *value, void *data) +Config_get_multivar_fn_wrapper(const git_config_entry *value, void *data) { PyObject *list = (PyObject *)data; PyObject *item = NULL; - if (!(item = PyUnicode_FromString(value))) + if (!(item = PyUnicode_FromString(value->value))) return -2; PyList_Append(list, item); From 86075d3f5006ada3f9c6f9f9b0f90a5491b842cd Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 25 Oct 2012 10:25:44 -0400 Subject: [PATCH 0265/2237] config: add force argument to Config_add_file. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes: src/pygit2/config.c: In function ‘Config_add_file’: src/pygit2/config.c:276:5: error: too few arguments to function ‘git_config_add_file_ondisk’ Also convert `int priority` -> `unsigned int level` to match current libgit2 header. Signed-off-by: W. Trevor King --- src/pygit2/config.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pygit2/config.c b/src/pygit2/config.c index 786b7c47a..823254ba2 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -268,12 +268,13 @@ Config_add_file(Config *self, PyObject *args) { int err; char *path; - int priority; + unsigned int level; + int force; - if (!PyArg_ParseTuple(args, "si", &path, &priority)) + if (!PyArg_ParseTuple(args, "sIi", &path, &level, &force)) return NULL; - err = git_config_add_file_ondisk(self->config, path, priority); + err = git_config_add_file_ondisk(self->config, path, level, force); if (err < 0) { Error_set_str(err, path); return NULL; From 396fbef69801a8a6fc4e286d79da7ab61fee92b3 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 25 Oct 2012 10:54:38 -0400 Subject: [PATCH 0266/2237] config: update Config_foreach_callback_wrapper to use git_config_entry *. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes: $ python setup.py build_ext --inplace src/pygit2/config.c: In function ‘Config_foreach’: src/pygit2/config.c:261:13: warning: passing argument 2 of ‘git_config_foreach’ from incompatible pointer type ../libgit2/include/git2/config.h:420:17: note: expected ‘int (*)(const struct git_config_entry *, void *)’ but argument is of type ‘int (*)(const char *, const char *, void *)’ Signed-off-by: W. Trevor King --- src/pygit2/config.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pygit2/config.c b/src/pygit2/config.c index 823254ba2..6f3bb9e32 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -214,8 +214,7 @@ Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) } int -Config_foreach_callback_wrapper(const char *c_name, const char *c_value, - void *c_payload) +Config_foreach_callback_wrapper(const git_config_entry *entry, void *c_payload) { PyObject *args = (PyObject *)c_payload; PyObject *py_callback = NULL; @@ -227,9 +226,9 @@ Config_foreach_callback_wrapper(const char *c_name, const char *c_value, return 0; if (py_payload) - args = Py_BuildValue("ssO", c_name, c_value, py_payload); + args = Py_BuildValue("ssO", entry->name, entry->value, py_payload); else - args = Py_BuildValue("ss", c_name, c_value); + args = Py_BuildValue("ss", entry->name, entry->value); if (!(py_result = PyObject_CallObject(py_callback,args))) return 0; From 01d2fdc508ce8404a50a7d2fe222ee2516bec987 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 25 Oct 2012 11:03:13 -0400 Subject: [PATCH 0267/2237] diff: add const to shared pointers in the diff API. This brings us into compliance with the libgit2 commit: commit bae957b95d59a840df72a725b06f00635471cfd8 Author: Russell Belfer Date: Tue Sep 25 16:31:46 2012 -0700 Add const to all shared pointers in diff API Signed-off-by: W. Trevor King --- src/pygit2/diff.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index 4fc44eb83..d01044224 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -42,8 +42,8 @@ extern PyTypeObject HunkType; static int diff_data_cb( void *cb_data, - git_diff_delta *delta, - git_diff_range *range, + const git_diff_delta *delta, + const git_diff_range *range, char line_origin, const char *content, size_t content_len) @@ -75,8 +75,8 @@ static int diff_data_cb( static int diff_hunk_cb( void *cb_data, - git_diff_delta *delta, - git_diff_range *range, + const git_diff_delta *delta, + const git_diff_range *range, const char *header, size_t header_len) { @@ -158,7 +158,8 @@ static int diff_hunk_cb( return 0; }; -static int diff_file_cb(void *cb_data, git_diff_delta *delta, float progress) +static int diff_file_cb(void *cb_data, const git_diff_delta *delta, + float progress) { PyObject *files, *file; @@ -201,8 +202,8 @@ Diff_changes(Diff *self) static int diff_print_cb( void *cb_data, - git_diff_delta *delta, - git_diff_range *range, + const git_diff_delta *delta, + const git_diff_range *range, char usage, const char *line, size_t line_len) From ebdb85e75453641dd94a78c5f164b3c169660b41 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 25 Oct 2012 13:23:18 -0400 Subject: [PATCH 0268/2237] index: remove read_tree() progress indicator This brings us into compliance with the libgit2 commit: commit 0ae81fc479bf3cf7ed31b3e3b070de7990102f1d Author: nulltoken Date: Wed Oct 17 15:30:22 2012 +0200 index: remove read_tree() progress indicator Signed-off-by: W. Trevor King --- src/pygit2/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index d0de48e7d..7dec03090 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -331,7 +331,7 @@ Index_read_tree(Index *self, PyObject *value) if (err < 0) return Error_set(err); - err = git_index_read_tree(self->index, tree, NULL); + err = git_index_read_tree(self->index, tree); if (err < 0) return Error_set(err); From 4660c9c149ac8c11edba7165f7d36925087c49a6 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 25 Oct 2012 13:52:15 -0400 Subject: [PATCH 0269/2237] config: extend Config.add_file() accept keyword arguments. This provides backward compatibility with older code that does not specify `force`. Signed-off-by: W. Trevor King --- include/pygit2/config.h | 2 +- src/pygit2/config.c | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/pygit2/config.h b/include/pygit2/config.h index b07849e2b..62dbc83ba 100644 --- a/include/pygit2/config.h +++ b/include/pygit2/config.h @@ -34,7 +34,7 @@ PyObject* Config_get_global_config(void); PyObject* Config_get_system_config(void); -PyObject* Config_add_file(Config *self, PyObject *args); +PyObject* Config_add_file(Config *self, PyObject *args, PyObject *kwds); PyObject* Config_getitem(Config *self, PyObject *key); PyObject* Config_foreach(Config *self, PyObject *args); PyObject* Config_get_multivar(Config *self, PyObject *args); diff --git a/src/pygit2/config.c b/src/pygit2/config.c index 6f3bb9e32..1c052a430 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -263,14 +263,17 @@ Config_foreach(Config *self, PyObject *args) } PyObject * -Config_add_file(Config *self, PyObject *args) +Config_add_file(Config *self, PyObject *args, PyObject *kwds) { + char *keywords[] = {"path", "level", "force", NULL}; int err; char *path; - unsigned int level; - int force; + unsigned int level = 0; + int force = 0; - if (!PyArg_ParseTuple(args, "sIi", &path, &level, &force)) + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "s|Ii", keywords, + &path, &level, &force)) return NULL; err = git_config_add_file_ondisk(self->config, path, level, force); @@ -353,7 +356,7 @@ PyMethodDef Config_methods[] = { "and value of each variable in the config backend, and an optional " "payload passed to this method. As soon as one of the callbacks returns " "an integer other than 0, this function returns that value."}, - {"add_file", (PyCFunction)Config_add_file, METH_VARARGS, + {"add_file", (PyCFunction)Config_add_file, METH_VARARGS | METH_KEYWORDS, "Add a config file instance to an existing config."}, {"get_multivar", (PyCFunction)Config_get_multivar, METH_VARARGS, "Get each value of a multivar ''name'' as a list. The optional ''regex'' " From cd643a3d70462cdb72917c9f735aff591a2c5cb9 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 25 Oct 2012 14:14:39 -0400 Subject: [PATCH 0270/2237] config: check for Py_BuildValue() errors in Config_foreach_callback_wrapper(). Signed-off-by: W. Trevor King --- src/pygit2/config.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pygit2/config.c b/src/pygit2/config.c index 1c052a430..f50a5e42c 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -229,6 +229,8 @@ Config_foreach_callback_wrapper(const git_config_entry *entry, void *c_payload) args = Py_BuildValue("ssO", entry->name, entry->value, py_payload); else args = Py_BuildValue("ss", entry->name, entry->value); + if (!args) + return 0; if (!(py_result = PyObject_CallObject(py_callback,args))) return 0; From cce9f799058baf500e67c802d7566b4e7e616fb9 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 25 Oct 2012 14:15:10 -0400 Subject: [PATCH 0271/2237] config: return -1 on error in Config_foreach_callback_wrapper(). Config_foreach_callback_wrapper is supposed to return 0 on success. From the docstring: As soon as one of the callbacks returns an integer other than 0, this function returns that value. If we've already had an exception, we want to bail immediately and raise the exception. Signed-off-by: W. Trevor King --- src/pygit2/config.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pygit2/config.c b/src/pygit2/config.c index f50a5e42c..cd2134299 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -223,20 +223,20 @@ Config_foreach_callback_wrapper(const git_config_entry *entry, void *c_payload) int c_result; if (!PyArg_ParseTuple(args, "O|O", &py_callback, &py_payload)) - return 0; + return -1; if (py_payload) args = Py_BuildValue("ssO", entry->name, entry->value, py_payload); else args = Py_BuildValue("ss", entry->name, entry->value); if (!args) - return 0; + return -1; if (!(py_result = PyObject_CallObject(py_callback,args))) - return 0; + return -1; if (!(c_result = PyLong_AsLong(py_result))) - return 0; + return -1; return c_result; } From e13a42a6e5d3a80c7ceab01aa649026be91d6841 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 25 Oct 2012 14:22:47 -0400 Subject: [PATCH 0272/2237] config: fix return conversion in Config_foreach_callback_wrapper() PyLong_AsLong() returns -1 on error. The previous implementation returned -1 when py_result was 0, which killed every .foreach() iteration after the first callback. We could skip the if clause and just return the result of PyLong_AsLong(), but keeping the if clause makes it more obvious that we *did* think about handling errors from PyLong_AsLong(). Signed-off-by: W. Trevor King --- src/pygit2/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/config.c b/src/pygit2/config.c index cd2134299..0cbee9262 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -235,7 +235,7 @@ Config_foreach_callback_wrapper(const git_config_entry *entry, void *c_payload) if (!(py_result = PyObject_CallObject(py_callback,args))) return -1; - if (!(c_result = PyLong_AsLong(py_result))) + if (c_result = PyLong_AsLong(py_result) == -1) return -1; return c_result; From c36bbf518c850f2c2f46bd03c81801b593ab0d9f Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 25 Oct 2012 14:37:39 -0400 Subject: [PATCH 0273/2237] test_config: tell Nose that foreach_test_wrapper() is not a test Signed-off-by: W. Trevor King --- test/test_config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_config.py b/test/test_config.py index c2bd9f21a..b4619225d 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -39,6 +39,7 @@ def foreach_test_wrapper(key, name, lst): lst[key] = name return 0 +foreach_test_wrapper.__test__ = False class ConfigTest(utils.RepoTestCase): From 43a798bbdbe29a30fbbf5abfe1ba9293390a9ce2 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 25 Oct 2012 15:47:02 -0400 Subject: [PATCH 0274/2237] setup.py: decode README.rst to unicode. Signed-off-by: W. Trevor King --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7a8dce883..59f5bc310 100644 --- a/setup.py +++ b/setup.py @@ -30,6 +30,7 @@ from __future__ import print_function +import codecs import os from subprocess import Popen, PIPE import sys @@ -162,7 +163,7 @@ def get_file_list(self): "Topic :: Software Development :: Version Control"] -with open('README.rst') as readme: +with codecs.open('README.rst', 'r', 'utf-8') as readme: long_description = readme.read() setup(name='pygit2', From 0b537bf780e2ae37abc39343296e81d815227c9f Mon Sep 17 00:00:00 2001 From: delanne Date: Wed, 31 Oct 2012 12:11:18 +0100 Subject: [PATCH 0275/2237] - add option flags for tree.diff - add a testcase test_diff_tree_opts - update testrepo.git - update existing testcases --- src/pygit2/tree.c | 2 +- .../18/e2d2e9db075f9eb43bcb2daa65a2867d29a15e | Bin 0 -> 99 bytes .../77/88019febe4f40259a64c529a9aed561e64ddbd | Bin 0 -> 30 bytes .../cc/ca47fbb26183e71a7a46d165299b84e2e6c0b3 | 2 ++ test/data/testrepo.git/refs/heads/master | 2 +- test/test_diff.py | 17 ++++++++++++++++- test/test_repository.py | 4 ++-- 7 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 test/data/testrepo.git/objects/18/e2d2e9db075f9eb43bcb2daa65a2867d29a15e create mode 100644 test/data/testrepo.git/objects/77/88019febe4f40259a64c529a9aed561e64ddbd create mode 100644 test/data/testrepo.git/objects/cc/ca47fbb26183e71a7a46d165299b84e2e6c0b3 diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index 76cefcb37..0127c08b3 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -274,7 +274,7 @@ Tree_diff_tree(Tree *self, PyObject *args) Diff *py_diff; PyObject *py_obj = NULL; - if (!PyArg_ParseTuple(args, "|O", &py_obj)) + if (!PyArg_ParseTuple(args, "|Oi", &py_obj, &opts.flags)) return NULL; if (py_obj == NULL) { diff --git a/test/data/testrepo.git/objects/18/e2d2e9db075f9eb43bcb2daa65a2867d29a15e b/test/data/testrepo.git/objects/18/e2d2e9db075f9eb43bcb2daa65a2867d29a15e new file mode 100644 index 0000000000000000000000000000000000000000..4e796d7e7fc30a40b99623d299a5b12da2bffe7e GIT binary patch literal 99 zcmV-p0G$7L0V^p=O;xZkV=y!@Ff%bxNMtDQV4VN@$rq-`Wj;Z(X1xuQOS!ujDw)JE z{hH{*J?b_NC%uX?M4s=oza1rQW?}#Y3dszK{x=tMdViC6HM#%)0lr$PkRP)y00415 FC5IWEDl-58 literal 0 HcmV?d00001 diff --git a/test/data/testrepo.git/objects/77/88019febe4f40259a64c529a9aed561e64ddbd b/test/data/testrepo.git/objects/77/88019febe4f40259a64c529a9aed561e64ddbd new file mode 100644 index 0000000000000000000000000000000000000000..189c2d58d7461124479d04d4d98f0db68abf31ea GIT binary patch literal 30 mcmb a"I֓咭06Ilb㡮b.}Iވ9ss0|YXN[}ZmS \ No newline at end of file diff --git a/test/data/testrepo.git/refs/heads/master b/test/data/testrepo.git/refs/heads/master index e863952d3..101232d36 100644 --- a/test/data/testrepo.git/refs/heads/master +++ b/test/data/testrepo.git/refs/heads/master @@ -1 +1 @@ -2cdae28389c059815e951d0bb9eed6533f61a46b +ccca47fbb26183e71a7a46d165299b84e2e6c0b3 diff --git a/test/test_diff.py b/test/test_diff.py index 5f61b0948..0aaa8b9ee 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -30,13 +30,14 @@ from __future__ import absolute_import from __future__ import unicode_literals import unittest - +import pygit2 from . import utils COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10' COMMIT_SHA1_2 = 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c' COMMIT_SHA1_3 = '2cdae28389c059815e951d0bb9eed6533f61a46b' +COMMIT_SHA1_4 = 'ccca47fbb26183e71a7a46d165299b84e2e6c0b3' PATCH = b"""diff --git a/a b/a index 7f129fd..af431f2 100644 @@ -133,6 +134,20 @@ def test_diff_tree(self): #self.assertEqual(hunk.data[0][0], b'a contents 2\n') #self.assertEqual(hunk.data[1][0], b'a contents\n') + def test_diff_tree_opts(self): + commit_c = self.repo[COMMIT_SHA1_3] + commit_d = self.repo[COMMIT_SHA1_4] + + for opt in [pygit2.GIT_DIFF_IGNORE_WHITESPACE, + pygit2.GIT_DIFF_IGNORE_WHITESPACE_EOL]: + diff = commit_c.tree.diff(commit_d.tree, pygit2.GIT_DIFF_IGNORE_WHITESPACE) + self.assertTrue(diff is not None) + self.assertEqual(0, len(diff.changes.get('hunks', list()))) + + diff = commit_c.tree.diff(commit_d.tree) + self.assertTrue(diff is not None) + self.assertEqual(1, len(diff.changes.get('hunks', list()))) + def test_diff_merge(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] diff --git a/test/test_repository.py b/test/test_repository.py index fa3a932bc..5f3832255 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -39,8 +39,8 @@ from . import utils -HEAD_SHA = '2cdae28389c059815e951d0bb9eed6533f61a46b' -PARENT_SHA = '5fe808e8953c12735680c257f56600cb0de44b10' # HEAD^ +HEAD_SHA = 'ccca47fbb26183e71a7a46d165299b84e2e6c0b3' +PARENT_SHA = '2cdae28389c059815e951d0bb9eed6533f61a46b' # HEAD^ A_HEX_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' A_BIN_SHA = binascii.unhexlify(A_HEX_SHA.encode('ascii')) From f1a72fb2e8a970c80270080c81d6538977f1e8d6 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 1 Nov 2012 14:30:57 +0100 Subject: [PATCH 0276/2237] fixed index_add and index_get for current development branch of libgit2 --- src/pygit2/index.c | 33 ++++++++------------------------- test/test_index.py | 4 ++-- 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 7dec03090..a43b6b10a 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -84,12 +84,12 @@ Index_add(Index *self, PyObject *args) { int err; const char *path; - int stage=0; + git_index_entry* entry; - if (!PyArg_ParseTuple(args, "s|i", &path, &stage)) + if (!PyArg_ParseTuple(args, "s", &path)) return NULL; - err = git_index_add(self->index, path, stage); + err = git_index_add_from_workdir(self->index, path); if (err < 0) return Error_set_str(err, path); @@ -281,7 +281,7 @@ Index_getitem(Index *self, PyObject *value) if (idx == -1) return NULL; - index_entry = git_index_get(self->index, idx); + index_entry = git_index_get_byindex(self->index, idx); if (!index_entry) { PyErr_SetObject(PyExc_KeyError, value); return NULL; @@ -293,26 +293,9 @@ Index_getitem(Index *self, PyObject *value) int Index_setitem(Index *self, PyObject *key, PyObject *value) { - int err; - int idx; - - if (value) { - PyErr_SetString(PyExc_NotImplementedError, - "set item on index not yet implemented"); - return -1; - } - - idx = Index_get_position(self, key); - if (idx == -1) - return -1; - - err = git_index_remove(self->index, idx); - if (err < 0) { - Error_set(err); - return -1; - } - - return 0; + PyErr_SetString(PyExc_NotImplementedError, + "set item on index not yet implemented"); + return -1; } PyObject * @@ -447,7 +430,7 @@ IndexIter_iternext(IndexIter *self) { git_index_entry *index_entry; - index_entry = git_index_get(self->owner->index, self->i); + index_entry = git_index_get_byindex(self->owner->index, self->i); if (!index_entry) return NULL; diff --git a/test/test_index.py b/test/test_index.py index da7784063..a4123ecc6 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -80,7 +80,7 @@ def test_clear(self): def test_write(self): index = self.repo.index - index.add('bye.txt', 0) + index.add('bye.txt') index.write() index.clear() @@ -133,7 +133,7 @@ def test_bare_index(self): self.assertEqual([x.hex for x in index], [x.hex for x in self.repo.index]) - self.assertRaises(pygit2.GitError, lambda: index.add('bye.txt', 0)) + self.assertRaises(pygit2.GitError, lambda: index.add('bye.txt')) if __name__ == '__main__': unittest.main() From b961b9218fc908b5a382002cf2cd56a914b6dc58 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 1 Nov 2012 15:02:17 +0100 Subject: [PATCH 0277/2237] fixed warning gcc -Wparentheses warning --- src/pygit2/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/config.c b/src/pygit2/config.c index 0cbee9262..0983bacc7 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -235,7 +235,7 @@ Config_foreach_callback_wrapper(const git_config_entry *entry, void *c_payload) if (!(py_result = PyObject_CallObject(py_callback,args))) return -1; - if (c_result = PyLong_AsLong(py_result) == -1) + if ((c_result = PyLong_AsLong(py_result) == -1)) return -1; return c_result; From 971fc2f101efd700bf06cb0484e1e675c510e112 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 1 Nov 2012 14:47:42 +0100 Subject: [PATCH 0278/2237] fixed config test for config.set_multivar config files have different priorities and only the highest one gets evaluated for set_multivar --- src/pygit2/index.c | 1 - test/test_config.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index a43b6b10a..b200b0e73 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -84,7 +84,6 @@ Index_add(Index *self, PyObject *args) { int err; const char *path; - git_index_entry* entry; if (!PyArg_ParseTuple(args, "s", &path)) return NULL; diff --git a/test/test_config.py b/test/test_config.py index b4619225d..8656436cd 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -151,7 +151,7 @@ def test_write(self): new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n") new_file.close() - config.add_file(config_filename, 0) + config.add_file(config_filename, 5) self.assertTrue('this.that' in config) config.set_multivar('this.that', '^.*beer', 'fool') l = config.get_multivar('this.that', 'fool') From 0d2a3a612316e34b471d92a0c21b8a79a0f956a4 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 1 Nov 2012 15:35:53 +0100 Subject: [PATCH 0279/2237] added Repository.is_detached A repository's HEAD is detached when it points directly to a commit instead of branch. You can use repo.is_detached to check this. --- src/pygit2/repository.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 8ba37042e..bd346ce9c 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -185,6 +185,22 @@ Repository_head(Repository *self) } +PyDoc_STRVAR( + Repository_is_detached_doc, + "A repository's HEAD is detached when it points directly to a commit\n" + "instead of a branch.\n" +); + +PyObject * +Repository_is_detached(Repository *self) +{ + if(git_repository_head_detached(self->repo) > 0) + return Py_True; + + return Py_False; +} + + PyObject * Repository_getitem(Repository *self, PyObject *value) { @@ -852,6 +868,8 @@ PyGetSetDef Repository_getseters[] = { "The normalized path to the git repository.", NULL}, {"head", (getter)Repository_head, NULL, "Current head reference of the repository.", NULL}, + {"is_detached", (getter)Repository_is_detached, NULL, + Repository_is_detached_doc}, {"config", (getter)Repository_get_config, NULL, "Get the configuration file for this repository.\n\n" "If a configuration file has not been set, the default " From 1bbb7b549f3c06aaeea87edbf138b0a6738753b7 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 1 Nov 2012 16:17:29 +0100 Subject: [PATCH 0280/2237] Added empty test repository --- test/data/emptyrepo.tar | Bin 0 -> 30720 bytes test/utils.py | 5 +++++ 2 files changed, 5 insertions(+) create mode 100644 test/data/emptyrepo.tar diff --git a/test/data/emptyrepo.tar b/test/data/emptyrepo.tar new file mode 100644 index 0000000000000000000000000000000000000000..8584ee7a1f2ca2c341e266e7483c8d16e07293a6 GIT binary patch literal 30720 zcmeHQ`*+h;w&qv+uQ+vbn>dZ1d9`5@CWOwEdpqr26z;6MAb@Pkv5GAjNhV}~`P=XJ z?R}0cD^5bdfv#R>0kNg?*k`|A68>nMp2z+;YTUWCpC*3xc6RRIS8H#l$=}V^_O|@y zXZ+l0wVT`9JI&o)d~UYd?Y;ILvvX?@iq6^(`2!HaC7=^@9b_b>Hih!AB6qIS%jO@ ze`lw)x1|49r2p9KzFL?P_!Rm#m-hclkpE6W|97LXADk|DXVQOH{k^&VUu$c-;QwuJ zwOe;gbHQ-TUGD$6|M#>T#r{dT#1J3}(kMRfNAbu@-@w}qqRPPtq9pUN32kyd?GG@|=;&OMgZT6y(1XdtrCrFE#+Yx&ODl z*KRKT|1ZIB1^V~=q#Fn06sCW1yI-gOTdh_>|L<%u|F`u27Dt22R(cYK-pKF41UUci zuJL<8YSKZF7&?AtEyomVCXEbd;J%-mR9{+*%P{=MzFv_3h30+S$v;8=qY$vv{|v$MUujQ2PfV>AL9xgDV7Z{y zkE79n6o*7P*vsV=^K%elMmq3P!x~y@&VqDc)>hUGvjg2|H1fkVsbl2dqS$z597ilJ zm{F9Z!*dga=6F-gNIx15qq879HD}Re*fSj;qfeu#XA(a(Q6DSVUCr1_5uSvJS)YV< z2QD!(LEoH56LaQ;FkO*BwN9>A_c3{+{xtGV_~GA6GgV-Qg8UDn=+$DKf9&@O`oGQi ze_8)65bGAc&ZYkt-XLVSMhOej+9)}#C*Ej0{6v#*4gK3&#r#hj-2jOHmj3@O5WKMD z$~TQp5H^xQc?AmcD1@dJN*ihdqro5*s$~h$*UAp$;TdpkJc$w9_yz$DPGX}QwNODioh{K0W zQ5E~Xqfugy*$?7G5l(|^q6wonqO@|1a?UV7L4Usj(+Lv|#CgD0eCoiqu;LhrkK#`c zW;xr+=Tc*=hFL<>UeNEuGvEn4qDe%-1;;yDU+i zf+(0z6asb<+wXcb-r%5KtCd1hpD=28?hg#Mek(PrPK{_v{LRcpLaa+9Q6A zdk#~^>-9h$^ILEl`n_7z@7KVm==Z_60^q`xT`$BkNg51?^hU@sAS;;)6v3Yu+7T{{ z^-_YK?}1=l4{%pj+ncLf6=0-n6w#Yid@`mnNn#PTu^$gjEvR?ASEdF4){@5a#)@fd z8hltWD~*)~y_v@IjCc)GuNz#x3n>Jq*RV;eV(#80k8HkAp~8nKyI`M{OZNc=*W@-v z$ReilEUcKic}ZEdbnSXb{q-&o_+@#eq!0nJZ@)FG!f)SwXAV#t$^m1{MwL+ciPt4) zzx{guV0E4Azd?u9-yc4HarETzPrp8T@$kWqzdSbY!Ex9u6a+k5uWuYatJXJG8#6T8 zH_uv)@fweI3ZrCduM<;wzKW$QiiP*@jgQtXe35I1hjMx8<`vZZrC;aTf7NK8ZTH=# z*Vz9Sd;Vto|Mqsac9!rVm4Xl3zE$79j6RLQP_=>&?Z| zIFstUi>Wm?$v{Fx|1H#t*Z^mgv4k~AULPSMv~)6INa=;f8zc0D)`OCC28#NODF1#408AF>r0K;P8zPX$da{*!!A3(JE|xh1UE#~3f{ zZXQHDv?Om4V0+empzZ5;Fz&Ea47^YM(2u?0rs=4!;M}{M$B(t#;YT#$z$Wc8pb1hv z=GWBkfKF_&^H#fF%CeQ|O~&wdNaz_eBBmaxh%&kpQd6m*L!koGM2oWad}idnu@>!p zxddktF4KE634_{@InpiN!Dq#O)Lqx;|K@IMcUJ$S7o7FKW&C%WnqSxcSpAQFprC(l z&Y10k1B^(Anj@ljtV#^Z0YV9{ddfJ4sY5DYzcxY~GPY%WH}L_cCIs1T1y5sBqb+f)g-mI&?EG0A3S{2;AxTuWk)emfwK&i@Ye5MIcW7= z>$}S8Un?KV`(HsD%$5Hs2^Zo6UMK&1%|iblZgJ_3m^hOn{O3`NYErBu65YhPbNdSdpW@EHb|*=h5=Kr1{qOyjl)5**ABRA znaV>qK47-8jCfmPuhkVsU+pqb5SB{_yMyjQCD(wu+OCv+X)D)zlAuh^M;$z#*#)ol z4SSSdBH*HLg&4kR?yi_K)CbU*6cJjGGoFmoDYkols+mpBEcb&fRVzD4t~Lk2y?c#~ zYPp0&CVySMg%-=}GTNm*e*wV&4vB(j8#!iWR7Bviog>E#qkwGawa0y?+O-9j*>U8W zQ}gS8(a%nKcLZ6r*Kj`VISVmrkCf$L9 z$PTk-W^js~0E6z)pKyyQb19Vf%E0-HAbg=;4}@9};TpIjg=m0n)AalR86ZivKi6Ry zNG~(KsQn8UXV$7%7Rh32dk%&l^MSbwZf1J`kTr_lu!c~txYJlB=}PuUm>svX{0C@- zRYR1ypqG%|AdlqE4wV$=j!p)@Cv~DElCA?A!jSzC4`ou$L`f75&tGuDi&g+&^mAV`pn*EqwfWWCRTJ#IO3%eobZkb3a__ADb zYv(DaV<6Td+|zVOj&x3gOvbe|s_}Ud&Ex61pqI^!+>Br)RT)-X`NWDV@ON~ttDH;NGGvelzUfB&DS&6B(KI?CnDFKxIHEODyq#yp~^=^Qf& z$(=^uM^VNuv;H4B(5qN*|sIHC=>o_&CV!NHifN>5{W5&V|9k?TXW{EwdyV=K!<}K>7{c_oCm|w## zTdDH9hyw!!%FIl~9(*(VBwCQ#jwMU0>!r2}lj5Cv@C4Ra>W?J&LvF4em0L+$PgeN1H1X%e7KY-%wv99Quq8)4#n z-;0L^0?a2Z-qe%lX$NcDk%awq^J8(E- z6xV@-(C@MlB0d+3VoxyQ&OD0mB?JZ_zVeZAMxR>j+$NI*?5k0<{S#VKObt8P+*!qI z0+KB==-*R=18`;+ z5BsTu;kLC-BnvlXED1BGtEvfAdI1}60&hsDcy=CLpC_Hf(g%T?q5nif%(l3P(*n zCz(A*un~{t&j`*!irHFGdjgrNB0=^c($StFf(^1$epT+l7gEpwYl||2hYA>!nC=wl z!a#*9r*8U_m=rfwzIEn0^OL*1eD9=qfH!v_Ul-pyu4BIS!9IeJ%Yre6AOj!e()Ab-ijnM2v+c+ zH>NsC=G>P#{Q%k!fi*;YBcEUD~sEUQuj zSWq%J$ZCjm*kYd<*M-Z6L$;R;nMuCMl4?-|J&SZnhM$doK2$3Z-?ZaCq$Qj0bH*#i3O* z(57X0PI%Wxy)2V3N2b_@PV2l&#k)Q;pvuJABlJiy6m$K<;GZPzAuU@kOF6uS78#B^ zj-v!V8C*3uP$T62jpN>e>)yV;1J&GdjL?Jb9!MbSP{XJ932UH^heuDg?L~1hBqQ1XOG|r)8G!OxkCuA zRRzSOKowZzDbflv2&tHFkPIR!E9OLMdn@Lb2v*Q-Ezc?;?TZMj`^pF7dNBihMm5M} z3MjBU@BqFBTV94N0&pn8LbOneYb~H5^(_Ia_W923 zBW9uzvch|O$PCCkI5J2Yt~!oqr!SIp zN;Ed}DdEbGaU8|_<_IZY7-iuX>64q~7kTbL^1x^lMmAWQg&)(OW`v^6=3qSZ>5mYs z&&mtKP-c*2ju2V{Ev6o9P-1yExv+!yNlFkr0sx-Fj$ZFgkT~he9tEI}h#?4O?Otxj zhYAe?UTwT##3XeEY4XdWEVm{Ol|ifZ`U&WKx?fn~M-*uM)^bh8 zA+_39=#07AZvI*B&h`KFJx&Yp{fMWl>;KKYCSLYA>;LVd0bp7G{}Md*vHo9Y7=QkmD3V+rydW6#fe!^1Up(rWeHuv2 zJ6sIOtzxLGlXo=C{c{+~zQ^J@?xSlm;&V(Lwuk3o(cn1s*a&eIBr=Yw6hrB7=tD_U zFu2Jl1A+Itp|SINAmEO8c-{}$H^l!kDF6<%*Iv@-cx6mIcX9y;3jWHoYgYMVkFTTQ zn=B3mJwH6xBEUz1!oqUOC%^Q>Ajt)oup$tnr&pipa@K%LK(9cbEg>AhQM84a=;y79oA?__OYr@~j;c2*)mhu23J3Hqc?EqJ>e4(A zzGT0S`2O~3PZ4Phlla7eQLh*}?RFx((5a`SnOv=`Clj`f%fnu1iIvR^)}NhhR-5K8 zIM$*0i=GD`^e~6$q}OexUI%u9GsyM;i;0SYPrauSIjOBo!J}0d&N{?!Au?5iJ4}& zZeli2f*#`&potTw&5KwVMTfP55@G>YgTAo4|Q&QrRtuj%uSSLoyx_R7NOey z9?x~KVMmKw52}J0SCz{s zxpZvcw(}QLf{3y_8`LuzQGDVO__ki=gTn_E4*i%PX_#Ux=~$W%tZ<1ObL*`aDL^~W zy6|r&BeZeQHXgXCmY9<@39ET{EdkoMi+~}=h+b3QX+_txg2SQn zq(mN?!JCpt@n+cqixyC!H*o!0L_A<&oDP8+)#@csVrvDlNY^~UGGU*A5{ZpM zcFbvOb7%^gv65>K%99p&UvpFN0vTK2(^irkmNQ=Xhqx^_z(H=2`AE?BU(WKlbqX?; zbp~fDjbN3^YEkn_hm}qF%wyzZwjC(!y2DMF_G;+oa9Z&YIc2Z6W+*ui-W;e=VZ!t`8X|7^Hk>K*@c}9+7|}ITe#Fz4 zF}A7kU35zg_4zzfkbiO1KY!KP6|R$i+%L%gc60gvf6e0Xw!Y1yKjWQ+{pT+L{O0uE zX>Kjw|9x8|pILVv{WTJr8TMIUK7szN<@+B$3+2nnOAIVAu*AR;14|5CiGlwEWVqD3 literal 0 HcmV?d00001 diff --git a/test/utils.py b/test/utils.py index 56f84ea80..36eb23882 100644 --- a/test/utils.py +++ b/test/utils.py @@ -128,3 +128,8 @@ def setUp(self): class DirtyRepoTestCase(RepoTestCase): repo_dir = 'dirtyrepo' + + +class EmptyRepoTestCase(RepoTestCase): + + repo_dir = 'emptyrepo' From 7dbd08edc2fbd5eb5039a6e25be67441db11da05 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 1 Nov 2012 16:17:51 +0100 Subject: [PATCH 0281/2237] added in Repository: is_empty, is_bare, head_is_orphaned and head_is_detached --- src/pygit2/repository.c | 60 ++++++++++++++++++++++++++++++++++++++--- test/test_repository.py | 27 +++++++++++++++++++ 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index bd346ce9c..61828973c 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -186,13 +186,13 @@ Repository_head(Repository *self) PyDoc_STRVAR( - Repository_is_detached_doc, + Repository_head_is_detached_doc, "A repository's HEAD is detached when it points directly to a commit\n" "instead of a branch.\n" ); PyObject * -Repository_is_detached(Repository *self) +Repository_head_is_detached(Repository *self) { if(git_repository_head_detached(self->repo) > 0) return Py_True; @@ -201,6 +201,52 @@ Repository_is_detached(Repository *self) } +PyDoc_STRVAR( + Repository_head_is_orphaned_doc, + "An orphan branch is one named from HEAD but which doesn't exist in\n" + "the refs namespace, because it doesn't have any commit to point to.\n" +); + +PyObject * +Repository_head_is_orphaned(Repository *self) +{ + if(git_repository_head_orphan(self->repo) > 0) + return Py_True; + + return Py_False; +} + + +PyDoc_STRVAR( + Repository_is_empty_doc, + "Check if a repository is empty\n" +); + +PyObject * +Repository_is_empty(Repository *self) +{ + if(git_repository_is_empty(self->repo) > 0) + return Py_True; + + return Py_False; +} + + +PyDoc_STRVAR( + Repository_is_bare_doc, + "Check if a repository is a bare repository.\n" +); + +PyObject * +Repository_is_bare(Repository *self) +{ + if(git_repository_is_bare(self->repo) > 0) + return Py_True; + + return Py_False; +} + + PyObject * Repository_getitem(Repository *self, PyObject *value) { @@ -868,8 +914,14 @@ PyGetSetDef Repository_getseters[] = { "The normalized path to the git repository.", NULL}, {"head", (getter)Repository_head, NULL, "Current head reference of the repository.", NULL}, - {"is_detached", (getter)Repository_is_detached, NULL, - Repository_is_detached_doc}, + {"head_is_detached", (getter)Repository_head_is_detached, NULL, + Repository_head_is_detached_doc}, + {"head_is_orphaned", (getter)Repository_head_is_orphaned, NULL, + Repository_head_is_orphaned_doc}, + {"is_empty", (getter)Repository_is_empty, NULL, + Repository_is_empty_doc}, + {"is_bare", (getter)Repository_is_bare, NULL, + Repository_is_bare_doc}, {"config", (getter)Repository_get_config, NULL, "Get the configuration file for this repository.\n\n" "If a configuration file has not been set, the default " diff --git a/test/test_repository.py b/test/test_repository.py index fa3a932bc..b3b71b64c 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -47,10 +47,18 @@ class RepositoryTest(utils.BareRepoTestCase): + def test_is_empty(self): + self.assertFalse(self.repo.is_empty) + + def test_is_bare(self): + self.assertTrue(self.repo.is_bare) + def test_head(self): head = self.repo.head self.assertEqual(HEAD_SHA, head.hex) self.assertEqual(type(head), Commit) + self.assertFalse(self.repo.head_is_orphaned) + self.assertFalse(self.repo.head_is_detached) def test_read(self): self.assertRaises(TypeError, self.repo.read, 123) @@ -135,6 +143,12 @@ def test_revparse_single(self): class RepositoryTest_II(utils.RepoTestCase): + def test_is_empty(self): + self.assertFalse(self.repo.is_empty) + + def test_is_bare(self): + self.assertFalse(self.repo.is_bare) + def test_get_path(self): directory = realpath(self.repo.path) expected = realpath(join(self._temp_dir, 'testrepo', '.git')) @@ -163,5 +177,18 @@ def test_discover_repo(self): os.makedirs(subdir) self.assertEqual(repo.path, discover_repository(subdir)) +class EmptyRepositoryTest(utils.EmptyRepoTestCase): + + def test_is_empty(self): + self.assertTrue(self.repo.is_empty) + + def test_is_base(self): + self.assertFalse(self.repo.is_bare) + + def test_head(self): + self.assertTrue(self.repo.head_is_orphaned) + self.assertFalse(self.repo.head_is_detached) + + if __name__ == '__main__': unittest.main() From 225f7bd1758c25825302e61527c4472241bf5504 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 4 Nov 2012 01:05:04 +0100 Subject: [PATCH 0282/2237] added Index_remove --- src/pygit2/index.c | 33 ++++++++++++++++++++++++++++++--- test/test_index.py | 8 ++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index b200b0e73..812e2774a 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -289,12 +289,37 @@ Index_getitem(Index *self, PyObject *value) return wrap_index_entry(index_entry, self); } +PyObject * +Index_remove(Index *self, PyObject *args) +{ + int err; + const char *path; + + if (!PyArg_ParseTuple(args, "s", &path)) + return NULL; + + err = git_index_remove(self->index, path, 0); + if (err < 0) { + Error_set(err); + return NULL; + } + + Py_RETURN_NONE; +} + int Index_setitem(Index *self, PyObject *key, PyObject *value) { - PyErr_SetString(PyExc_NotImplementedError, - "set item on index not yet implemented"); - return -1; + if (value != NULL) { + PyErr_SetString(PyExc_NotImplementedError, + "set item on index not yet implemented"); + return -1; + } + + if(Index_remove(self, Py_BuildValue("(N)", key)) == NULL) + return -1; + + return 0; } PyObject * @@ -336,6 +361,8 @@ Index_write_tree(Index *self) PyMethodDef Index_methods[] = { {"add", (PyCFunction)Index_add, METH_VARARGS, "Add or update an index entry from a file in disk."}, + {"remove", (PyCFunction)Index_remove, METH_VARARGS, + "Removes an entry from index."}, {"clear", (PyCFunction)Index_clear, METH_NOARGS, "Clear the contents (all the entries) of an index object."}, {"diff", (PyCFunction)Index_diff_tree, METH_VARARGS, diff --git a/test/test_index.py b/test/test_index.py index a4123ecc6..8950cbcc0 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -135,5 +135,13 @@ def test_bare_index(self): self.assertRaises(pygit2.GitError, lambda: index.add('bye.txt')) + def test_del(self): + index = self.repo.index + del index['hello.txt'] + + def test_remove(self): + index = self.repo.index + index.remove('hello.txt') + if __name__ == '__main__': unittest.main() From 929b7752418f1f1f2d5048b5bc1f33d7bb055f2d Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 4 Nov 2012 01:05:04 +0100 Subject: [PATCH 0283/2237] added Index_remove --- src/pygit2/index.c | 33 ++++++++++++++++++++++++++++++--- test/test_index.py | 8 ++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index b200b0e73..812e2774a 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -289,12 +289,37 @@ Index_getitem(Index *self, PyObject *value) return wrap_index_entry(index_entry, self); } +PyObject * +Index_remove(Index *self, PyObject *args) +{ + int err; + const char *path; + + if (!PyArg_ParseTuple(args, "s", &path)) + return NULL; + + err = git_index_remove(self->index, path, 0); + if (err < 0) { + Error_set(err); + return NULL; + } + + Py_RETURN_NONE; +} + int Index_setitem(Index *self, PyObject *key, PyObject *value) { - PyErr_SetString(PyExc_NotImplementedError, - "set item on index not yet implemented"); - return -1; + if (value != NULL) { + PyErr_SetString(PyExc_NotImplementedError, + "set item on index not yet implemented"); + return -1; + } + + if(Index_remove(self, Py_BuildValue("(N)", key)) == NULL) + return -1; + + return 0; } PyObject * @@ -336,6 +361,8 @@ Index_write_tree(Index *self) PyMethodDef Index_methods[] = { {"add", (PyCFunction)Index_add, METH_VARARGS, "Add or update an index entry from a file in disk."}, + {"remove", (PyCFunction)Index_remove, METH_VARARGS, + "Removes an entry from index."}, {"clear", (PyCFunction)Index_clear, METH_NOARGS, "Clear the contents (all the entries) of an index object."}, {"diff", (PyCFunction)Index_diff_tree, METH_VARARGS, diff --git a/test/test_index.py b/test/test_index.py index a4123ecc6..8950cbcc0 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -135,5 +135,13 @@ def test_bare_index(self): self.assertRaises(pygit2.GitError, lambda: index.add('bye.txt')) + def test_del(self): + index = self.repo.index + del index['hello.txt'] + + def test_remove(self): + index = self.repo.index + index.remove('hello.txt') + if __name__ == '__main__': unittest.main() From 3887c8f1c549c3698a8d3d06df0605c1c4d8c76a Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 4 Nov 2012 13:03:05 +0100 Subject: [PATCH 0284/2237] fixed api changes: git_tree_create_fromindex to git_index_write_tree --- src/pygit2/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 812e2774a..8681bd39f 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -351,7 +351,7 @@ Index_write_tree(Index *self) git_oid oid; int err; - err = git_tree_create_fromindex(&oid, self->index); + err = git_index_write_tree(&oid, self->index); if (err < 0) return Error_set(err); From e3a71d4f416776314e9c8f0c4e29e79de1f5f818 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 4 Nov 2012 21:49:15 +0100 Subject: [PATCH 0285/2237] removed unused variable in repository.c --- src/pygit2/repository.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 14393fab8..0fcfbb75d 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -63,7 +63,6 @@ lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, { int err; git_object *obj; - Object *py_obj = NULL; err = git_object_lookup_prefix(&obj, repo->repo, oid, (unsigned int)len, type); From 8827ea2312ec9bb535334578a46930433015eba7 Mon Sep 17 00:00:00 2001 From: delanne Date: Thu, 8 Nov 2012 12:39:26 +0100 Subject: [PATCH 0286/2237] Fix memory leak reported by valgrind --- src/pygit2/config.c | 12 ++++++++++-- src/pygit2/index.c | 5 +++-- src/pygit2/repository.c | 14 +++++++++----- src/pygit2/tree.c | 6 ++++-- test/test_index.py | 2 +- 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/pygit2/config.c b/src/pygit2/config.c index 0983bacc7..508550c59 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -139,7 +139,7 @@ Config_contains(Config *self, PyObject *py_key) { return -1; err = git_config_get_string(&c_value, self->config, c_key); - + free(c_key); if (err == GIT_ENOTFOUND) return 0; if (err < 0) { @@ -164,15 +164,18 @@ Config_getitem(Config *self, PyObject *py_key) err = git_config_get_int64(&c_intvalue, self->config, c_key); if (err == GIT_OK) { + free(c_key); return PyInt_FromLong((long)c_intvalue); } err = git_config_get_bool(&c_boolvalue, self->config, c_key); if (err == GIT_OK) { + free(c_key); return PyBool_FromLong((long)c_boolvalue); } err = git_config_get_string(&c_charvalue, self->config, c_key); + free(c_key); if (err < 0) { if (err == GIT_ENOTFOUND) { PyErr_SetObject(PyExc_KeyError, py_key); @@ -189,6 +192,7 @@ Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) { int err; const char *c_key; + const char *py_str; if (!(c_key = py_str_to_c_str(py_key,NULL))) return -1; @@ -203,9 +207,13 @@ Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) (int64_t)PyInt_AsLong(py_value)); } else { py_value = PyObject_Str(py_value); + py_str = py_str_to_c_str(py_value,NULL); err = git_config_set_string(self->config, c_key, - py_str_to_c_str(py_value,NULL)); + py_str); + free(py_str); } + + free(c_key); if (err < 0) { Error_set(err); return -1; diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 8681bd39f..c97160861 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -214,6 +214,7 @@ Index_get_position(Index *self, PyObject *value) free(path); return -1; } + free(path); return idx; } @@ -223,7 +224,7 @@ Index_contains(Index *self, PyObject *value) char *path; int idx; - path = py_path_to_c_str(value); + path = PyString_AsString(value); if (!path) return -1; idx = git_index_find(self->index, path); @@ -231,7 +232,6 @@ Index_contains(Index *self, PyObject *value) return 0; if (idx < 0) { Error_set_str(idx, path); - free(path); return -1; } @@ -339,6 +339,7 @@ Index_read_tree(Index *self, PyObject *value) return Error_set(err); err = git_index_read_tree(self->index, tree); + git_tree_free(tree); if (err < 0) return Error_set(err); diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 8ba37042e..d5015a9c3 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -167,6 +167,7 @@ Repository_head(Repository *self) { git_reference *head; const git_oid *oid; + PyObject *pyobj; int err; err = git_repository_head(&head, self->repo); @@ -180,8 +181,9 @@ Repository_head(Repository *self) } oid = git_reference_oid(head); - - return lookup_object(self, oid, GIT_OBJ_COMMIT); + pyobj = lookup_object(self, oid, GIT_OBJ_COMMIT); + git_reference_free(head); + return pyobj; } @@ -203,7 +205,7 @@ Repository_revparse_single(Repository *self, PyObject *py_spec) { git_object *c_obj; char *c_spec; - char *encoding = "ascii"; + char *encoding = "ascii"; int err; /* 1- Get the C revision spec */ @@ -213,12 +215,14 @@ Repository_revparse_single(Repository *self, PyObject *py_spec) /* 2- Lookup */ err = git_revparse_single(&c_obj, self->repo, c_spec); + if (err < 0) { PyObject *err_obj = Error_set_str(err, c_spec); free(c_spec); return err_obj; } - + free(c_spec); + return wrap_object(c_obj, self); } @@ -636,7 +640,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) free(c_name); return err_obj; } - + free(c_name); /* 3- Make an instance of Reference and return it */ return wrap_reference(c_reference); } diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index 76cefcb37..9c9f07106 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -43,6 +43,7 @@ void TreeEntry_dealloc(TreeEntry *self) { Py_XDECREF(self->owner); + git_tree_entry_free(self->entry); PyObject_Del(self); } @@ -231,7 +232,7 @@ Tree_getitem_by_index(Tree *self, PyObject *py_index) PyErr_SetObject(PyExc_IndexError, py_index); return NULL; } - return wrap_tree_entry(entry, self); + return wrap_tree_entry(git_tree_entry_dup(entry), self); } TreeEntry * @@ -261,6 +262,7 @@ Tree_getitem(Tree *self, PyObject *value) if (err < 0) return (TreeEntry*)Error_set(err); + // git_tree_entry_dup is already done in git_tree_entry_bypath return wrap_tree_entry(entry, self); } @@ -527,7 +529,7 @@ TreeIter_iternext(TreeIter *self) return NULL; self->i += 1; - return (TreeEntry*)wrap_tree_entry(tree_entry, self->owner); + return (TreeEntry*)wrap_tree_entry(git_tree_entry_dup(tree_entry), self->owner); } PyTypeObject TreeIterType = { diff --git a/test/test_index.py b/test/test_index.py index 8950cbcc0..45d2aae12 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -66,7 +66,7 @@ def test_add(self): index = self.repo.index sha = '0907563af06c7464d62a70cdd135a6ba7d2b41d8' - self.assertFalse('bye.txt' in index) + #~ self.assertFalse('bye.txt' in index) index.add('bye.txt') self.assertTrue('bye.txt' in index) self.assertEqual(len(index), 3) From c6e06d60d14917a3967b304a25d1ea97c9ea38ee Mon Sep 17 00:00:00 2001 From: delanne Date: Thu, 8 Nov 2012 12:44:50 +0100 Subject: [PATCH 0287/2237] Fix: uncomment a commented line --- test/test_index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_index.py b/test/test_index.py index 45d2aae12..8950cbcc0 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -66,7 +66,7 @@ def test_add(self): index = self.repo.index sha = '0907563af06c7464d62a70cdd135a6ba7d2b41d8' - #~ self.assertFalse('bye.txt' in index) + self.assertFalse('bye.txt' in index) index.add('bye.txt') self.assertTrue('bye.txt' in index) self.assertEqual(len(index), 3) From 9ffbe7b2ce54446ea099e0aa8a9fa51aa31d3fb9 Mon Sep 17 00:00:00 2001 From: delanne Date: Fri, 9 Nov 2012 14:45:07 +0100 Subject: [PATCH 0288/2237] Fix memleak + refcount issues --- include/pygit2/types.h | 9 +++- src/pygit2/diff.c | 94 ++++++++++++++++++++++++++++-------------- src/pygit2/tree.c | 2 +- 3 files changed, 72 insertions(+), 33 deletions(-) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index a2c7f560d..9b4d91788 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -57,9 +57,16 @@ OBJECT_STRUCT(Blob, git_blob, blob) OBJECT_STRUCT(Tag, git_tag, tag) OBJECT_STRUCT(Index, git_index, index) OBJECT_STRUCT(Walker, git_revwalk, walk) -OBJECT_STRUCT(Diff, git_diff_list, diff) OBJECT_STRUCT(Config, git_config, config) +typedef struct { + PyObject_HEAD + Repository *repo; + git_diff_list *diff; + PyObject *diff_changes; +} Diff; + + typedef struct { PyObject_HEAD PyObject *owner; /* Tree or TreeBuilder */ diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index d01044224..5b7243958 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -48,7 +48,7 @@ static int diff_data_cb( const char *content, size_t content_len) { - PyObject *hunks, *data, *tmp; + PyObject *hunks, *data; Hunk *hunk; Py_ssize_t size; @@ -61,14 +61,12 @@ static int diff_data_cb( if (hunk == NULL) return -1; - tmp = PyBytes_FromStringAndSize(content, content_len); - - data = Py_BuildValue("(O,i)", - tmp, + data = Py_BuildValue("(s#,i)", + content, content_len, line_origin ); - PyList_Append(hunk->data, data); + Py_DECREF(data); return 0; } @@ -91,6 +89,7 @@ static int diff_hunk_cb( if (hunks == NULL) { hunks = PyList_New(0); PyDict_SetItemString(cb_data, "hunks", hunks); + Py_DECREF(hunks); } hunk = (Hunk*)PyType_GenericNew(&HunkType, NULL, NULL); @@ -125,6 +124,7 @@ static int diff_hunk_cb( old_path = malloc(sizeof(char) * len); if (old_path == NULL) { free(hunk->header); + hunk->header = NULL; return -1; } @@ -153,7 +153,12 @@ static int diff_hunk_cb( hunk->data = PyList_New(0); } - PyList_Append(hunks, (PyObject *)hunk); + if(PyList_Append(hunks, (PyObject *)hunk) == 0) { + Py_DECREF(hunk); + } + else { + return -1; + } return 0; }; @@ -169,6 +174,7 @@ static int diff_file_cb(void *cb_data, const git_diff_delta *delta, if(files == NULL) { files = PyList_New(0); PyDict_SetItemString(cb_data, "files", files); + Py_DECREF(files); } file = Py_BuildValue("(s,s,i)", @@ -177,7 +183,10 @@ static int diff_file_cb(void *cb_data, const git_diff_delta *delta, delta->status ); - PyList_Append(files, file); + if (PyList_Append(files, file) == 0) { + // If success + Py_DECREF(file); + } } return 0; @@ -186,18 +195,20 @@ static int diff_file_cb(void *cb_data, const git_diff_delta *delta, PyObject * Diff_changes(Diff *self) { - PyObject *payload; - payload = PyDict_New(); - - git_diff_foreach( - self->diff, - payload, - &diff_file_cb, - &diff_hunk_cb, - &diff_data_cb - ); - return payload; + if (self->diff_changes == NULL){ + self->diff_changes = PyDict_New(); + + git_diff_foreach( + self->diff, + self->diff_changes, + &diff_file_cb, + &diff_hunk_cb, + &diff_data_cb + ); + } + + return PyDict_Copy(self->diff_changes); } static int diff_print_cb( @@ -227,24 +238,42 @@ Diff_patch(Diff *self) static int Hunk_init(Hunk *self, PyObject *args, PyObject *kwds) { - self->old_start = 0; - self->old_lines = 0; - - self->new_start = 0; - self->new_lines = 0; - - self->data = PyList_New(0); - if (self->data == NULL) { - Py_XDECREF(self); - return -1; - } + self->header = NULL; + + self->old_file = NULL; + self->old_start = 0; + self->old_lines = 0; + + self->new_file = NULL; + self->new_start = 0; + self->new_lines = 0; + + self->old_oid = NULL; + self->new_oid = NULL; + + self->data = PyList_New(0); + if (self->data == NULL) { + Py_XDECREF(self); + return -1; + } - return 0; + return 0; } static void Hunk_dealloc(Hunk *self) { + if (self->header != NULL) { + free(self->header); + } + if (self->new_file != NULL) { + free(self->new_file); + } + if (self->old_file != NULL) { + free(self->old_file); + } + Py_XDECREF(self->old_oid); + Py_XDECREF(self->new_oid); Py_XDECREF(self->data); PyObject_Del(self); } @@ -321,6 +350,8 @@ Diff_merge(Diff *self, PyObject *args) if (err < 0) return Error_set(err); + Py_XDECREF(self->diff_changes); + self->diff_changes = NULL; Py_RETURN_NONE; } @@ -329,6 +360,7 @@ Diff_dealloc(Diff *self) { git_diff_list_free(self->diff); Py_XDECREF(self->repo); + Py_XDECREF(self->diff_changes); PyObject_Del(self); } diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index 9c9f07106..448602d29 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -307,10 +307,10 @@ Tree_diff_tree(Tree *self, PyObject *args) py_diff = PyObject_New(Diff, &DiffType); if (py_diff) { - Py_INCREF(py_diff); Py_INCREF(self->repo); py_diff->repo = self->repo; py_diff->diff = diff; + py_diff->diff_changes = NULL; } return (PyObject*)py_diff; From 783025d32704c435e0500c6c26dca0e660d63e0a Mon Sep 17 00:00:00 2001 From: delanne Date: Fri, 9 Nov 2012 14:55:20 +0100 Subject: [PATCH 0289/2237] Fix: "The opt variable is not used in the loop" --- test/test_diff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_diff.py b/test/test_diff.py index 0aaa8b9ee..6fa9ba0a9 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -140,7 +140,7 @@ def test_diff_tree_opts(self): for opt in [pygit2.GIT_DIFF_IGNORE_WHITESPACE, pygit2.GIT_DIFF_IGNORE_WHITESPACE_EOL]: - diff = commit_c.tree.diff(commit_d.tree, pygit2.GIT_DIFF_IGNORE_WHITESPACE) + diff = commit_c.tree.diff(commit_d.tree, opt) self.assertTrue(diff is not None) self.assertEqual(0, len(diff.changes.get('hunks', list()))) From 8216dad986cadf7c5851b8b179ccb4b3676efba5 Mon Sep 17 00:00:00 2001 From: delanne Date: Fri, 9 Nov 2012 16:19:47 +0100 Subject: [PATCH 0290/2237] Fix issue reported by Travis for Python3.1 and Python3.2 --- src/pygit2/index.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index c97160861..b772f58d2 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -224,17 +224,20 @@ Index_contains(Index *self, PyObject *value) char *path; int idx; - path = PyString_AsString(value); + path = py_path_to_c_str(value); if (!path) return -1; idx = git_index_find(self->index, path); - if (idx == GIT_ENOTFOUND) + if (idx == GIT_ENOTFOUND) { + free(path); return 0; + } if (idx < 0) { + free(path); Error_set_str(idx, path); return -1; } - + free(path); return 1; } From b84e8dc9a0fa9bb246350336a8f71687d1600854 Mon Sep 17 00:00:00 2001 From: delanne Date: Mon, 12 Nov 2012 10:25:55 +0100 Subject: [PATCH 0291/2237] Fix the issue reported by Jdavid 'Here you are freeing the path and then you use it.' --- src/pygit2/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index b772f58d2..cc2fd1042 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -233,8 +233,8 @@ Index_contains(Index *self, PyObject *value) return 0; } if (idx < 0) { - free(path); Error_set_str(idx, path); + free(path); return -1; } free(path); From 40751b62b364fbb48fe54beb5cc333729b1a195a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 14 Nov 2012 18:08:08 +0100 Subject: [PATCH 0292/2237] style: remove tabs and trailing white-space --- src/pygit2/diff.c | 8 ++++---- src/pygit2/object.c | 2 +- src/pygit2/repository.c | 10 +++++----- src/pygit2/tree.c | 2 +- test/test_refs.py | 10 +++++----- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index 5b7243958..81751aebc 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -196,9 +196,9 @@ PyObject * Diff_changes(Diff *self) { - if (self->diff_changes == NULL){ + if (self->diff_changes == NULL) { self->diff_changes = PyDict_New(); - + git_diff_foreach( self->diff, self->diff_changes, @@ -239,7 +239,7 @@ static int Hunk_init(Hunk *self, PyObject *args, PyObject *kwds) { self->header = NULL; - + self->old_file = NULL; self->old_start = 0; self->old_lines = 0; @@ -250,7 +250,7 @@ Hunk_init(Hunk *self, PyObject *args, PyObject *kwds) self->old_oid = NULL; self->new_oid = NULL; - + self->data = PyList_New(0); if (self->data == NULL) { Py_XDECREF(self); diff --git a/src/pygit2/object.c b/src/pygit2/object.c index 172cf3f8b..c990fd2de 100644 --- a/src/pygit2/object.c +++ b/src/pygit2/object.c @@ -176,7 +176,7 @@ wrap_object(git_object *c_object, Repository *repo) if (py_obj) { py_obj->obj = c_object; - if (repo) { + if (repo) { py_obj->repo = repo; Py_INCREF(repo); } diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index a150253c6..2754591f5 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -182,7 +182,7 @@ Repository_head(Repository *self) oid = git_reference_oid(head); pyobj = lookup_object(self, oid, GIT_OBJ_COMMIT); git_reference_free(head); - return pyobj; + return pyobj; } @@ -214,14 +214,14 @@ Repository_revparse_single(Repository *self, PyObject *py_spec) /* 2- Lookup */ err = git_revparse_single(&c_obj, self->repo, c_spec); - - if (err < 0) { + + if (err < 0) { PyObject *err_obj = Error_set_str(err, c_spec); free(c_spec); return err_obj; } free(c_spec); - + return wrap_object(c_obj, self); } @@ -634,7 +634,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) /* 2- Lookup */ err = git_reference_lookup(&c_reference, self->repo, c_name); - if (err < 0) { + if (err < 0) { PyObject *err_obj = Error_set_str(err, c_name); free(c_name); return err_obj; diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index fdd20a797..c3921af45 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -250,7 +250,7 @@ Tree_getitem(Tree *self, PyObject *value) path = py_path_to_c_str(value); if (path == NULL) return NULL; - + err = git_tree_entry_bypath(&entry, self->tree, path); free(path); diff --git a/test/test_refs.py b/test/test_refs.py index 5ba77e1fb..b61d855f8 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -175,9 +175,9 @@ def test_create_reference(self): reference = self.repo.lookup_reference('refs/tags/version1') self.assertEqual(reference.hex, LAST_COMMIT) - # try to create existing reference - self.assertRaises(ValueError, self.repo.create_reference, - 'refs/tags/version1', LAST_COMMIT) + # try to create existing reference + self.assertRaises(ValueError, self.repo.create_reference, + 'refs/tags/version1', LAST_COMMIT) # try to create existing reference with force reference = self.repo.create_reference('refs/tags/version1', @@ -195,8 +195,8 @@ def test_create_symbolic_reference(self): # try to create existing symbolic reference - self.assertRaises(ValueError, self.repo.create_reference, - 'refs/tags/beta','refs/heads/master', symbolic=True) + self.assertRaises(ValueError, self.repo.create_reference, + 'refs/tags/beta','refs/heads/master', symbolic=True) # try to create existing symbolic reference with force reference = self.repo.create_reference('refs/tags/beta', From c55881f9ce5973486e9e395995b05b57b156fc44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 14 Nov 2012 18:55:28 +0100 Subject: [PATCH 0293/2237] Fix compile warnings --- src/pygit2/config.c | 20 ++++++++++---------- src/pygit2/signature.c | 2 +- src/pygit2/tree.c | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/pygit2/config.c b/src/pygit2/config.c index 508550c59..5924a9563 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -133,9 +133,10 @@ int Config_contains(Config *self, PyObject *py_key) { int err; const char *c_value; - const char *c_key; + char *c_key; - if (!(c_key = py_str_to_c_str(py_key,NULL))) + c_key = py_str_to_c_str(py_key,NULL); + if (c_key == NULL) return -1; err = git_config_get_string(&c_value, self->config, c_key); @@ -154,10 +155,10 @@ PyObject * Config_getitem(Config *self, PyObject *py_key) { int err; - int64_t c_intvalue; - int c_boolvalue; - const char *c_charvalue; - const char *c_key; + int64_t c_intvalue; + int c_boolvalue; + const char *c_charvalue; + char *c_key; if (!(c_key = py_str_to_c_str(py_key,NULL))) return NULL; @@ -191,8 +192,8 @@ int Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) { int err; - const char *c_key; - const char *py_str; + char *c_key; + char *py_str; if (!(c_key = py_str_to_c_str(py_key,NULL))) return -1; @@ -208,8 +209,7 @@ Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) } else { py_value = PyObject_Str(py_value); py_str = py_str_to_c_str(py_value,NULL); - err = git_config_set_string(self->config, c_key, - py_str); + err = git_config_set_string(self->config, c_key, py_str); free(py_str); } diff --git a/src/pygit2/signature.c b/src/pygit2/signature.c index cd53a32f8..cb8425d94 100644 --- a/src/pygit2/signature.c +++ b/src/pygit2/signature.c @@ -85,7 +85,7 @@ Signature_dealloc(Signature *self) Py_DECREF(self->obj); else { git_signature_free((git_signature*)self->signature); - free((void*)self->encoding); + free((char*)self->encoding); } Py_TYPE(self)->tp_free((PyObject*)self); } diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index c3921af45..a4004c614 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -43,7 +43,7 @@ void TreeEntry_dealloc(TreeEntry *self) { Py_XDECREF(self->owner); - git_tree_entry_free(self->entry); + git_tree_entry_free((git_tree_entry*)self->entry); PyObject_Del(self); } From c615ca29cd7b13961e07fb58b8ead8d4c4d7f442 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 15 Nov 2012 19:32:25 +0100 Subject: [PATCH 0294/2237] fixed refcount error use Py_RETURN_TRUE instead of return Py_True and Py_RETURN_FALSE instead of return Py_False. Otherwise there will be refcount errors. --- src/pygit2/repository.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 61828973c..7c011a58b 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -195,9 +195,9 @@ PyObject * Repository_head_is_detached(Repository *self) { if(git_repository_head_detached(self->repo) > 0) - return Py_True; + Py_RETURN_TRUE; - return Py_False; + Py_RETURN_FALSE; } @@ -211,9 +211,9 @@ PyObject * Repository_head_is_orphaned(Repository *self) { if(git_repository_head_orphan(self->repo) > 0) - return Py_True; + Py_RETURN_TRUE; - return Py_False; + Py_RETURN_FALSE; } @@ -226,9 +226,9 @@ PyObject * Repository_is_empty(Repository *self) { if(git_repository_is_empty(self->repo) > 0) - return Py_True; + Py_RETURN_TRUE; - return Py_False; + Py_RETURN_FALSE; } @@ -241,9 +241,9 @@ PyObject * Repository_is_bare(Repository *self) { if(git_repository_is_bare(self->repo) > 0) - return Py_True; + Py_RETURN_TRUE; - return Py_False; + Py_RETURN_FALSE; } From 88de08288c1513e756bf03c9b371d03c2c45b506 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 15 Nov 2012 19:34:52 +0100 Subject: [PATCH 0295/2237] coding style - use 4 spaces for intendation and a space before keywords --- src/pygit2/repository.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 7c011a58b..2f4da6e34 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -194,10 +194,10 @@ PyDoc_STRVAR( PyObject * Repository_head_is_detached(Repository *self) { - if(git_repository_head_detached(self->repo) > 0) - Py_RETURN_TRUE; + if (git_repository_head_detached(self->repo) > 0) + Py_RETURN_TRUE; - Py_RETURN_FALSE; + Py_RETURN_FALSE; } @@ -210,10 +210,10 @@ PyDoc_STRVAR( PyObject * Repository_head_is_orphaned(Repository *self) { - if(git_repository_head_orphan(self->repo) > 0) - Py_RETURN_TRUE; + if (git_repository_head_orphan(self->repo) > 0) + Py_RETURN_TRUE; - Py_RETURN_FALSE; + Py_RETURN_FALSE; } @@ -225,10 +225,10 @@ PyDoc_STRVAR( PyObject * Repository_is_empty(Repository *self) { - if(git_repository_is_empty(self->repo) > 0) - Py_RETURN_TRUE; + if (git_repository_is_empty(self->repo) > 0) + Py_RETURN_TRUE; - Py_RETURN_FALSE; + Py_RETURN_FALSE; } @@ -240,10 +240,10 @@ PyDoc_STRVAR( PyObject * Repository_is_bare(Repository *self) { - if(git_repository_is_bare(self->repo) > 0) - Py_RETURN_TRUE; + if (git_repository_is_bare(self->repo) > 0) + Py_RETURN_TRUE; - Py_RETURN_FALSE; + Py_RETURN_FALSE; } From 5262c9e23b9cbcc6bb2066a27d8adc17dd333403 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 16 Nov 2012 13:04:47 +0100 Subject: [PATCH 0296/2237] fixed parameter order for diff functions (libgit2) --- src/pygit2/index.c | 10 ++++++---- src/pygit2/tree.c | 15 ++++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 8681bd39f..e80073352 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -117,15 +117,17 @@ Index_diff_tree(Index *self, PyObject *args) if (py_obj == NULL) { err = git_diff_workdir_to_index( + &diff, self->repo->repo, - &opts, - &diff); + self->index, + &opts); } else if (PyObject_TypeCheck(py_obj, &TreeType)) { err = git_diff_index_to_tree( + &diff, self->repo->repo, - &opts, ((Tree *)py_obj)->tree, - &diff); + self->index, + &opts); } else { PyErr_SetObject(PyExc_TypeError, py_obj); return NULL; diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index 76cefcb37..097c609ed 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -279,23 +279,24 @@ Tree_diff_tree(Tree *self, PyObject *args) if (py_obj == NULL) { err = git_diff_workdir_to_tree( + &diff, self->repo->repo, - &opts, self->tree, - &diff); + &opts); } else if (PyObject_TypeCheck(py_obj, &TreeType)) { err = git_diff_tree_to_tree( + &diff, self->repo->repo, - &opts, self->tree, ((Tree *)py_obj)->tree, - &diff); + &opts); } else if (PyObject_TypeCheck(py_obj, &IndexType)) { err = git_diff_index_to_tree( - ((Index *)py_obj)->repo->repo, - &opts, + &diff, + self->repo->repo, self->tree, - &diff); + ((Index *)py_obj)->index, + &opts); } else { PyErr_SetObject(PyExc_TypeError, py_obj); return NULL; From b2359200bbbc1d113bbb3b87f8bce798a99243f4 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 19 Nov 2012 23:11:18 +0100 Subject: [PATCH 0297/2237] revlog iteration will start with last commit, not the first one --- test/test_revwalk.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test_revwalk.py b/test/test_revwalk.py index f7e56c66a..4fb1fd9c3 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -44,13 +44,13 @@ 'acecd5ea2924a4b900e7e149496e1f4b57976e51'] REVLOGS = [ - ('J. David Ibañez', 'commit (initial): First commit'), - ('J. David Ibañez', 'checkout: moving from master to i18n'), - ('J. David Ibañez', 'commit: Say hello in Spanish'), - ('J. David Ibañez', 'commit: Say hello in French'), - ('J. David Ibañez', 'checkout: moving from i18n to master'), + ('J. David Ibañez', 'merge i18n: Merge made by recursive.'), ('J. David Ibañez', 'commit: Add .gitignore file'), - ('J. David Ibañez', 'merge i18n: Merge made by recursive.') + ('J. David Ibañez', 'checkout: moving from i18n to master'), + ('J. David Ibañez', 'commit: Say hello in French'), + ('J. David Ibañez', 'commit: Say hello in Spanish'), + ('J. David Ibañez', 'checkout: moving from master to i18n'), + ('J. David Ibañez', 'commit (initial): First commit') ] From 64bb6efc2b651ea5bd65444d5e69a45536f60409 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 19 Nov 2012 23:12:12 +0100 Subject: [PATCH 0298/2237] added Error_set_exc() for throwing individual Exceptions --- include/pygit2/error.h | 1 + src/pygit2/error.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/pygit2/error.h b/include/pygit2/error.h index 38df75eae..7dcf58991 100644 --- a/include/pygit2/error.h +++ b/include/pygit2/error.h @@ -34,6 +34,7 @@ PyObject* Error_type(int type); PyObject* Error_set(int err); +PyObject* Error_set_exc(PyObject* exception); PyObject* Error_set_str(int err, const char *str); PyObject* Error_set_oid(int err, const git_oid *oid, size_t len); diff --git a/src/pygit2/error.c b/src/pygit2/error.c index dd4138dfa..6e94336b2 100644 --- a/src/pygit2/error.c +++ b/src/pygit2/error.c @@ -92,6 +92,17 @@ PyObject* Error_set(int err) return NULL; } +PyObject* Error_set_exc(PyObject* exception) +{ + const git_error* error = giterr_last(); + char* message = (error == NULL) ? + "(No error information given)" : error->message; + PyErr_SetString(exception, message); + + return NULL; +} + + PyObject* Error_set_str(int err, const char *str) { const git_error* error; From 549602815007bc47d9650f0ddeb8376e3648053c Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 19 Nov 2012 23:13:02 +0100 Subject: [PATCH 0299/2237] throw an IOError instead of KeyError, if git_config_open_ondisk() fails --- src/pygit2/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/config.c b/src/pygit2/config.c index 5924a9563..16a371362 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -52,7 +52,7 @@ Config_init(Config *self, PyObject *args, PyObject *kwds) } err = git_config_open_ondisk(&self->config, path); if (err < 0) { - Error_set_str(err, path); + Error_set_exc(PyExc_IOError); return -1; } } else { From 3e0c6f521d45e1ec0b2b14b9d98da7bb8eaab23a Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 19 Nov 2012 23:14:27 +0100 Subject: [PATCH 0300/2237] new config file has to exist (change in libgit2 - see 270160b) --- test/test_config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_config.py b/test/test_config.py index 8656436cd..aa5063b96 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -65,6 +65,7 @@ def test_system_config(self): pass def test_new(self): + open(config_filename, 'w').close() # touch file config_write = pygit2.Config(config_filename) self.assertNotEqual(config_write, None) From dbf468b2ecb2b66db19f4bfdbaa208dfc8e1c924 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 20 Nov 2012 21:02:08 +0100 Subject: [PATCH 0301/2237] correct error handling for errors other than GIT_ENOTFOUND --- src/pygit2/config.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/pygit2/config.c b/src/pygit2/config.c index 16a371362..24e98ac4b 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -50,18 +50,23 @@ Config_init(Config *self, PyObject *args, PyObject *kwds) if (!PyArg_ParseTuple(args, "s", &path)) { return -1; } + err = git_config_open_ondisk(&self->config, path); - if (err < 0) { - Error_set_exc(PyExc_IOError); - return -1; - } + } else { err = git_config_new(&self->config); - if (err < 0) { + } + + if (err < 0) { + if (err == GIT_ENOTFOUND) { + Error_set_exc(PyExc_IOError); + } else { Error_set(err); - return -1; } + + return -1; } + return 0; } From 6b3607cf9581434a1da289290e8a1120cf0af44b Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 22 Nov 2012 15:47:53 +0100 Subject: [PATCH 0302/2237] only use error message if giterr_last() is not null exptected errors in libgit2 must not set a git_error. --- src/pygit2/error.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pygit2/error.c b/src/pygit2/error.c index 6e94336b2..bb806d128 100644 --- a/src/pygit2/error.c +++ b/src/pygit2/error.c @@ -113,6 +113,9 @@ PyObject* Error_set_str(int err, const char *str) } error = giterr_last(); + if (error == NULL) //exptected error - no error msg set + return PyErr_Format(Error_type(err), "%s", str); + return PyErr_Format(Error_type(err), "%s: %s", str, error->message); } From 20fb789e11dc125b7d0ad3c95da8f12c31dc5dfe Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 22 Nov 2012 16:33:40 +0100 Subject: [PATCH 0303/2237] fixed typo --- src/pygit2/error.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/error.c b/src/pygit2/error.c index bb806d128..e0c702a92 100644 --- a/src/pygit2/error.c +++ b/src/pygit2/error.c @@ -113,7 +113,7 @@ PyObject* Error_set_str(int err, const char *str) } error = giterr_last(); - if (error == NULL) //exptected error - no error msg set + if (error == NULL) //expected error - no error msg set return PyErr_Format(Error_type(err), "%s", str); return PyErr_Format(Error_type(err), "%s: %s", str, error->message); From 04068415335b8cf2528cb97430a9143f417fe82e Mon Sep 17 00:00:00 2001 From: delanne Date: Mon, 26 Nov 2012 12:18:57 +0100 Subject: [PATCH 0304/2237] - thanks to Martin R. Hufsky for the patch for git_diff_find_similar - I updated the unittest (they failed with the patch), and added a short unittest which emulates --find-copies-harder --- src/pygit2.c | 12 +++++++++ src/pygit2/diff.c | 25 ++++++++++++++++-- .../05/6e626e51b1fc1ee2182800e399ed8d84c8f082 | Bin 0 -> 154 bytes .../1a/f81f24e48f92009ca02a874edb6151c71f60de | Bin 0 -> 107 bytes test/data/testrepo.git/refs/heads/master | 2 +- test/test_config.py | 2 +- test/test_diff.py | 20 +++++++++++--- test/test_repository.py | 4 +-- 8 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 test/data/testrepo.git/objects/05/6e626e51b1fc1ee2182800e399ed8d84c8f082 create mode 100644 test/data/testrepo.git/objects/1a/f81f24e48f92009ca02a874edb6151c71f60de diff --git a/src/pygit2.c b/src/pygit2.c index e41015a65..41613e33d 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -264,6 +264,18 @@ moduleinit(PyObject* m) PyModule_AddIntConstant(m, "GIT_DIFF_RECURSE_UNTRACKED_DIRS", GIT_DIFF_RECURSE_UNTRACKED_DIRS); + /* Flags for diff find similar */ + PyModule_AddIntConstant(m, "GIT_DIFF_FIND_RENAMES", // --find-renames + GIT_DIFF_FIND_RENAMES); + PyModule_AddIntConstant(m, "GIT_DIFF_FIND_RENAMES_FROM_REWRITES", // --break-rewrites=N + GIT_DIFF_FIND_RENAMES_FROM_REWRITES); + PyModule_AddIntConstant(m, "GIT_DIFF_FIND_COPIES", // --find-copies + GIT_DIFF_FIND_COPIES); + PyModule_AddIntConstant(m, "GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED", // --find-copies-harder + GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED); + PyModule_AddIntConstant(m, "GIT_DIFF_FIND_AND_BREAK_REWRITES", // --break-rewrites=/M + GIT_DIFF_FIND_AND_BREAK_REWRITES); + /* Flags for diffed files */ PyModule_AddIntConstant(m, "GIT_DIFF_FILE_VALID_OID", GIT_DIFF_FILE_VALID_OID); diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index 81751aebc..526a31233 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -177,10 +177,11 @@ static int diff_file_cb(void *cb_data, const git_diff_delta *delta, Py_DECREF(files); } - file = Py_BuildValue("(s,s,i)", + file = Py_BuildValue("(s,s,i,i)", delta->old_file.path, delta->new_file.path, - delta->status + delta->status, + delta->similarity ); if (PyList_Append(files, file) == 0) { @@ -355,6 +356,24 @@ Diff_merge(Diff *self, PyObject *args) Py_RETURN_NONE; } +PyObject * +Diff_find_similar(Diff *self, PyObject *args) +{ + int err; + git_diff_options opts = {0}; + + if (!PyArg_ParseTuple(args, "|i", &opts.flags)) + return NULL; + + err = git_diff_find_similar(self->diff, &opts); + if (err < 0) + return Error_set(err); + + Py_XDECREF(self->diff_changes); + self->diff_changes = NULL; + Py_RETURN_NONE; +} + static void Diff_dealloc(Diff *self) { @@ -373,6 +392,8 @@ PyGetSetDef Diff_getseters[] = { static PyMethodDef Diff_methods[] = { {"merge", (PyCFunction)Diff_merge, METH_VARARGS, "Merge one diff into another."}, + {"find_similar", (PyCFunction)Diff_find_similar, METH_VARARGS, + "Find renamed files in diff."}, {NULL, NULL, 0, NULL} }; diff --git a/test/data/testrepo.git/objects/05/6e626e51b1fc1ee2182800e399ed8d84c8f082 b/test/data/testrepo.git/objects/05/6e626e51b1fc1ee2182800e399ed8d84c8f082 new file mode 100644 index 0000000000000000000000000000000000000000..9ec45d6f126d5eec6d4788fc035076871e78c2c1 GIT binary patch literal 154 zcmV;L0A>Gp0iBLp3c@fD0R7G>@-LL^<_#iVWs_{QVA>L**xOt127b=Kz~r^Hc}PTi z8l71J)c_R@n@}Sv6*DQM;0>4Lz(V#=ohmFg>bwk@b0*_!O6VYH_CTJDE8r}~n1W$s zmsQe&UgNQLS@=oI!t(N=_so6z@w(scjppZ+*LIblt&WJOL7o*TB@TN`4*oMg`fZma Iz8#K7xw_^~4*&oF literal 0 HcmV?d00001 diff --git a/test/data/testrepo.git/objects/1a/f81f24e48f92009ca02a874edb6151c71f60de b/test/data/testrepo.git/objects/1a/f81f24e48f92009ca02a874edb6151c71f60de new file mode 100644 index 0000000000000000000000000000000000000000..46f991f47a828d2defb47dd52d1fd696e240e4c7 GIT binary patch literal 107 zcmV-x0F?iD0V^p=O;s>7GGH(?FfcPQQAlJc?_iw&`pFlj$Ynl3vu3>wlS{d~7bdBf zoL^9hPel^L^lPFI_o&-Aob)Qn5P81S{&tkKnTY`qC?qo^`rlm4>HSUO)#U#F2l#5G NLVnD;003wLGug5iGKv5I literal 0 HcmV?d00001 diff --git a/test/data/testrepo.git/refs/heads/master b/test/data/testrepo.git/refs/heads/master index 101232d36..b8e984855 100644 --- a/test/data/testrepo.git/refs/heads/master +++ b/test/data/testrepo.git/refs/heads/master @@ -1 +1 @@ -ccca47fbb26183e71a7a46d165299b84e2e6c0b3 +056e626e51b1fc1ee2182800e399ed8d84c8f082 diff --git a/test/test_config.py b/test/test_config.py index aa5063b96..cc64a53e4 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -78,7 +78,7 @@ def test_new(self): self.assertFalse(config_read['core.bare']) self.assertTrue('core.editor' in config_read) self.assertEqual(config_read['core.editor'], 'ed') - + def test_add(self): config = pygit2.Config() diff --git a/test/test_diff.py b/test/test_diff.py index 6fa9ba0a9..e79ddf29f 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -38,6 +38,7 @@ COMMIT_SHA1_2 = 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c' COMMIT_SHA1_3 = '2cdae28389c059815e951d0bb9eed6533f61a46b' COMMIT_SHA1_4 = 'ccca47fbb26183e71a7a46d165299b84e2e6c0b3' +COMMIT_SHA1_5 = '056e626e51b1fc1ee2182800e399ed8d84c8f082' PATCH = b"""diff --git a/a b/a index 7f129fd..af431f2 100644 @@ -119,7 +120,7 @@ def test_diff_tree(self): # self.assertIsNotNone is 2.7 only self.assertTrue(diff is not None) # self.assertIn is 2.7 only - self.assertTrue(('a','a', 3) in diff.changes['files']) + self.assertTrue(('a','a', 3, 0) in diff.changes['files']) self.assertEqual(2, len(diff.changes['hunks'])) hunk = diff.changes['hunks'][0] @@ -162,13 +163,13 @@ def test_diff_merge(self): self.assertTrue(diff_c is not None) # assertIn / assertNotIn are 2.7 only - self.assertTrue(('b','b', 3) not in diff_b.changes['files']) - self.assertTrue(('b','b', 3) in diff_c.changes['files']) + self.assertTrue(('b','b', 3, 0) not in diff_b.changes['files']) + self.assertTrue(('b','b', 3, 0) in diff_c.changes['files']) diff_b.merge(diff_c) # assertIn is 2.7 only - self.assertTrue(('b','b', 3) in diff_b.changes['files']) + self.assertTrue(('b','b', 3, 0) in diff_b.changes['files']) hunk = diff_b.changes['hunks'][1] self.assertEqual(hunk.old_start, 1) @@ -203,5 +204,16 @@ def test_diff_oids(self): self.assertEqual(diff.changes['hunks'][0].old_oid, '7f129fd57e31e935c6d60a0c794efe4e6927664b') self.assertEqual(diff.changes['hunks'][0].new_oid, 'af431f20fc541ed6d5afede3e2dc7160f6f01f16') + def test_find_similar(self): + commit_a = self.repo[COMMIT_SHA1_4] + commit_b = self.repo[COMMIT_SHA1_5] + + #~ Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate + #~ --find-copies-harder during rename transformion... + diff = commit_a.tree.diff(commit_b.tree, pygit2.GIT_DIFF_INCLUDE_UNMODIFIED) + self.assertFalse(('a', 'a.copy', 5, 100) in diff.changes['files']) + diff.find_similar(pygit2.GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED) + self.assertTrue(('a', 'a.copy', 5, 100) in diff.changes['files']) + if __name__ == '__main__': unittest.main() diff --git a/test/test_repository.py b/test/test_repository.py index c2c05ec9c..10dd9f5fe 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -39,8 +39,8 @@ from . import utils -HEAD_SHA = 'ccca47fbb26183e71a7a46d165299b84e2e6c0b3' -PARENT_SHA = '2cdae28389c059815e951d0bb9eed6533f61a46b' # HEAD^ +HEAD_SHA = '056e626e51b1fc1ee2182800e399ed8d84c8f082' +PARENT_SHA = 'ccca47fbb26183e71a7a46d165299b84e2e6c0b3' # HEAD^ A_HEX_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' A_BIN_SHA = binascii.unhexlify(A_HEX_SHA.encode('ascii')) From a511f299bb63123ea046138f7e3049e9b4e34a39 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 28 Nov 2012 18:10:07 +0100 Subject: [PATCH 0305/2237] fixex api changes of libgit2 in 64c5112 --- include/pygit2/types.h | 2 +- src/pygit2/commit.c | 2 +- src/pygit2/config.c | 2 +- src/pygit2/diff.c | 24 ++++++++++++------------ src/pygit2/index.c | 8 ++++---- src/pygit2/reference.c | 26 +++++++++++++++----------- src/pygit2/repository.c | 8 ++++---- src/pygit2/tag.c | 2 +- test/test_refs.py | 1 + 9 files changed, 40 insertions(+), 35 deletions(-) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 9b4d91788..e4e4f5d65 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -97,7 +97,7 @@ typedef struct { typedef struct { PyObject_HEAD - git_index_entry *entry; + const git_index_entry *entry; } IndexEntry; typedef struct { diff --git a/src/pygit2/commit.c b/src/pygit2/commit.c index 6abc92a3e..c0ccee197 100644 --- a/src/pygit2/commit.c +++ b/src/pygit2/commit.c @@ -135,7 +135,7 @@ Commit_get_parents(Commit *commit) return NULL; for (i=0; i < parent_count; i++) { - parent_oid = git_commit_parent_oid(commit->commit, i); + parent_oid = git_commit_parent_id(commit->commit, i); if (parent_oid == NULL) { Py_DECREF(list); Error_set(GIT_ENOTFOUND); diff --git a/src/pygit2/config.c b/src/pygit2/config.c index 24e98ac4b..6a84f6300 100644 --- a/src/pygit2/config.c +++ b/src/pygit2/config.c @@ -204,7 +204,7 @@ Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) return -1; if (!py_value) { - err = git_config_delete(self->config, c_key); + err = git_config_delete_entry(self->config, c_key); } else if (PyBool_Check(py_value)) { err = git_config_set_bool(self->config, c_key, (int)PyObject_IsTrue(py_value)); diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index 526a31233..8cd0d5136 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -41,12 +41,12 @@ extern PyTypeObject DiffType; extern PyTypeObject HunkType; static int diff_data_cb( - void *cb_data, const git_diff_delta *delta, const git_diff_range *range, char line_origin, const char *content, - size_t content_len) + size_t content_len, + void *cb_data) { PyObject *hunks, *data; Hunk *hunk; @@ -72,11 +72,11 @@ static int diff_data_cb( } static int diff_hunk_cb( - void *cb_data, const git_diff_delta *delta, const git_diff_range *range, const char *header, - size_t header_len) + size_t header_len, + void *cb_data) { PyObject *hunks; Hunk *hunk; @@ -163,8 +163,8 @@ static int diff_hunk_cb( return 0; }; -static int diff_file_cb(void *cb_data, const git_diff_delta *delta, - float progress) +static int diff_file_cb(const git_diff_delta *delta, float progress, +void *cb_data) { PyObject *files, *file; @@ -202,10 +202,10 @@ Diff_changes(Diff *self) git_diff_foreach( self->diff, - self->diff_changes, &diff_file_cb, &diff_hunk_cb, - &diff_data_cb + &diff_data_cb, + self->diff_changes ); } @@ -213,12 +213,12 @@ Diff_changes(Diff *self) } static int diff_print_cb( - void *cb_data, const git_diff_delta *delta, const git_diff_range *range, char usage, const char *line, - size_t line_len) + size_t line_len, + void *cb_data) { PyObject *data = PyBytes_FromStringAndSize(line, line_len); PyBytes_ConcatAndDel((PyObject **)cb_data, data); @@ -231,7 +231,7 @@ Diff_patch(Diff *self) { PyObject *patch = PyBytes_FromString(""); - git_diff_print_patch(self->diff, &patch, &diff_print_cb); + git_diff_print_patch(self->diff, &diff_print_cb, (void*) &patch); return patch; } @@ -360,7 +360,7 @@ PyObject * Diff_find_similar(Diff *self, PyObject *args) { int err; - git_diff_options opts = {0}; + git_diff_find_options opts = {0}; if (!PyArg_ParseTuple(args, "|i", &opts.flags)) return NULL; diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 327e314ad..2d1d8b51e 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -264,7 +264,7 @@ Index_len(Index *self) } PyObject * -wrap_index_entry(git_index_entry *entry, Index *index) +wrap_index_entry(const git_index_entry *entry, Index *index) { IndexEntry *py_entry; @@ -278,8 +278,8 @@ wrap_index_entry(git_index_entry *entry, Index *index) PyObject * Index_getitem(Index *self, PyObject *value) { - int idx; - git_index_entry *index_entry; + size_t idx; + const git_index_entry *index_entry; idx = Index_get_position(self, value); if (idx == -1) @@ -460,7 +460,7 @@ IndexIter_dealloc(IndexIter *self) PyObject * IndexIter_iternext(IndexIter *self) { - git_index_entry *index_entry; + const git_index_entry *index_entry; index_entry = git_index_get_byindex(self->owner->index, self->i); if (!index_entry) diff --git a/src/pygit2/reference.c b/src/pygit2/reference.c index 75c937e9d..be572b40f 100644 --- a/src/pygit2/reference.c +++ b/src/pygit2/reference.c @@ -63,13 +63,13 @@ PyObject* RefLogIter_iternext(PyObject *self) &RefLogEntryType, NULL, NULL ); - git_oid_fmt(oid_old, git_reflog_entry_oidold(entry)); - git_oid_fmt(oid_new, git_reflog_entry_oidnew(entry)); + git_oid_fmt(oid_old, git_reflog_entry_id_old(entry)); + git_oid_fmt(oid_new, git_reflog_entry_id_new(entry)); py_entry->oid_new = PyUnicode_FromStringAndSize(oid_new, 40); py_entry->oid_old = PyUnicode_FromStringAndSize(oid_old, 40); - py_entry->msg = strdup(git_reflog_entry_msg(entry)); + py_entry->msg = strdup(git_reflog_entry_message(entry)); signature = git_signature_dup( git_reflog_entry_committer(entry) @@ -220,10 +220,14 @@ Reference_get_target(Reference *self) CHECK_REFERENCE(self); /* Get the target */ - c_name = git_reference_target(self->reference); - if (c_name == NULL) { - PyErr_SetString(PyExc_ValueError, "no target available"); - return NULL; + if (GIT_REF_OID == git_reference_type(self->reference)) { + return git_oid_to_py_str(git_reference_target(self->reference)); + } else { + c_name = git_reference_symbolic_target(self->reference); + if (c_name == NULL) { + PyErr_SetString(PyExc_ValueError, "no target available"); + return NULL; + } } /* Make a PyString and return it */ @@ -244,7 +248,7 @@ Reference_set_target(Reference *self, PyObject *py_name) return -1; /* Set the new target */ - err = git_reference_set_target(self->reference, c_name); + err = git_reference_symbolic_set_target(self->reference, c_name); free(c_name); if (err < 0) { Error_set(err); @@ -269,7 +273,7 @@ Reference_get_oid(Reference *self) CHECK_REFERENCE(self); /* Get the oid (only for "direct" references) */ - oid = git_reference_oid(self->reference); + oid = git_reference_target(self->reference); if (oid == NULL) { PyErr_SetString(PyExc_ValueError, "oid is only available if the reference is direct " @@ -297,7 +301,7 @@ Reference_set_oid(Reference *self, PyObject *py_hex) } /* Set the oid */ - err = git_reference_set_oid(self->reference, &oid); + err = git_reference_set_target(self->reference, &oid); if (err < 0) { Error_set(err); return -1; @@ -314,7 +318,7 @@ Reference_get_hex(Reference *self) CHECK_REFERENCE(self); /* Get the oid (only for "direct" references) */ - oid = git_reference_oid(self->reference); + oid = git_reference_target(self->reference); if (oid == NULL) { PyErr_SetString(PyExc_ValueError, "oid is only available if the reference is direct " diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 4b3ae179a..b72ccf302 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -179,7 +179,7 @@ Repository_head(Repository *self) return NULL; } - oid = git_reference_oid(head); + oid = git_reference_target(head); pyobj = lookup_object(self, oid, GIT_OBJ_COMMIT); git_reference_free(head); return pyobj; @@ -527,7 +527,7 @@ Repository_create_blob_fromfile(Repository *self, PyObject *args) if (!PyArg_ParseTuple(args, "s", &path)) return NULL; - err = git_blob_create_fromfile(&oid, self->repo, path); + err = git_blob_create_fromworkdir(&oid, self->repo, path); if (err < 0) return Error_set(err); @@ -740,7 +740,7 @@ Repository_create_reference(Repository *self, PyObject *args, PyObject* keywds) return Error_set(err); } - err = git_reference_create_oid(&c_reference, self->repo, c_name, &oid, force); + err = git_reference_create(&c_reference, self->repo, c_name, &oid, force); } else { #if PY_MAJOR_VERSION == 2 c_target = PyString_AsString(py_obj); @@ -750,7 +750,7 @@ Repository_create_reference(Repository *self, PyObject *args, PyObject* keywds) if(c_target == NULL) return NULL; - err = git_reference_create_symbolic(&c_reference, self->repo, c_name, + err = git_reference_symbolic_create(&c_reference, self->repo, c_name, c_target, force); } diff --git a/src/pygit2/tag.c b/src/pygit2/tag.c index c34dbc004..d4745d21c 100644 --- a/src/pygit2/tag.c +++ b/src/pygit2/tag.c @@ -39,7 +39,7 @@ Tag_get_target(Tag *self) { const git_oid *oid; - oid = git_tag_target_oid(self->tag); + oid = git_tag_target_id(self->tag); return git_oid_to_python(oid->id); } diff --git a/test/test_refs.py b/test/test_refs.py index b61d855f8..082e53df1 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -174,6 +174,7 @@ def test_create_reference(self): self.assertTrue('refs/tags/version1' in refs) reference = self.repo.lookup_reference('refs/tags/version1') self.assertEqual(reference.hex, LAST_COMMIT) + self.assertEqual(reference.target, LAST_COMMIT) # try to create existing reference self.assertRaises(ValueError, self.repo.create_reference, From e4097c5de5aa22b2e7656de3417b5bbd4e7abe18 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 28 Nov 2012 18:13:56 +0100 Subject: [PATCH 0306/2237] Fix uninitialized variable --- src/pygit2/diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index 8cd0d5136..b8ba98925 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -81,7 +81,7 @@ static int diff_hunk_cb( PyObject *hunks; Hunk *hunk; int len; - char* old_path, *new_path; + char* old_path = NULL, *new_path = NULL; char oid[GIT_OID_HEXSZ]; From ca2c0d75c5683ffa407b91e092aa3a616a886dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 12 Dec 2012 18:58:30 +0100 Subject: [PATCH 0307/2237] Update to latest libgit2 Specifically to changes made by ligbit2's pull-request number #1115: https://github.com/libgit2/libgit2/pull/1115 --- src/pygit2/diff.c | 2 +- src/pygit2/index.c | 2 +- src/pygit2/tree.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c index b8ba98925..e31a08bd7 100644 --- a/src/pygit2/diff.c +++ b/src/pygit2/diff.c @@ -360,7 +360,7 @@ PyObject * Diff_find_similar(Diff *self, PyObject *args) { int err; - git_diff_find_options opts = {0}; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; if (!PyArg_ParseTuple(args, "|i", &opts.flags)) return NULL; diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 2d1d8b51e..47730a135 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -105,7 +105,7 @@ Index_clear(Index *self) PyObject * Index_diff_tree(Index *self, PyObject *args) { - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; int err; diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index ccdeea679..c7fad3cda 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -269,7 +269,7 @@ Tree_getitem(Tree *self, PyObject *value) PyObject * Tree_diff_tree(Tree *self, PyObject *args) { - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; int err; From 0acb7df5647231e2be5a07a8e9117d4395dee59c Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 13 Dec 2012 08:18:36 -0500 Subject: [PATCH 0308/2237] setup.py: Add LIBGIT2_LIB to override the library search path I build libgit2 in a `build/` subdirectory of a Git checkout, and never bother installing it. I'd like to link pygit2 against it there, but LIBGIT2 assumes libgit2.so will be in the default library search path or $LIBGIT/lib. This patch adds LIBGIT2_LIB, a new environment variable for overriding the additional library search paths. An alternative to this patch is to set LDFLAGS="-L..." explicitly before building pygit2. I've avoided that approach here minimize the difference between configuring this library path explicitly, and configuring implicitly via LIBGIT2. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 59f5bc310..9f2d518b0 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ libgit2_bin = os.path.join(libgit2_path, 'bin') libgit2_include = os.path.join(libgit2_path, 'include') -libgit2_lib = os.path.join(libgit2_path, 'lib') +libgit2_lib = os.getenv('LIBGIT2_LIB', os.path.join(libgit2_path, 'lib')) pygit2_exts = [os.path.join('src', 'pygit2.c')] + [ os.path.join('src', 'pygit2', entry) for entry in os.listdir('src/pygit2') From 1e8b84a5b5f9d97814775eacff4e66e94cdf529d Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 13 Dec 2012 08:43:18 -0500 Subject: [PATCH 0309/2237] README.rst: document non-standard linking on *nix There have been a few comments on pygit2's issue tracker about linking problems on Unix/Linux [1]. David suggested that a README comment might be in order, so here it is. I think that people new to *nix linking should probably just stick to installing to standard locations, since a short README blurb is never going to do justice to a subject that probably deserves it's own chapter. Instead of explaining what's going on, I just give a quick recipe for building and installing libgit2 under /usr/local (the default location), and then building pygit2 against that libgit2 (using RUNPATH). [1]: https://github.com/libgit2/pygit2/issues/134 --- README.rst | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/README.rst b/README.rst index d7f88d5d1..09b0dd732 100644 --- a/README.rst +++ b/README.rst @@ -28,6 +28,67 @@ When those are installed, you can install pygit2:: $ python setup.py install $ python setup.py test +Building on \*nix (including OS X) +---------------------------------- + +If you installed libgit2 and pygit2 in one of the usual places, you +should be able to skip this section and just use the generic pygit2 +installation steps described above. This is the recommended +procedure. + +`Shared libraries`_ packaged by your distribution are usually in +``/usr/lib``. To keep manually installed libraries separate, they are +usually installed in ``/usr/local/lib``. If you installed libgit2 +using the default installation procedure (e.g. without specifying +``CMAKE_INSTALL_PREFIX``), you probably installed it under +``/usr/local/lib``. On some distributions (e.g. Ubuntu), +``/usr/local/lib`` is not in the linker's default search path (see the +`ld man page`_ for details), and you will get errors like: + + $ python -c 'import pygit2' + Traceback (most recent call last): + File "", line 1, in + File "pygit2/__init__.py", line 29, in + from _pygit2 import * + ImportError: libgit2.so.0: cannot open shared object file: No such file or directory + +The following recipe shows how to install libgit2 and pygit2 on these +systems. First, download and install libgit2 (following the +instructions in the libgit2 ``README.md``):: + + $ git clone git://github.com/libgit2/libgit2.git + $ mkdir libgit2/build + $ cd libgit2/build + $ cmake .. + $ cmake --build . + $ sudo cmake --build . --target install + $ cd ../.. + +Now, download and install pygit2. You will probably have to set the +``LIBGIT2`` environment variable so the compiler can find the libgit2 +headers and libraries. + + $ git clone git://github.com/libgit2/pygit2.git + $ cd pygit2 + $ export LIBGIT2="/usr/local" + $ export LDFLAGS="-Wl,-rpath='$LIBGIT2/lib',--enable-new-dtags $LDFLAGS" + $ python setup.py build + $ sudo python setup.py install + +This compiles the pygit2 libraries with a ``RUNPATH``, which bakes +extra library search paths directly into the binaries (see the `ld man +page`_ for details). With ``RUNPATH`` compiled in, you won't have to +use ``LD_LIBRARY_PATH``. You can check to ensure ``RUNPATH`` was set +with readelf_:: + + $ readelf --dynamic build/lib.linux-x86_64-3.2/_pygit2.cpython-32.so | grep PATH + 0x000000000000000f (RPATH) Library rpath: [/usr/local/lib] + 0x000000000000001d (RUNPATH) Library runpath: [/usr/local/lib] + +.. _Shared libraries: http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html +.. _ld man page: http://linux.die.net/man/1/ld +.. _readelf: http://www.gnu.org/software/binutils/ + Building on Windows ------------------- From d9abb1af594520d1f3035889b299261915f65525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 19 Dec 2012 13:46:54 +0100 Subject: [PATCH 0310/2237] Update to libgit2 changes in git_diff_* functions See libgit2's 56c72b759c3ad commit --- src/pygit2/index.c | 4 ++-- src/pygit2/tree.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 47730a135..556b0848c 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -116,13 +116,13 @@ Index_diff_tree(Index *self, PyObject *args) return NULL; if (py_obj == NULL) { - err = git_diff_workdir_to_index( + err = git_diff_index_to_workdir( &diff, self->repo->repo, self->index, &opts); } else if (PyObject_TypeCheck(py_obj, &TreeType)) { - err = git_diff_index_to_tree( + err = git_diff_tree_to_index( &diff, self->repo->repo, ((Tree *)py_obj)->tree, diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index c7fad3cda..fcbe88595 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -280,7 +280,7 @@ Tree_diff_tree(Tree *self, PyObject *args) return NULL; if (py_obj == NULL) { - err = git_diff_workdir_to_tree( + err = git_diff_tree_to_workdir( &diff, self->repo->repo, self->tree, @@ -293,7 +293,7 @@ Tree_diff_tree(Tree *self, PyObject *args) ((Tree *)py_obj)->tree, &opts); } else if (PyObject_TypeCheck(py_obj, &IndexType)) { - err = git_diff_index_to_tree( + err = git_diff_tree_to_index( &diff, self->repo->repo, self->tree, From 016c5d63be5ee61d38ffe39c97ed7283349d1c3e Mon Sep 17 00:00:00 2001 From: David Sanders Date: Wed, 19 Dec 2012 16:02:26 -0700 Subject: [PATCH 0311/2237] Correct rst syntax errors in README.rst --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 09b0dd732..bd2112b84 100644 --- a/README.rst +++ b/README.rst @@ -43,7 +43,7 @@ using the default installation procedure (e.g. without specifying ``CMAKE_INSTALL_PREFIX``), you probably installed it under ``/usr/local/lib``. On some distributions (e.g. Ubuntu), ``/usr/local/lib`` is not in the linker's default search path (see the -`ld man page`_ for details), and you will get errors like: +`ld man page`_ for details), and you will get errors like:: $ python -c 'import pygit2' Traceback (most recent call last): @@ -66,7 +66,7 @@ instructions in the libgit2 ``README.md``):: Now, download and install pygit2. You will probably have to set the ``LIBGIT2`` environment variable so the compiler can find the libgit2 -headers and libraries. +headers and libraries:: $ git clone git://github.com/libgit2/pygit2.git $ cd pygit2 From 72639d800c29c1f9d76ec9578a02af0b16e8d12b Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 21 Dec 2012 16:28:32 +0100 Subject: [PATCH 0312/2237] added pygit api link --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index d7f88d5d1..c0e51310d 100644 --- a/README.rst +++ b/README.rst @@ -7,6 +7,8 @@ pygit2 - libgit2 bindings in Python pygit2 is a set of Python bindings to the libgit2 linkable C Git library. The supported versions of Python are 2.6, 2.7, 3.1 and 3.2 +You can find an up-to-date api documentation at http://libgit2.github.com/pygit2. + Through this text Python 3 is used for the inline examples. Also, the Python 3 terminology is used (for instance we say text strings instead of unicode strings). From 3058181e1481578fed26d4af423cfd26f971516f Mon Sep 17 00:00:00 2001 From: richo Date: Fri, 4 Jan 2013 13:42:18 +1100 Subject: [PATCH 0313/2237] Implement Iterator protocol on Repository Uses a stupid implementation that builds a list in memory and returns it's iterator. --- src/pygit2/repository.c | 31 ++++++++++++++++++++++++++++++- test/test_repository.py | 7 +++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index b72ccf302..a7bb377e5 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -161,6 +161,35 @@ Repository_contains(Repository *self, PyObject *value) return exists; } +static int +Repository_build_as_iter(git_oid *oid, PyObject *accum) +{ + PyList_Append(accum, git_oid_to_py_str(oid)); + return 0; +} + +PyObject * +Repository_as_iter(Repository *self) +{ + git_odb *odb; + int err; + PyObject *accum = PyList_New(0); + + err = git_repository_odb(&odb, self->repo); + if (err < 0) { + Error_set(err); + return -1; + } + err = git_odb_foreach(odb, Repository_build_as_iter, accum); + if (err < 0) { + Error_set(err); + return -1; + } + + git_odb_free(odb); + return PyObject_GetIter(accum); +} + PyObject * Repository_head(Repository *self) { @@ -984,7 +1013,7 @@ PyTypeObject RepositoryType = { (inquiry)Repository_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ + (getiterfunc)Repository_as_iter, /* tp_iter */ 0, /* tp_iternext */ Repository_methods, /* tp_methods */ 0, /* tp_members */ diff --git a/test/test_repository.py b/test/test_repository.py index 10dd9f5fe..3acf00818 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -93,6 +93,13 @@ def test_contains(self): self.assertFalse('a' * 40 in self.repo) self.assertFalse('a' * 20 in self.repo) + def test_iterable(self): + l = [] + for obj in self.repo: + l.append(obj) + + self.assertTrue(A_HEX_SHA in l) + def test_lookup_blob(self): self.assertRaises(TypeError, lambda: self.repo[123]) self.assertEqual(self.repo[A_BIN_SHA].hex, A_HEX_SHA) From 77224e434ba1c04c14c37f652a8193e25438e4dd Mon Sep 17 00:00:00 2001 From: richo Date: Fri, 4 Jan 2013 22:55:56 +1100 Subject: [PATCH 0314/2237] Ensure that odb is always freed --- src/pygit2/repository.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index a7bb377e5..674c8a920 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -182,6 +182,7 @@ Repository_as_iter(Repository *self) } err = git_odb_foreach(odb, Repository_build_as_iter, accum); if (err < 0) { + git_odb_free(odb); Error_set(err); return -1; } From b65244282ecce3ac2a6d8750df911524e87ae23a Mon Sep 17 00:00:00 2001 From: richo Date: Fri, 4 Jan 2013 23:01:19 +1100 Subject: [PATCH 0315/2237] Handle errors while building the list gracefully --- src/pygit2/repository.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 674c8a920..63fc5de5d 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -164,7 +164,13 @@ Repository_contains(Repository *self, PyObject *value) static int Repository_build_as_iter(git_oid *oid, PyObject *accum) { - PyList_Append(accum, git_oid_to_py_str(oid)); + int err; + + err = PyList_Append(accum, git_oid_to_py_str(oid)); + if (err < 0) { + Error_set(err); + return -1; + } return 0; } @@ -181,7 +187,10 @@ Repository_as_iter(Repository *self) return -1; } err = git_odb_foreach(odb, Repository_build_as_iter, accum); - if (err < 0) { + if (err == GIT_EUSER) { + git_odb_free(odb); + return -1; + } else if (err < 0) { git_odb_free(odb); Error_set(err); return -1; From 63d319895c9401b24678f05d3d022b1279548f54 Mon Sep 17 00:00:00 2001 From: richo Date: Fri, 4 Jan 2013 23:03:03 +1100 Subject: [PATCH 0316/2237] Don't leave dangling references to oid strings --- src/pygit2/repository.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 63fc5de5d..bc0a2b8c6 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -165,12 +165,14 @@ static int Repository_build_as_iter(git_oid *oid, PyObject *accum) { int err; + PyObject *oid_str = git_oid_to_py_str(oid); - err = PyList_Append(accum, git_oid_to_py_str(oid)); + err = PyList_Append(accum, oid_str); if (err < 0) { Error_set(err); return -1; } + Py_XDECREF(oid_str); return 0; } From badb7f435d6c83340bc2d6c8575399aef87dea1b Mon Sep 17 00:00:00 2001 From: richo Date: Sat, 5 Jan 2013 02:20:40 +1100 Subject: [PATCH 0317/2237] Always decref the new string. Don't set err. --- src/pygit2/repository.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index bc0a2b8c6..f5f31d315 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -168,12 +168,8 @@ Repository_build_as_iter(git_oid *oid, PyObject *accum) PyObject *oid_str = git_oid_to_py_str(oid); err = PyList_Append(accum, oid_str); - if (err < 0) { - Error_set(err); - return -1; - } - Py_XDECREF(oid_str); - return 0; + Py_DECREF(oid_str); + return err; } PyObject * From ce6663eea54b488b9dff261c0115af299b4dc433 Mon Sep 17 00:00:00 2001 From: richo Date: Sat, 5 Jan 2013 11:15:50 +1100 Subject: [PATCH 0318/2237] Shamelessly add richo to AUTHORS --- AUTHORS | 1 + README.rst | 1 + 2 files changed, 2 insertions(+) diff --git a/AUTHORS b/AUTHORS index 64fa00dba..90b8db912 100644 --- a/AUTHORS +++ b/AUTHORS @@ -21,6 +21,7 @@ Martin Lenders Nico von Geyso Petr Hosek Petr Viktorin +Richo Healey Rui Abreu Ferreira Sarath Lakshman Sebastian Thiel diff --git a/README.rst b/README.rst index 1a799a728..866e0a928 100644 --- a/README.rst +++ b/README.rst @@ -423,6 +423,7 @@ pygit2 project (sorted alphabetically): - Nico von Geyso - Petr Hosek - Petr Viktorin +- Richo Healey - Rui Abreu Ferreira - Sarath Lakshman - Sebastian Thiel From 6f6e1c255676724f6e4c8169d4bf223f4867e789 Mon Sep 17 00:00:00 2001 From: richo Date: Sat, 5 Jan 2013 11:39:37 +1100 Subject: [PATCH 0319/2237] Use correct prototypes --- src/pygit2/repository.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index f5f31d315..5d0fc84a3 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -162,12 +162,12 @@ Repository_contains(Repository *self, PyObject *value) } static int -Repository_build_as_iter(git_oid *oid, PyObject *accum) +Repository_build_as_iter(const git_oid *oid, void *accum) { int err; PyObject *oid_str = git_oid_to_py_str(oid); - err = PyList_Append(accum, oid_str); + err = PyList_Append((PyObject*)accum, oid_str); Py_DECREF(oid_str); return err; } @@ -184,7 +184,7 @@ Repository_as_iter(Repository *self) Error_set(err); return -1; } - err = git_odb_foreach(odb, Repository_build_as_iter, accum); + err = git_odb_foreach(odb, Repository_build_as_iter, (void*)accum); if (err == GIT_EUSER) { git_odb_free(odb); return -1; From 4b42efbc8a19e2b17501377cbc102195d3beb236 Mon Sep 17 00:00:00 2001 From: richo Date: Sat, 5 Jan 2013 11:39:53 +1100 Subject: [PATCH 0320/2237] return NULL over -1 --- src/pygit2/repository.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 5d0fc84a3..daa2c4465 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -182,16 +182,16 @@ Repository_as_iter(Repository *self) err = git_repository_odb(&odb, self->repo); if (err < 0) { Error_set(err); - return -1; + return NULL; } err = git_odb_foreach(odb, Repository_build_as_iter, (void*)accum); if (err == GIT_EUSER) { git_odb_free(odb); - return -1; + return NULL; } else if (err < 0) { git_odb_free(odb); Error_set(err); - return -1; + return NULL; } git_odb_free(odb); From 9ec56c07ea46067cba9185443ec21962921bdb81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 5 Jan 2013 10:36:09 +0100 Subject: [PATCH 0321/2237] Make Repostirory_as_iter shorter Error_set already returns NULL --- src/pygit2/repository.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index daa2c4465..4dc7c34d3 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -180,21 +180,17 @@ Repository_as_iter(Repository *self) PyObject *accum = PyList_New(0); err = git_repository_odb(&odb, self->repo); - if (err < 0) { - Error_set(err); - return NULL; - } + if (err < 0) + return Error_set(err); + err = git_odb_foreach(odb, Repository_build_as_iter, (void*)accum); + git_odb_free(odb); if (err == GIT_EUSER) { - git_odb_free(odb); return NULL; } else if (err < 0) { - git_odb_free(odb); - Error_set(err); - return NULL; + return Error_set(err); } - git_odb_free(odb); return PyObject_GetIter(accum); } From b63982d10268d6ca8ddc714b3f21e1b9f636b3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 6 Jan 2013 09:11:59 +0100 Subject: [PATCH 0322/2237] Readme: add table of contents --- README.rst | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/README.rst b/README.rst index 866e0a928..a35230826 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,7 @@ + +###################################################################### pygit2 - libgit2 bindings in Python -===================================== +###################################################################### .. image:: https://secure.travis-ci.org/libgit2/pygit2.png :target: http://travis-ci.org/libgit2/pygit2 @@ -13,8 +15,11 @@ Through this text Python 3 is used for the inline examples. Also, the Python 3 terminology is used (for instance we say text strings instead of unicode strings). -INSTALLING AND RUNNING -======================== +.. contents:: + +********************************************************************** +Installing and running +********************************************************************** First you need to install the latest version of libgit2. You can find platform-specific instructions to build the library in the libgit2 website: @@ -31,7 +36,7 @@ When those are installed, you can install pygit2:: $ python setup.py test Building on \*nix (including OS X) ----------------------------------- +=================================== If you installed libgit2 and pygit2 in one of the usual places, you should be able to skip this section and just use the generic pygit2 @@ -92,7 +97,7 @@ with readelf_:: .. _readelf: http://www.gnu.org/software/binutils/ Building on Windows -------------------- +=================================== pygit2 expects to find the libgit2 installed files in the directory specified in the ``LIBGIT2`` environment variable. @@ -114,6 +119,10 @@ At this point, you're ready to execute the generic pygit2 installation steps described above. +********************************************************************** +Usage +********************************************************************** + The repository ================= @@ -384,20 +393,21 @@ Inspect the status of the repository:: ... print "Filepath %s isn't clean" % filepath -CONTRIBUTING -============== +********************************************************************** +Contributing +********************************************************************** Fork libgit2/pygit2 on GitHub, make it awesomer (preferably in a branch named for the topic), send a pull request. TODO ----------------- +================= See issues -AUTHORS +Authors ============== The following people have contributed at least one patch to the @@ -433,7 +443,7 @@ pygit2 project (sorted alphabetically): - pistacchio -LICENSE +License ============== GPLv2 with linking exception. See COPYING for more details. From 139ea8d67c121ee3d6f837d65dae2f58562bec24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 6 Jan 2013 09:57:03 +0100 Subject: [PATCH 0323/2237] sphinx-quickstart --- docs/Makefile | 153 +++++++++++++++++++++++++++++++ docs/conf.py | 242 +++++++++++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 22 +++++ 3 files changed, 417 insertions(+) create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/index.rst diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 000000000..d394e42d8 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pygit2.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pygit2.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/pygit2" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pygit2" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 000000000..40954ae4f --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,242 @@ +# -*- coding: utf-8 -*- +# +# pygit2 documentation build configuration file, created by +# sphinx-quickstart on Sun Jan 6 09:55:26 2013. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'pygit2' +copyright = u'2013, J. David Ibáñez' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.17' +# The full version, including alpha/beta/rc tags. +release = '0.17.3' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'pygit2doc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'pygit2.tex', u'pygit2 Documentation', + u'J. David Ibáñez', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'pygit2', u'pygit2 Documentation', + [u'J. David Ibáñez'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'pygit2', u'pygit2 Documentation', + u'J. David Ibáñez', 'pygit2', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 000000000..25cb550f4 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,22 @@ +.. pygit2 documentation master file, created by + sphinx-quickstart on Sun Jan 6 09:55:26 2013. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to pygit2's documentation! +================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + From 0ff85b8fea3d220c456972e829c0eb6529b6d014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 12 Jan 2013 18:42:12 +0100 Subject: [PATCH 0324/2237] Use autodocs --- .gitignore | 1 + docs/_static/.keep | 0 docs/index.rst | 2 ++ docs/pygit2.rst | 14 ++++++++++++++ 4 files changed, 17 insertions(+) create mode 100644 docs/_static/.keep create mode 100644 docs/pygit2.rst diff --git a/.gitignore b/.gitignore index 5111fd0ff..972131dfd 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ pygit2/*.pyc pygit2/__pycache__ *.egg-info *.swp +docs/_build diff --git a/docs/_static/.keep b/docs/_static/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/docs/index.rst b/docs/index.rst index 25cb550f4..6f7790f78 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,6 +11,8 @@ Contents: .. toctree:: :maxdepth: 2 + pygit2 + Indices and tables diff --git a/docs/pygit2.rst b/docs/pygit2.rst new file mode 100644 index 000000000..d266f0411 --- /dev/null +++ b/docs/pygit2.rst @@ -0,0 +1,14 @@ +:mod:`pygit2` +============= + +.. Util functions +.. automodule:: pygit2.utils + :members: + :show-inheritance: + :undoc-members: + +.. c extension +.. automodule:: _pygit2 + :members: + :show-inheritance: + :undoc-members: From 29708773e0004bbcb2b343ef161b553688fc7817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 12 Jan 2013 19:03:05 +0100 Subject: [PATCH 0325/2237] Copy docs from the readme file --- docs/index.rst | 11 +- docs/install.rst | 104 +++++++++++ docs/{pygit2.rst => reference.rst} | 2 +- docs/usage.rst | 275 +++++++++++++++++++++++++++++ 4 files changed, 390 insertions(+), 2 deletions(-) create mode 100644 docs/install.rst rename docs/{pygit2.rst => reference.rst} (93%) create mode 100644 docs/usage.rst diff --git a/docs/index.rst b/docs/index.rst index 6f7790f78..8c527a3e6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,12 +6,21 @@ Welcome to pygit2's documentation! ================================== +.. image:: https://secure.travis-ci.org/libgit2/pygit2.png + :target: http://travis-ci.org/libgit2/pygit2 + +pygit2 is a set of Python bindings to the libgit2 linkable C Git library. +The supported versions of Python are 2.6, 2.7, 3.1 and 3.2 + + Contents: .. toctree:: :maxdepth: 2 - pygit2 + install + usage + reference diff --git a/docs/install.rst b/docs/install.rst new file mode 100644 index 000000000..8728b9374 --- /dev/null +++ b/docs/install.rst @@ -0,0 +1,104 @@ +********************************************************************** +How to Install +********************************************************************** + + +.. contents:: + + +First you need to install the latest version of libgit2. +You can find platform-specific instructions to build the library in the libgit2 website: + + http://libgit2.github.com + +Also, make sure you have Python 2.6+ installed together with the Python development headers. + +When those are installed, you can install pygit2:: + + $ git clone git://github.com/libgit2/pygit2.git + $ cd pygit2 + $ python setup.py install + $ python setup.py test + +Building on \*nix (including OS X) +=================================== + +If you installed libgit2 and pygit2 in one of the usual places, you +should be able to skip this section and just use the generic pygit2 +installation steps described above. This is the recommended +procedure. + +`Shared libraries`_ packaged by your distribution are usually in +``/usr/lib``. To keep manually installed libraries separate, they are +usually installed in ``/usr/local/lib``. If you installed libgit2 +using the default installation procedure (e.g. without specifying +``CMAKE_INSTALL_PREFIX``), you probably installed it under +``/usr/local/lib``. On some distributions (e.g. Ubuntu), +``/usr/local/lib`` is not in the linker's default search path (see the +`ld man page`_ for details), and you will get errors like:: + + $ python -c 'import pygit2' + Traceback (most recent call last): + File "", line 1, in + File "pygit2/__init__.py", line 29, in + from _pygit2 import * + ImportError: libgit2.so.0: cannot open shared object file: No such file or directory + +The following recipe shows how to install libgit2 and pygit2 on these +systems. First, download and install libgit2 (following the +instructions in the libgit2 ``README.md``):: + + $ git clone git://github.com/libgit2/libgit2.git + $ mkdir libgit2/build + $ cd libgit2/build + $ cmake .. + $ cmake --build . + $ sudo cmake --build . --target install + $ cd ../.. + +Now, download and install pygit2. You will probably have to set the +``LIBGIT2`` environment variable so the compiler can find the libgit2 +headers and libraries:: + + $ git clone git://github.com/libgit2/pygit2.git + $ cd pygit2 + $ export LIBGIT2="/usr/local" + $ export LDFLAGS="-Wl,-rpath='$LIBGIT2/lib',--enable-new-dtags $LDFLAGS" + $ python setup.py build + $ sudo python setup.py install + +This compiles the pygit2 libraries with a ``RUNPATH``, which bakes +extra library search paths directly into the binaries (see the `ld man +page`_ for details). With ``RUNPATH`` compiled in, you won't have to +use ``LD_LIBRARY_PATH``. You can check to ensure ``RUNPATH`` was set +with readelf_:: + + $ readelf --dynamic build/lib.linux-x86_64-3.2/_pygit2.cpython-32.so | grep PATH + 0x000000000000000f (RPATH) Library rpath: [/usr/local/lib] + 0x000000000000001d (RUNPATH) Library runpath: [/usr/local/lib] + +.. _Shared libraries: http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html +.. _ld man page: http://linux.die.net/man/1/ld +.. _readelf: http://www.gnu.org/software/binutils/ + +Building on Windows +=================================== + +pygit2 expects to find the libgit2 installed files in the directory specified +in the ``LIBGIT2`` environment variable. + +In addition, make sure that libgit2 is build in "__cdecl" mode. +The following recipe shows you how to do it, assuming you're working +from a bash shell:: + + $ export LIBGIT2=C:/Dev/libgit2 + $ git clone git://github.com/libgit2/libgit2.git + $ cd libgit2 + $ mkdir build + $ cd build + $ cmake .. -DSTDCALL=OFF -DCMAKE_INSTALL_PREFIX=$LIBGIT2 -G "Visual Studio 9 2008" + $ cmake --build . --config release --target install + $ ctest -v + +At this point, you're ready to execute the generic pygit2 installation +steps described above. diff --git a/docs/pygit2.rst b/docs/reference.rst similarity index 93% rename from docs/pygit2.rst rename to docs/reference.rst index d266f0411..044ae5433 100644 --- a/docs/pygit2.rst +++ b/docs/reference.rst @@ -1,4 +1,4 @@ -:mod:`pygit2` +Reference ============= .. Util functions diff --git a/docs/usage.rst b/docs/usage.rst new file mode 100644 index 000000000..83602f8d5 --- /dev/null +++ b/docs/usage.rst @@ -0,0 +1,275 @@ +********************************************************************** +Usage samples +********************************************************************** + +.. contents:: + + +The repository +================= + +Everything starts by opening an existing repository:: + + >>> from pygit2 import Repository + >>> repo = Repository('pygit2/.git') + +Or by creating a new one:: + + >>> from pygit2 import init_repository + >>> bare = False + >>> repo = init_repository('test', bare) + +These are the basic attributes of a repository:: + + Repository.path -- path to the Git repository + Repository.workdir -- path to the working directory, None in the case of + a bare repo + + +Git objects +=========== + +In the first place Git is a key-value storage system. The values stored are +called *objects*, there are four types (commits, trees, blobs and tags), +for each type pygit2 has a Python class:: + + # Get the last commit + >>> head = repo.head + + # Show commits and trees + >>> commit + + >>> commit.tree + + +These four classes (``Commit``, ``Tree``, ``Blob`` and ``Tag``) inherit from +the ``Object`` base class, which provides shared behaviour. A Git object is +identified by a unique *object id*, which is a binary byte string; this is +often represented as an hexadecimal text string:: + + >>> commit.oid + b'x\xde\xb5W\x8d\x01<\xdb\xdf\x08o\xa1\xd1\xa3\xe7\xd9\x82\xe8\x88\x8f' + >>> commit.hex + '78deb5578d013cdbdf086fa1d1a3e7d982e8888f' + +The API of pygit2 accepts both the raw object id and its hexadecimal +representation, the difference is done based on its type (a byte or a text +string). + +This is the common interface for all Git objects:: + + Object.type -- one of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, + GIT_OBJ_BLOB or GIT_OBJ_TAG constants + Object.oid -- the object id, a byte string 20 bytes long + Object.hex -- hexadecimal representation of the object id, a text + string 40 chars long + Object.read_raw() -- returns the byte string with the raw contents of the + of the object + +Objects can not be modified once they have been created. + + +Commits +----------------- + +A commit is a snapshot of the working dir with meta informations like author, +committer and others.:: + + Commit.author -- the author of the commit + Commit.committer -- the committer of the commit + Commit.message -- the message, a text string + Commit.tree -- the tree object attached to the commit + Commit.parents -- the list of parent commits + + +Signatures +............. + +The author and committer attributes of commit objects are ``Signature`` +objects:: + + >>> commit.author + + +This is their interface:: + + Signature.name -- person's name + Signature.email -- person's email address + Signature.time -- unix time + Signature.offset -- offset from utc in minutes + + +Creating commits +................ + +Commits can be created by calling the ``create_commit`` method of the +repository with the following parameters:: + + >>> author = Signature('Alice Author', 'alice@authors.tld') + >>> committer = Signature('Cecil Committer', 'cecil@committers.tld') + >>> tree = repo.TreeBuilder().write() + >>> repo.create_commit( + ... 'refs/heads/master', # the name of the reference to update + ... author, committer, 'one line commit message\n\ndetailed commit message', + ... tree, # binary string representing the tree object ID + ... [] # list of binary strings representing parents of the new commit + ... ) + '#\xe4>> tree = commit.tree + >>> len(tree) + 6 + + # Iteration + >>> for entry in tree: + ... print(entry.hex, entry.name) + ... + 7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa .gitignore + c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 COPYING + 32b30b90b062f66957d6790c3c155c289c34424e README.md + c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 pygit2.c + 85a67270a49ef16cdd3d328f06a3e4b459f09b27 setup.py + 3d8985bbec338eb4d47c5b01b863ee89d044bd53 test + + # Get an entry by name + >>> entry = tree['pygit2.c'] + >>> entry + + + # Get the object the entry points to + >>> blob = repo[entry.oid] + >>> blob + + +This is the interface of a tree entry:: + + TreeEntry.name -- name of the tree entry + TreeEntry.oid -- the id of the git object + TreeEntry.hex -- hexadecimal representation of the oid + TreeEntry.filemode -- the Unix file attributes + TreeEntry.to_object() -- returns the git object (equivalent to repo[entry.oid]) + + +Diff +----------------- + +A diff shows the changes between trees, an index or the working dir:: + + # Diff two trees + >>> t0 = repo.head.tree + >>> t1 = repo.head.parents[0].tree + >>> diff = t1.diff(t0) + >>> diff + + # Diff a tree with the index + >>> tree = repo.head.tree + >>> diff = tree.diff(repo.index) + + # Diff a tree with the current working dir + >>> tree = repo.head.tree + >>> diff = tree.diff() + +The interface for a diff:: + + Diff.changes -- Dict of 'files' and 'hunks' for every change + Diff.patch -- a patch for every changeset + Diff.merge -- Merge two Diffs + + +Blobs +----------------- + +A blob is equivalent to a file in a file system.:: + + # create a blob out of memory + >>> oid = repo.create_blob('foo bar') + >>> blob = repo[oid] + + Blob.data -- the contents of the blob, a byte string + +Tags +----------------- + +A tag is a static label for a commit. See references for more information. + + + +References +================= + +Reference lookup:: + + >>> all_refs = repo.listall_references() + >>> master_ref = repo.lookup_reference("refs/heads/master") + >>> commit = repo[master_ref.oid] + +Reference log:: + + >>> head = repo.lookup_reference('refs/heads/master') + >>> for entry in head.log(): + ... print(entry.message) + +The interface for RefLogEntry:: + + RefLogEntry.committer -- Signature of Committer + RefLogEntry.message -- the message of the RefLogEntry + RefLogEntry.oid_old -- oid of old reference + RefLogEntry.oid_new -- oid of new reference + +Revision parsing +================ + +You can use any of the fancy `` forms supported by libgit2:: + + >>> commit = repo.revparse_single('HEAD^') + +Revision walking +================= + +You can iterate through the revision history with repo.walk:: + + >>> from pygit2 import GIT_SORT_TIME + >>> for commit in repo.walk(oid, GIT_SORT_TIME): + ... print(commit.hex) + +The index file +================= + +Index read:: + + >>> index = repo.index + >>> index.read() + >>> oid = index['path/to/file'].oid # from path to object id + >>> blob = repo[oid] # from object id to object + +Iterate over all entries of the index:: + + >>> for entry in index: + ... print entry.path, entry.hex + +Index write:: + + >>> index.add('path/to/file') # git add + >>> del index['path/to/file'] # git rm + >>> index.write() # don't forget to save the changes + +Status +================= + +Inspect the status of the repository:: + + >>> from pygit2 import GIT_STATUS_CURRENT + >>> status = repo.status() + >>> for filepath, flags in status.items(): + ... if flags != GIT_STATUS_CURRENT: + ... print "Filepath %s isn't clean" % filepath From c725ee7f05ebfeacdfd4927ec8b9832580ae1367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 13 Jan 2013 12:13:25 +0100 Subject: [PATCH 0326/2237] Simplify README file --- README.rst | 426 +++++-------------------------------------------- docs/index.rst | 11 +- 2 files changed, 49 insertions(+), 388 deletions(-) diff --git a/README.rst b/README.rst index a35230826..4761e2958 100644 --- a/README.rst +++ b/README.rst @@ -6,407 +6,40 @@ pygit2 - libgit2 bindings in Python .. image:: https://secure.travis-ci.org/libgit2/pygit2.png :target: http://travis-ci.org/libgit2/pygit2 -pygit2 is a set of Python bindings to the libgit2 linkable C Git library. -The supported versions of Python are 2.6, 2.7, 3.1 and 3.2 +Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2 +implements the core of Git. Pygit2 works with Python 2.6, 2.7, 3.1 and 3.2 -You can find an up-to-date api documentation at http://libgit2.github.com/pygit2. +Pygit2 links: -Through this text Python 3 is used for the inline examples. Also, the Python -3 terminology is used (for instance we say text strings instead of unicode -strings). +- http://github.com/libgit2/pygit2 -- Source code and issue tracker +- http://www.pygit2.org/ -- Documentation +- http://pypi.python.org/pypi/pygit2 -- Download -.. contents:: -********************************************************************** -Installing and running -********************************************************************** +Quick install guide +=================== -First you need to install the latest version of libgit2. -You can find platform-specific instructions to build the library in the libgit2 website: +1. Download libgit2 v0.17.0 + https://github.com/downloads/libgit2/libgit2/libgit2-0.17.0.tar.gz - http://libgit2.github.com +2. Build and install libgit2 + http://libgit2.github.com/#install -Also, make sure you have Python 2.6+ installed together with the Python development headers. +3. Install pygit2 with *pip*:: -When those are installed, you can install pygit2:: + $ pip install pygit2 - $ git clone git://github.com/libgit2/pygit2.git - $ cd pygit2 - $ python setup.py install - $ python setup.py test +For detailed instructions check the documentation, +http://www.pygit2.org/install.html -Building on \*nix (including OS X) -=================================== -If you installed libgit2 and pygit2 in one of the usual places, you -should be able to skip this section and just use the generic pygit2 -installation steps described above. This is the recommended -procedure. - -`Shared libraries`_ packaged by your distribution are usually in -``/usr/lib``. To keep manually installed libraries separate, they are -usually installed in ``/usr/local/lib``. If you installed libgit2 -using the default installation procedure (e.g. without specifying -``CMAKE_INSTALL_PREFIX``), you probably installed it under -``/usr/local/lib``. On some distributions (e.g. Ubuntu), -``/usr/local/lib`` is not in the linker's default search path (see the -`ld man page`_ for details), and you will get errors like:: - - $ python -c 'import pygit2' - Traceback (most recent call last): - File "", line 1, in - File "pygit2/__init__.py", line 29, in - from _pygit2 import * - ImportError: libgit2.so.0: cannot open shared object file: No such file or directory - -The following recipe shows how to install libgit2 and pygit2 on these -systems. First, download and install libgit2 (following the -instructions in the libgit2 ``README.md``):: - - $ git clone git://github.com/libgit2/libgit2.git - $ mkdir libgit2/build - $ cd libgit2/build - $ cmake .. - $ cmake --build . - $ sudo cmake --build . --target install - $ cd ../.. - -Now, download and install pygit2. You will probably have to set the -``LIBGIT2`` environment variable so the compiler can find the libgit2 -headers and libraries:: - - $ git clone git://github.com/libgit2/pygit2.git - $ cd pygit2 - $ export LIBGIT2="/usr/local" - $ export LDFLAGS="-Wl,-rpath='$LIBGIT2/lib',--enable-new-dtags $LDFLAGS" - $ python setup.py build - $ sudo python setup.py install - -This compiles the pygit2 libraries with a ``RUNPATH``, which bakes -extra library search paths directly into the binaries (see the `ld man -page`_ for details). With ``RUNPATH`` compiled in, you won't have to -use ``LD_LIBRARY_PATH``. You can check to ensure ``RUNPATH`` was set -with readelf_:: - - $ readelf --dynamic build/lib.linux-x86_64-3.2/_pygit2.cpython-32.so | grep PATH - 0x000000000000000f (RPATH) Library rpath: [/usr/local/lib] - 0x000000000000001d (RUNPATH) Library runpath: [/usr/local/lib] - -.. _Shared libraries: http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html -.. _ld man page: http://linux.die.net/man/1/ld -.. _readelf: http://www.gnu.org/software/binutils/ - -Building on Windows -=================================== - -pygit2 expects to find the libgit2 installed files in the directory specified -in the ``LIBGIT2`` environment variable. - -In addition, make sure that libgit2 is build in "__cdecl" mode. -The following recipe shows you how to do it, assuming you're working -from a bash shell:: - - $ export LIBGIT2=C:/Dev/libgit2 - $ git clone git://github.com/libgit2/libgit2.git - $ cd libgit2 - $ mkdir build - $ cd build - $ cmake .. -DSTDCALL=OFF -DCMAKE_INSTALL_PREFIX=$LIBGIT2 -G "Visual Studio 9 2008" - $ cmake --build . --config release --target install - $ ctest -v - -At this point, you're ready to execute the generic pygit2 installation -steps described above. - - -********************************************************************** -Usage -********************************************************************** - -The repository -================= - -Everything starts by opening an existing repository:: - - >>> from pygit2 import Repository - >>> repo = Repository('pygit2/.git') - -Or by creating a new one:: - - >>> from pygit2 import init_repository - >>> bare = False - >>> repo = init_repository('test', bare) - -These are the basic attributes of a repository:: - - Repository.path -- path to the Git repository - Repository.workdir -- path to the working directory, None in the case of - a bare repo - - -Git objects -=========== - -In the first place Git is a key-value storage system. The values stored are -called *objects*, there are four types (commits, trees, blobs and tags), -for each type pygit2 has a Python class:: - - # Get the last commit - >>> head = repo.head - - # Show commits and trees - >>> commit - - >>> commit.tree - - -These four classes (``Commit``, ``Tree``, ``Blob`` and ``Tag``) inherit from -the ``Object`` base class, which provides shared behaviour. A Git object is -identified by a unique *object id*, which is a binary byte string; this is -often represented as an hexadecimal text string:: - - >>> commit.oid - b'x\xde\xb5W\x8d\x01<\xdb\xdf\x08o\xa1\xd1\xa3\xe7\xd9\x82\xe8\x88\x8f' - >>> commit.hex - '78deb5578d013cdbdf086fa1d1a3e7d982e8888f' - -The API of pygit2 accepts both the raw object id and its hexadecimal -representation, the difference is done based on its type (a byte or a text -string). - -This is the common interface for all Git objects:: - - Object.type -- one of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, - GIT_OBJ_BLOB or GIT_OBJ_TAG constants - Object.oid -- the object id, a byte string 20 bytes long - Object.hex -- hexadecimal representation of the object id, a text - string 40 chars long - Object.read_raw() -- returns the byte string with the raw contents of the - of the object - -Objects can not be modified once they have been created. - - -Commits ------------------ - -A commit is a snapshot of the working dir with meta informations like author, -committer and others.:: - - Commit.author -- the author of the commit - Commit.committer -- the committer of the commit - Commit.message -- the message, a text string - Commit.tree -- the tree object attached to the commit - Commit.parents -- the list of parent commits - - -Signatures -............. - -The author and committer attributes of commit objects are ``Signature`` -objects:: - - >>> commit.author - - -This is their interface:: - - Signature.name -- person's name - Signature.email -- person's email address - Signature.time -- unix time - Signature.offset -- offset from utc in minutes - - -Creating commits -................ - -Commits can be created by calling the ``create_commit`` method of the -repository with the following parameters:: - - >>> author = Signature('Alice Author', 'alice@authors.tld') - >>> committer = Signature('Cecil Committer', 'cecil@committers.tld') - >>> tree = repo.TreeBuilder().write() - >>> repo.create_commit( - ... 'refs/heads/master', # the name of the reference to update - ... author, committer, 'one line commit message\n\ndetailed commit message', - ... tree, # binary string representing the tree object ID - ... [] # list of binary strings representing parents of the new commit - ... ) - '#\xe4>> tree = commit.tree - >>> len(tree) - 6 - - # Iteration - >>> for entry in tree: - ... print(entry.hex, entry.name) - ... - 7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa .gitignore - c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 COPYING - 32b30b90b062f66957d6790c3c155c289c34424e README.md - c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 pygit2.c - 85a67270a49ef16cdd3d328f06a3e4b459f09b27 setup.py - 3d8985bbec338eb4d47c5b01b863ee89d044bd53 test - - # Get an entry by name - >>> entry = tree['pygit2.c'] - >>> entry - - - # Get the object the entry points to - >>> blob = repo[entry.oid] - >>> blob - - -This is the interface of a tree entry:: - - TreeEntry.name -- name of the tree entry - TreeEntry.oid -- the id of the git object - TreeEntry.hex -- hexadecimal representation of the oid - TreeEntry.filemode -- the Unix file attributes - TreeEntry.to_object() -- returns the git object (equivalent to repo[entry.oid]) - - -Diff ------------------ - -A diff shows the changes between trees, an index or the working dir:: - - # Diff two trees - >>> t0 = repo.head.tree - >>> t1 = repo.head.parents[0].tree - >>> diff = t1.diff(t0) - >>> diff - - # Diff a tree with the index - >>> tree = repo.head.tree - >>> diff = tree.diff(repo.index) - - # Diff a tree with the current working dir - >>> tree = repo.head.tree - >>> diff = tree.diff() - -The interface for a diff:: - - Diff.changes -- Dict of 'files' and 'hunks' for every change - Diff.patch -- a patch for every changeset - Diff.merge -- Merge two Diffs - - -Blobs ------------------ - -A blob is equivalent to a file in a file system.:: - - # create a blob out of memory - >>> oid = repo.create_blob('foo bar') - >>> blob = repo[oid] - - Blob.data -- the contents of the blob, a byte string - -Tags ------------------ - -A tag is a static label for a commit. See references for more information. - - - -References -================= - -Reference lookup:: - - >>> all_refs = repo.listall_references() - >>> master_ref = repo.lookup_reference("refs/heads/master") - >>> commit = repo[master_ref.oid] - -Reference log:: - - >>> head = repo.lookup_reference('refs/heads/master') - >>> for entry in head.log(): - ... print(entry.message) - -The interface for RefLogEntry:: - - RefLogEntry.committer -- Signature of Committer - RefLogEntry.message -- the message of the RefLogEntry - RefLogEntry.oid_old -- oid of old reference - RefLogEntry.oid_new -- oid of new reference - -Revision parsing -================ - -You can use any of the fancy `` forms supported by libgit2:: - - >>> commit = repo.revparse_single('HEAD^') - -Revision walking -================= - -You can iterate through the revision history with repo.walk:: - - >>> from pygit2 import GIT_SORT_TIME - >>> for commit in repo.walk(oid, GIT_SORT_TIME): - ... print(commit.hex) - -The index file -================= - -Index read:: - - >>> index = repo.index - >>> index.read() - >>> oid = index['path/to/file'].oid # from path to object id - >>> blob = repo[oid] # from object id to object - -Iterate over all entries of the index:: - - >>> for entry in index: - ... print entry.path, entry.hex - -Index write:: - - >>> index.add('path/to/file') # git add - >>> del index['path/to/file'] # git rm - >>> index.write() # don't forget to save the changes - -Status -================= - -Inspect the status of the repository:: - - >>> from pygit2 import GIT_STATUS_CURRENT - >>> status = repo.status() - >>> for filepath, flags in status.items(): - ... if flags != GIT_STATUS_CURRENT: - ... print "Filepath %s isn't clean" % filepath - - -********************************************************************** Contributing -********************************************************************** +============ Fork libgit2/pygit2 on GitHub, make it awesomer (preferably in a branch named for the topic), send a pull request. -TODO -================= - -See issues - - Authors ============== @@ -446,4 +79,27 @@ pygit2 project (sorted alphabetically): License ============== -GPLv2 with linking exception. See COPYING for more details. +**GPLv2 with linking exception.** + +This program 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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. diff --git a/docs/index.rst b/docs/index.rst index 8c527a3e6..10eb5f5cb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,11 +9,16 @@ Welcome to pygit2's documentation! .. image:: https://secure.travis-ci.org/libgit2/pygit2.png :target: http://travis-ci.org/libgit2/pygit2 -pygit2 is a set of Python bindings to the libgit2 linkable C Git library. -The supported versions of Python are 2.6, 2.7, 3.1 and 3.2 +Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2 +implements the core of Git. Pygit2 works with Python 2.6, 2.7, 3.1 and 3.2 +Pygit2 links: -Contents: +- http://github.com/libgit2/pygit2 -- Source code and issue tracker +- http://www.pygit2.org/ -- Documentation +- http://pypi.python.org/pypi/pygit2 -- Download + +Table of Contents: .. toctree:: :maxdepth: 2 From 356fca312a79192e90a1558f4154fc42565e82da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 13 Jan 2013 12:35:04 +0100 Subject: [PATCH 0327/2237] Update authors list And remove AUTHORS text file. --- .mailmap | 2 ++ AUTHORS | 31 ------------------------------- README.rst | 9 +++++++++ 3 files changed, 11 insertions(+), 31 deletions(-) delete mode 100644 AUTHORS diff --git a/.mailmap b/.mailmap index f566e646f..a38a543bd 100644 --- a/.mailmap +++ b/.mailmap @@ -1,2 +1,4 @@ J. David Ibáñez Martin Lenders +Richo Healey +Xavier Delannoy diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 90b8db912..000000000 --- a/AUTHORS +++ /dev/null @@ -1,31 +0,0 @@ -The following people have contributed at least one patch to the -pygit2 project (sorted alphabetically): - -Amit Bakshi -András Veres-Szentkirályi -Benjamin Kircher -Bryan O'Sullivan -Carlos Martín Nieto -Christian Boos -Dave Borowitz -David Versmisse -Erik van Zijst -Han-Wen Nienhuys -Hugh Cole-Baker -J. David Ibáñez -Jared Flatow -John Szakmeister -Josh Bleecher Snyder -Julien Miotte -Martin Lenders -Nico von Geyso -Petr Hosek -Petr Viktorin -Richo Healey -Rui Abreu Ferreira -Sarath Lakshman -Sebastian Thiel -Vicent Marti -Yonggang Luo -Zoran Zaric -pistacchio diff --git a/README.rst b/README.rst index 4761e2958..9c462403a 100644 --- a/README.rst +++ b/README.rst @@ -46,15 +46,21 @@ Authors The following people have contributed at least one patch to the pygit2 project (sorted alphabetically): +- Alex Chamberlain - Amit Bakshi - András Veres-Szentkirályi +- Ben Davis - Benjamin Kircher - Bryan O'Sullivan - Carlos Martín Nieto - Christian Boos - David Borowitz (*Original author*) +- David Sanders - David Versmisse +- Eric Davis +- Eric Schrijver - Erik van Zijst +- Ferengee - Han-Wen Nienhuys - Hugh Cole-Baker - J David Ibáñez (*Current maintainer*) @@ -67,10 +73,13 @@ pygit2 project (sorted alphabetically): - Petr Hosek - Petr Viktorin - Richo Healey +- Ridge Kennedy - Rui Abreu Ferreira - Sarath Lakshman - Sebastian Thiel - Vicent Marti +- W Trevor King +- Xavier Delannoy - Yonggang Luo - Zoran Zaric - pistacchio From e34911b63e5d2266f9f72a4e3f32e27b13190feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 13 Jan 2013 22:26:49 +0100 Subject: [PATCH 0328/2237] docs: split usage guide into several source files --- docs/{reference.rst => autodoc.rst} | 2 +- docs/index-file.rst | 21 ++++++ docs/index.rst | 19 +++++- docs/log.rst | 9 +++ docs/{usage.rst => objects.rst} | 101 +--------------------------- docs/references.rst | 22 ++++++ docs/repository.rst | 24 +++++++ docs/revparse.rst | 7 ++ docs/status.rst | 11 +++ 9 files changed, 114 insertions(+), 102 deletions(-) rename docs/{reference.rst => autodoc.rst} (93%) create mode 100644 docs/index-file.rst create mode 100644 docs/log.rst rename docs/{usage.rst => objects.rst} (69%) create mode 100644 docs/references.rst create mode 100644 docs/repository.rst create mode 100644 docs/revparse.rst create mode 100644 docs/status.rst diff --git a/docs/reference.rst b/docs/autodoc.rst similarity index 93% rename from docs/reference.rst rename to docs/autodoc.rst index 044ae5433..fee3bdff6 100644 --- a/docs/reference.rst +++ b/docs/autodoc.rst @@ -1,4 +1,4 @@ -Reference +API Reference ============= .. Util functions diff --git a/docs/index-file.rst b/docs/index-file.rst new file mode 100644 index 000000000..80e733988 --- /dev/null +++ b/docs/index-file.rst @@ -0,0 +1,21 @@ +********************************************************************** +The index file +********************************************************************** + +Index read:: + + >>> index = repo.index + >>> index.read() + >>> oid = index['path/to/file'].oid # from path to object id + >>> blob = repo[oid] # from object id to object + +Iterate over all entries of the index:: + + >>> for entry in index: + ... print entry.path, entry.hex + +Index write:: + + >>> index.add('path/to/file') # git add + >>> del index['path/to/file'] # git rm + >>> index.write() # don't forget to save the changes diff --git a/docs/index.rst b/docs/index.rst index 10eb5f5cb..ff6216068 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,14 +18,27 @@ Pygit2 links: - http://www.pygit2.org/ -- Documentation - http://pypi.python.org/pypi/pygit2 -- Download -Table of Contents: +Topics: + .. toctree:: :maxdepth: 2 install - usage - reference + autodoc + +Usage guide: + +.. toctree:: + :maxdepth: 1 + + repository + objects + references + revparse + log + index-file + status diff --git a/docs/log.rst b/docs/log.rst new file mode 100644 index 000000000..ea0378b2c --- /dev/null +++ b/docs/log.rst @@ -0,0 +1,9 @@ +********************************************************************** +Commit log +********************************************************************** + +You can iterate through the revision history with repo.walk:: + + >>> from pygit2 import GIT_SORT_TIME + >>> for commit in repo.walk(oid, GIT_SORT_TIME): + ... print(commit.hex) diff --git a/docs/usage.rst b/docs/objects.rst similarity index 69% rename from docs/usage.rst rename to docs/objects.rst index 83602f8d5..699ffdd5d 100644 --- a/docs/usage.rst +++ b/docs/objects.rst @@ -1,34 +1,11 @@ ********************************************************************** -Usage samples +Git objects ********************************************************************** -.. contents:: - - -The repository -================= - -Everything starts by opening an existing repository:: - - >>> from pygit2 import Repository - >>> repo = Repository('pygit2/.git') - -Or by creating a new one:: - - >>> from pygit2 import init_repository - >>> bare = False - >>> repo = init_repository('test', bare) - -These are the basic attributes of a repository:: - - Repository.path -- path to the Git repository - Repository.workdir -- path to the working directory, None in the case of - a bare repo +.. contents:: Contents + :local: -Git objects -=========== - In the first place Git is a key-value storage system. The values stored are called *objects*, there are four types (commits, trees, blobs and tags), for each type pygit2 has a Python class:: @@ -201,75 +178,3 @@ Tags ----------------- A tag is a static label for a commit. See references for more information. - - - -References -================= - -Reference lookup:: - - >>> all_refs = repo.listall_references() - >>> master_ref = repo.lookup_reference("refs/heads/master") - >>> commit = repo[master_ref.oid] - -Reference log:: - - >>> head = repo.lookup_reference('refs/heads/master') - >>> for entry in head.log(): - ... print(entry.message) - -The interface for RefLogEntry:: - - RefLogEntry.committer -- Signature of Committer - RefLogEntry.message -- the message of the RefLogEntry - RefLogEntry.oid_old -- oid of old reference - RefLogEntry.oid_new -- oid of new reference - -Revision parsing -================ - -You can use any of the fancy `` forms supported by libgit2:: - - >>> commit = repo.revparse_single('HEAD^') - -Revision walking -================= - -You can iterate through the revision history with repo.walk:: - - >>> from pygit2 import GIT_SORT_TIME - >>> for commit in repo.walk(oid, GIT_SORT_TIME): - ... print(commit.hex) - -The index file -================= - -Index read:: - - >>> index = repo.index - >>> index.read() - >>> oid = index['path/to/file'].oid # from path to object id - >>> blob = repo[oid] # from object id to object - -Iterate over all entries of the index:: - - >>> for entry in index: - ... print entry.path, entry.hex - -Index write:: - - >>> index.add('path/to/file') # git add - >>> del index['path/to/file'] # git rm - >>> index.write() # don't forget to save the changes - -Status -================= - -Inspect the status of the repository:: - - >>> from pygit2 import GIT_STATUS_CURRENT - >>> status = repo.status() - >>> for filepath, flags in status.items(): - ... if flags != GIT_STATUS_CURRENT: - ... print "Filepath %s isn't clean" % filepath diff --git a/docs/references.rst b/docs/references.rst new file mode 100644 index 000000000..d56ec70b4 --- /dev/null +++ b/docs/references.rst @@ -0,0 +1,22 @@ +********************************************************************** +References +********************************************************************** + +Reference lookup:: + + >>> all_refs = repo.listall_references() + >>> master_ref = repo.lookup_reference("refs/heads/master") + >>> commit = repo[master_ref.oid] + +Reference log:: + + >>> head = repo.lookup_reference('refs/heads/master') + >>> for entry in head.log(): + ... print(entry.message) + +The interface for RefLogEntry:: + + RefLogEntry.committer -- Signature of Committer + RefLogEntry.message -- the message of the RefLogEntry + RefLogEntry.oid_old -- oid of old reference + RefLogEntry.oid_new -- oid of new reference diff --git a/docs/repository.rst b/docs/repository.rst new file mode 100644 index 000000000..551717f7d --- /dev/null +++ b/docs/repository.rst @@ -0,0 +1,24 @@ +********************************************************************** +The repository +********************************************************************** + + +Everything starts by opening an existing repository:: + + >>> from pygit2 import Repository + >>> repo = Repository('pygit2/.git') + + +Or by creating a new one:: + + >>> from pygit2 import init_repository + >>> bare = False + >>> repo = init_repository('test', bare) + + + +These are the basic attributes of a repository:: + + Repository.path -- path to the Git repository + Repository.workdir -- path to the working directory, None in the case of + a bare repo diff --git a/docs/revparse.rst b/docs/revparse.rst new file mode 100644 index 000000000..42c11a5ef --- /dev/null +++ b/docs/revparse.rst @@ -0,0 +1,7 @@ +********************************************************************** +Revision parsing +********************************************************************** + +You can use any of the fancy `` forms supported by libgit2:: + + >>> commit = repo.revparse_single('HEAD^') diff --git a/docs/status.rst b/docs/status.rst new file mode 100644 index 000000000..6bfdbfcea --- /dev/null +++ b/docs/status.rst @@ -0,0 +1,11 @@ +********************************************************************** +Status +********************************************************************** + +Inspect the status of the repository:: + + >>> from pygit2 import GIT_STATUS_CURRENT + >>> status = repo.status() + >>> for filepath, flags in status.items(): + ... if flags != GIT_STATUS_CURRENT: + ... print "Filepath %s isn't clean" % filepath From 6bb932cd1c9865cb40eebfb0da218c6c900a1ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 19 Jan 2013 15:21:43 +0100 Subject: [PATCH 0329/2237] Update to latest libgit2 API renaming See libgit2 commit 25743bd7c5f14f2287d9c4, where git_index_add_from_workdir was renamed to git_index_add_bypath --- src/pygit2/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 556b0848c..80ce55799 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -88,7 +88,7 @@ Index_add(Index *self, PyObject *args) if (!PyArg_ParseTuple(args, "s", &path)) return NULL; - err = git_index_add_from_workdir(self->index, path); + err = git_index_add_bypath(self->index, path); if (err < 0) return Error_set_str(err, path); From c1a48d514aa928ae715adbe9e5279ec60b152d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 19 Jan 2013 15:39:51 +0100 Subject: [PATCH 0330/2237] docs: make autodoc work without installing pygit2 --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 40954ae4f..e2826b5f7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,7 +16,7 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath('../build/lib.linux-x86_64-2.7')) # -- General configuration ----------------------------------------------------- From 181c6ed91ef2de566cae6172a3fbad6660265aef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 19 Jan 2013 17:05:36 +0100 Subject: [PATCH 0331/2237] docs: merge auto generated and hand writen docs --- docs/autodoc.rst | 14 -------- docs/config.rst | 8 +++++ docs/diff.rst | 26 +++++++++++++++ docs/errors.rst | 8 +++++ docs/index-file.rst | 14 ++++++++ docs/index.rst | 15 ++++++--- docs/objects.rst | 76 ++++++++++++++++++------------------------ docs/references.rst | 8 +++++ docs/repository.rst | 10 +++--- docs/utils.rst | 8 +++++ src/pygit2/commit.c | 19 +++++++---- src/pygit2/object.c | 15 ++++++--- src/pygit2/signature.c | 7 ++-- 13 files changed, 146 insertions(+), 82 deletions(-) delete mode 100644 docs/autodoc.rst create mode 100644 docs/config.rst create mode 100644 docs/diff.rst create mode 100644 docs/errors.rst create mode 100644 docs/utils.rst diff --git a/docs/autodoc.rst b/docs/autodoc.rst deleted file mode 100644 index fee3bdff6..000000000 --- a/docs/autodoc.rst +++ /dev/null @@ -1,14 +0,0 @@ -API Reference -============= - -.. Util functions -.. automodule:: pygit2.utils - :members: - :show-inheritance: - :undoc-members: - -.. c extension -.. automodule:: _pygit2 - :members: - :show-inheritance: - :undoc-members: diff --git a/docs/config.rst b/docs/config.rst new file mode 100644 index 000000000..3c1505256 --- /dev/null +++ b/docs/config.rst @@ -0,0 +1,8 @@ +********************************************************************** +Configuration file +********************************************************************** + +.. autoclass:: pygit2.Config + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/diff.rst b/docs/diff.rst new file mode 100644 index 000000000..c5350b8bc --- /dev/null +++ b/docs/diff.rst @@ -0,0 +1,26 @@ +********************************************************************** +Diff +********************************************************************** + + +A diff shows the changes between trees, an index or the working dir:: + + # Diff two trees + >>> t0 = repo.head.tree + >>> t1 = repo.head.parents[0].tree + >>> diff = t1.diff(t0) + >>> diff + + # Diff a tree with the index + >>> tree = repo.head.tree + >>> diff = tree.diff(repo.index) + + # Diff a tree with the current working dir + >>> tree = repo.head.tree + >>> diff = tree.diff() + +The interface for a diff:: + + Diff.changes -- Dict of 'files' and 'hunks' for every change + Diff.patch -- a patch for every changeset + Diff.merge -- Merge two Diffs diff --git a/docs/errors.rst b/docs/errors.rst new file mode 100644 index 000000000..6a3715a2e --- /dev/null +++ b/docs/errors.rst @@ -0,0 +1,8 @@ +********************************************************************** +Errors +********************************************************************** + +.. autoexception:: pygit2.GitError + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/index-file.rst b/docs/index-file.rst index 80e733988..ff5045349 100644 --- a/docs/index-file.rst +++ b/docs/index-file.rst @@ -19,3 +19,17 @@ Index write:: >>> index.add('path/to/file') # git add >>> del index['path/to/file'] # git rm >>> index.write() # don't forget to save the changes + + + +.. Autogenerated + +.. autoclass:: pygit2.Index + :members: + :show-inheritance: + :undoc-members: + +.. autoclass:: pygit2.IndexEntry + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/index.rst b/docs/index.rst index ff6216068..a92433e71 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,28 +18,35 @@ Pygit2 links: - http://www.pygit2.org/ -- Documentation - http://pypi.python.org/pypi/pygit2 -- Download -Topics: - +Start: .. toctree:: :maxdepth: 2 install - autodoc Usage guide: .. toctree:: - :maxdepth: 1 + :maxdepth: 2 repository objects references revparse log + diff index-file status + config + errors + +More: + +.. toctree:: + :maxdepth: 1 + utils Indices and tables diff --git a/docs/objects.rst b/docs/objects.rst index 699ffdd5d..88578b31e 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -33,30 +33,24 @@ The API of pygit2 accepts both the raw object id and its hexadecimal representation, the difference is done based on its type (a byte or a text string). -This is the common interface for all Git objects:: +Objects can not be modified once they have been created. - Object.type -- one of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, - GIT_OBJ_BLOB or GIT_OBJ_TAG constants - Object.oid -- the object id, a byte string 20 bytes long - Object.hex -- hexadecimal representation of the object id, a text - string 40 chars long - Object.read_raw() -- returns the byte string with the raw contents of the - of the object +This is the common interface for all Git objects: -Objects can not be modified once they have been created. +.. autoclass:: pygit2.Object + :members: type, oid, hex, read_raw Commits ----------------- A commit is a snapshot of the working dir with meta informations like author, -committer and others.:: +committer and others. - Commit.author -- the author of the commit - Commit.committer -- the committer of the commit - Commit.message -- the message, a text string - Commit.tree -- the tree object attached to the commit - Commit.parents -- the list of parent commits +.. autoclass:: pygit2.Commit + :members: author, committer, message, message_encoding, tree, parents, + commit_time, commit_time_offset + :show-inheritance: Signatures @@ -68,12 +62,8 @@ objects:: >>> commit.author -This is their interface:: - - Signature.name -- person's name - Signature.email -- person's email address - Signature.time -- unix time - Signature.offset -- offset from utc in minutes +.. autoclass:: pygit2.Signature + :members: name, email, time, offset Creating commits @@ -137,30 +127,15 @@ This is the interface of a tree entry:: TreeEntry.to_object() -- returns the git object (equivalent to repo[entry.oid]) -Diff ------------------ - -A diff shows the changes between trees, an index or the working dir:: - - # Diff two trees - >>> t0 = repo.head.tree - >>> t1 = repo.head.parents[0].tree - >>> diff = t1.diff(t0) - >>> diff - - # Diff a tree with the index - >>> tree = repo.head.tree - >>> diff = tree.diff(repo.index) +.. autoclass:: pygit2.Tree + :members: + :show-inheritance: + :undoc-members: - # Diff a tree with the current working dir - >>> tree = repo.head.tree - >>> diff = tree.diff() - -The interface for a diff:: - - Diff.changes -- Dict of 'files' and 'hunks' for every change - Diff.patch -- a patch for every changeset - Diff.merge -- Merge two Diffs +.. autoclass:: pygit2.TreeEntry + :members: + :show-inheritance: + :undoc-members: Blobs @@ -174,7 +149,20 @@ A blob is equivalent to a file in a file system.:: Blob.data -- the contents of the blob, a byte string + +.. autoclass:: pygit2.Blob + :members: + :show-inheritance: + :undoc-members: + + Tags ----------------- A tag is a static label for a commit. See references for more information. + + +.. autoclass:: pygit2.Tag + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/references.rst b/docs/references.rst index d56ec70b4..2adce486e 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -20,3 +20,11 @@ The interface for RefLogEntry:: RefLogEntry.message -- the message of the RefLogEntry RefLogEntry.oid_old -- oid of old reference RefLogEntry.oid_new -- oid of new reference + + +.. Autogenerated + +.. autoclass:: pygit2.Reference + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/repository.rst b/docs/repository.rst index 551717f7d..35f46d257 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -2,13 +2,11 @@ The repository ********************************************************************** - Everything starts by opening an existing repository:: >>> from pygit2 import Repository >>> repo = Repository('pygit2/.git') - Or by creating a new one:: >>> from pygit2 import init_repository @@ -16,9 +14,9 @@ Or by creating a new one:: >>> repo = init_repository('test', bare) +.. autofunction:: pygit2.init_repository -These are the basic attributes of a repository:: +.. autofunction:: pygit2.discover_repository - Repository.path -- path to the Git repository - Repository.workdir -- path to the working directory, None in the case of - a bare repo +.. autoclass:: pygit2.Repository + :members: diff --git a/docs/utils.rst b/docs/utils.rst new file mode 100644 index 000000000..42d0d63f1 --- /dev/null +++ b/docs/utils.rst @@ -0,0 +1,8 @@ +********************************************************************** +Utilities +********************************************************************** + +.. automodule:: pygit2.utils + :members: + :show-inheritance: + :undoc-members: diff --git a/src/pygit2/commit.c b/src/pygit2/commit.c index c0ccee197..ab13948b4 100644 --- a/src/pygit2/commit.c +++ b/src/pygit2/commit.c @@ -156,17 +156,22 @@ Commit_get_parents(Commit *commit) PyGetSetDef Commit_getseters[] = { {"message_encoding", (getter)Commit_get_message_encoding, NULL, "message encoding", NULL}, - {"message", (getter)Commit_get_message, NULL, "message", NULL}, - {"_message", (getter)Commit_get_raw_message, NULL, "message (bytes)", NULL}, + {"message", (getter)Commit_get_message, NULL, + "The commit message, a text string.", NULL}, + {"_message", (getter)Commit_get_raw_message, NULL, "message (bytes)", + NULL}, {"commit_time", (getter)Commit_get_commit_time, NULL, "commit time", NULL}, {"commit_time_offset", (getter)Commit_get_commit_time_offset, NULL, "commit time offset", NULL}, - {"committer", (getter)Commit_get_committer, NULL, "committer", NULL}, - {"author", (getter)Commit_get_author, NULL, "author", NULL}, - {"tree", (getter)Commit_get_tree, NULL, "tree object", NULL}, - {"parents", (getter)Commit_get_parents, NULL, "parents of this commit", - NULL}, + {"committer", (getter)Commit_get_committer, NULL, + "The committer of the commit.", NULL}, + {"author", (getter)Commit_get_author, NULL, + "The author of the commit.", NULL}, + {"tree", (getter)Commit_get_tree, NULL, + "The tree object attached to the commit.", NULL}, + {"parents", (getter)Commit_get_parents, NULL, + "The list of parent commits.", NULL}, {NULL} }; diff --git a/src/pygit2/object.c b/src/pygit2/object.c index c990fd2de..d20f7f174 100644 --- a/src/pygit2/object.c +++ b/src/pygit2/object.c @@ -99,15 +99,22 @@ Object_read_raw(Object *self) } PyGetSetDef Object_getseters[] = { - {"oid", (getter)Object_get_oid, NULL, "object id", NULL}, - {"hex", (getter)Object_get_hex, NULL, "hex oid", NULL}, - {"type", (getter)Object_get_type, NULL, "type number", NULL}, + {"oid", (getter)Object_get_oid, NULL, + "The object id, a byte string 20 bytes long.", NULL}, + {"hex", (getter)Object_get_hex, NULL, + "Hexadecimal representation of the object id, a text string 40 chars " + "long.", + NULL}, + {"type", (getter)Object_get_type, NULL, + "One of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, GIT_OBJ_BLOB or " + "GIT_OBJ_TAG constants.", + NULL}, {NULL} }; PyMethodDef Object_methods[] = { {"read_raw", (PyCFunction)Object_read_raw, METH_NOARGS, - "Read the raw contents of the object from the repo."}, + "Returns the byte string with the raw contents of the of the object."}, {NULL} }; diff --git a/src/pygit2/signature.c b/src/pygit2/signature.c index cb8425d94..720ed3842 100644 --- a/src/pygit2/signature.c +++ b/src/pygit2/signature.c @@ -143,9 +143,10 @@ PyGetSetDef Signature_getseters[] = { {"_name", (getter)Signature_get_raw_name, NULL, "Name (bytes)", NULL}, {"_email", (getter)Signature_get_raw_email, NULL, "Email (bytes)", NULL}, {"name", (getter)Signature_get_name, NULL, "Name", NULL}, - {"email", (getter)Signature_get_email, NULL, "Email", NULL}, - {"time", (getter)Signature_get_time, NULL, "Time", NULL}, - {"offset", (getter)Signature_get_offset, NULL, "Offset", NULL}, + {"email", (getter)Signature_get_email, NULL, "Email address", NULL}, + {"time", (getter)Signature_get_time, NULL, "Unix time", NULL}, + {"offset", (getter)Signature_get_offset, NULL, + "Offset from UTC in minutes", NULL}, {NULL} }; From b0235e70c74ebf11a8f8595131ba672fa050192f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 20 Jan 2013 12:40:31 +0100 Subject: [PATCH 0332/2237] Improve Index docs And fix a refcount error through the way. --- docs/diff.rst | 4 ++ docs/index-file.rst | 13 ++---- src/pygit2.c | 18 +++++--- src/pygit2/index.c | 102 ++++++++++++++++++++++++++++++++------------ 4 files changed, 95 insertions(+), 42 deletions(-) diff --git a/docs/diff.rst b/docs/diff.rst index c5350b8bc..dc4029591 100644 --- a/docs/diff.rst +++ b/docs/diff.rst @@ -24,3 +24,7 @@ The interface for a diff:: Diff.changes -- Dict of 'files' and 'hunks' for every change Diff.patch -- a patch for every changeset Diff.merge -- Merge two Diffs + + +.. autoclass:: pygit2.Diff + :members: diff --git a/docs/index-file.rst b/docs/index-file.rst index ff5045349..94ecf6fef 100644 --- a/docs/index-file.rst +++ b/docs/index-file.rst @@ -1,5 +1,5 @@ ********************************************************************** -The index file +Index file ********************************************************************** Index read:: @@ -21,15 +21,8 @@ Index write:: >>> index.write() # don't forget to save the changes - -.. Autogenerated - .. autoclass:: pygit2.Index - :members: - :show-inheritance: - :undoc-members: + :members: add, remove, clear, read, write, read_tree, write_tree, diff .. autoclass:: pygit2.IndexEntry - :members: - :show-inheritance: - :undoc-members: + :members: oid, hex, path, mode diff --git a/src/pygit2.c b/src/pygit2.c index 41613e33d..7e2b8ccbf 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -208,6 +208,9 @@ moduleinit(PyObject* m) Py_INCREF(&IndexEntryType); PyModule_AddObject(m, "IndexEntry", (PyObject *)&IndexEntryType); + Py_INCREF(&DiffType); + PyModule_AddObject(m, "Diff", (PyObject *)&DiffType); + Py_INCREF(&ReferenceType); PyModule_AddObject(m, "Reference", (PyObject *)&ReferenceType); @@ -265,15 +268,20 @@ moduleinit(PyObject* m) GIT_DIFF_RECURSE_UNTRACKED_DIRS); /* Flags for diff find similar */ - PyModule_AddIntConstant(m, "GIT_DIFF_FIND_RENAMES", // --find-renames + // --find-renames + PyModule_AddIntConstant(m, "GIT_DIFF_FIND_RENAMES", GIT_DIFF_FIND_RENAMES); - PyModule_AddIntConstant(m, "GIT_DIFF_FIND_RENAMES_FROM_REWRITES", // --break-rewrites=N + // --break-rewrites=N + PyModule_AddIntConstant(m, "GIT_DIFF_FIND_RENAMES_FROM_REWRITES", GIT_DIFF_FIND_RENAMES_FROM_REWRITES); - PyModule_AddIntConstant(m, "GIT_DIFF_FIND_COPIES", // --find-copies + // --find-copies + PyModule_AddIntConstant(m, "GIT_DIFF_FIND_COPIES", GIT_DIFF_FIND_COPIES); - PyModule_AddIntConstant(m, "GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED", // --find-copies-harder + // --find-copies-harder + PyModule_AddIntConstant(m, "GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED", GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED); - PyModule_AddIntConstant(m, "GIT_DIFF_FIND_AND_BREAK_REWRITES", // --break-rewrites=/M + // --break-rewrites=/M + PyModule_AddIntConstant(m, "GIT_DIFF_FIND_AND_BREAK_REWRITES", GIT_DIFF_FIND_AND_BREAK_REWRITES); /* Flags for diffed files */ diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 80ce55799..ad7c9f257 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -79,6 +79,10 @@ Index_traverse(Index *self, visitproc visit, void *arg) return 0; } +PyDoc_STRVAR(Index_add__doc__, + "add(path)\n\n" + "Add or update an index entry from a file in disk."); + PyObject * Index_add(Index *self, PyObject *args) { @@ -95,6 +99,10 @@ Index_add(Index *self, PyObject *args) Py_RETURN_NONE; } +PyDoc_STRVAR(Index_clear__doc__, + "clear()\n\n" + "Clear the contents (all the entries) of an index object."); + PyObject * Index_clear(Index *self) { @@ -102,8 +110,15 @@ Index_clear(Index *self) Py_RETURN_NONE; } + +PyDoc_STRVAR(Index_diff__doc__, + "diff([tree]) -> Diff\n\n" + "Return a :py:class:`~pygit2.Diff` object with the differences between the " + "index and the working copy. If a :py:class:`~pygit2.Tree` object is " + "passed, return the diferences between the index and the given tree."); + PyObject * -Index_diff_tree(Index *self, PyObject *args) +Index_diff(Index *self, PyObject *args) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; @@ -137,7 +152,6 @@ Index_diff_tree(Index *self, PyObject *args) py_diff = PyObject_New(Diff, &DiffType); if (py_diff) { - Py_INCREF(py_diff); Py_INCREF(self->repo); py_diff->repo = self->repo; py_diff->diff = diff; @@ -146,6 +160,11 @@ Index_diff_tree(Index *self, PyObject *args) return (PyObject*)py_diff; } +PyDoc_STRVAR(Index_find__doc__, + "_find(path) -> integer\n\n" + "Find the first index of any entries which point to given path in the " + "index file."); + PyObject * Index_find(Index *self, PyObject *py_path) { @@ -163,6 +182,12 @@ Index_find(Index *self, PyObject *py_path) return PyInt_FromLong(idx); } + +PyDoc_STRVAR(Index_read__doc__, + "read()\n\n" + "Update the contents of an existing index object in memory by reading from " + "the hard disk."); + PyObject * Index_read(Index *self) { @@ -175,6 +200,12 @@ Index_read(Index *self) Py_RETURN_NONE; } + +PyDoc_STRVAR(Index_write__doc__, + "write()\n\n" + "Write an existing index object from memory back to disk using an atomic " + "file lock."); + PyObject * Index_write(Index *self) { @@ -294,6 +325,11 @@ Index_getitem(Index *self, PyObject *value) return wrap_index_entry(index_entry, self); } + +PyDoc_STRVAR(Index_remove__doc__, + "remove(path)\n\n" + "Removes an entry from index."); + PyObject * Index_remove(Index *self, PyObject *args) { @@ -327,6 +363,11 @@ Index_setitem(Index *self, PyObject *key, PyObject *value) return 0; } + +PyDoc_STRVAR(Index_read_tree__doc__, + "read_tree(tree)\n\n" + "Update the index file from the tree identified by the given oid."); + PyObject * Index_read_tree(Index *self, PyObject *value) { @@ -351,6 +392,11 @@ Index_read_tree(Index *self, PyObject *value) Py_RETURN_NONE; } + +PyDoc_STRVAR(Index_write_tree__doc__, + "write_tree() -> str\n\n" + "Create a tree object from the index file, return its oid."); + PyObject * Index_write_tree(Index *self) { @@ -365,27 +411,17 @@ Index_write_tree(Index *self) } PyMethodDef Index_methods[] = { - {"add", (PyCFunction)Index_add, METH_VARARGS, - "Add or update an index entry from a file in disk."}, - {"remove", (PyCFunction)Index_remove, METH_VARARGS, - "Removes an entry from index."}, - {"clear", (PyCFunction)Index_clear, METH_NOARGS, - "Clear the contents (all the entries) of an index object."}, - {"diff", (PyCFunction)Index_diff_tree, METH_VARARGS, - "Diff index to tree."}, - {"_find", (PyCFunction)Index_find, METH_O, - "Find the first index of any entries which point to given path in the" - " Git index."}, - {"read", (PyCFunction)Index_read, METH_NOARGS, - "Update the contents of an existing index object in memory by reading" - " from the hard disk."}, - {"write", (PyCFunction)Index_write, METH_NOARGS, - "Write an existing index object from memory back to disk using an" - " atomic file lock."}, + {"add", (PyCFunction)Index_add, METH_VARARGS, Index_add__doc__}, + {"remove", (PyCFunction)Index_remove, METH_VARARGS, Index_remove__doc__}, + {"clear", (PyCFunction)Index_clear, METH_NOARGS, Index_clear__doc__}, + {"diff", (PyCFunction)Index_diff, METH_VARARGS, Index_diff__doc__}, + {"_find", (PyCFunction)Index_find, METH_O, Index_find__doc__}, + {"read", (PyCFunction)Index_read, METH_NOARGS, Index_read__doc__}, + {"write", (PyCFunction)Index_write, METH_NOARGS, Index_write__doc__}, {"read_tree", (PyCFunction)Index_read_tree, METH_O, - "Update the index file from the given tree object."}, + Index_read_tree__doc__}, {"write_tree", (PyCFunction)Index_write_tree, METH_NOARGS, - "Create a tree object from the index file, return its oid."}, + Index_write_tree__doc__}, {NULL} }; @@ -406,6 +442,8 @@ PyMappingMethods Index_as_mapping = { (objobjargproc)Index_setitem, /* mp_ass_subscript */ }; +PyDoc_STRVAR(Index__doc__, "Index file."); + PyTypeObject IndexType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Index", /* tp_name */ @@ -429,7 +467,7 @@ PyTypeObject IndexType = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "Index file", /* tp_doc */ + Index__doc__, /* tp_doc */ (traverseproc)Index_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -507,24 +545,32 @@ IndexEntry_dealloc(IndexEntry *self) PyObject_Del(self); } +PyDoc_STRVAR(IndexEntry_mode__doc__, "Mode."); + PyObject * IndexEntry_get_mode(IndexEntry *self) { return PyInt_FromLong(self->entry->mode); } +PyDoc_STRVAR(IndexEntry_path__doc__, "Path."); + PyObject * IndexEntry_get_path(IndexEntry *self) { return to_path(self->entry->path); } +PyDoc_STRVAR(IndexEntry_oid__doc__, "Object id."); + PyObject * IndexEntry_get_oid(IndexEntry *self) { return git_oid_to_python(self->entry->oid.id); } +PyDoc_STRVAR(IndexEntry_hex__doc__, "Hex id."); + PyObject * IndexEntry_get_hex(IndexEntry *self) { @@ -532,13 +578,15 @@ IndexEntry_get_hex(IndexEntry *self) } PyGetSetDef IndexEntry_getseters[] = { - {"mode", (getter)IndexEntry_get_mode, NULL, "mode", NULL}, - {"path", (getter)IndexEntry_get_path, NULL, "path", NULL}, - {"oid", (getter)IndexEntry_get_oid, NULL, "object id", NULL}, - {"hex", (getter)IndexEntry_get_hex, NULL, "hex oid", NULL}, + {"mode", (getter)IndexEntry_get_mode, NULL, IndexEntry_mode__doc__, NULL}, + {"path", (getter)IndexEntry_get_path, NULL, IndexEntry_path__doc__, NULL}, + {"oid", (getter)IndexEntry_get_oid, NULL, IndexEntry_oid__doc__, NULL}, + {"hex", (getter)IndexEntry_get_hex, NULL, IndexEntry_hex__doc__, NULL}, {NULL}, }; +PyDoc_STRVAR(IndexEntry__doc__, "Index entry."); + PyTypeObject IndexEntryType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.IndexEntry", /* tp_name */ @@ -560,7 +608,7 @@ PyTypeObject IndexEntryType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ - "Index entry", /* tp_doc */ + IndexEntry__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ From 93b8c633c313d5b4f91888a58c879012860eb356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 27 Jan 2013 23:49:02 +0100 Subject: [PATCH 0333/2237] docs: working on the repository chapter (in progress) --- docs/repository.rst | 22 ++++++---- src/pygit2.c | 15 +++++-- src/pygit2/repository.c | 96 +++++++++++++++++++++++++---------------- 3 files changed, 84 insertions(+), 49 deletions(-) diff --git a/docs/repository.rst b/docs/repository.rst index 35f46d257..e1f84a6e5 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -2,21 +2,27 @@ The repository ********************************************************************** -Everything starts by opening an existing repository:: - - >>> from pygit2 import Repository - >>> repo = Repository('pygit2/.git') +.. autofunction:: pygit2.init_repository -Or by creating a new one:: + This is how to create non-bare repository:: >>> from pygit2 import init_repository >>> bare = False >>> repo = init_repository('test', bare) -.. autofunction:: pygit2.init_repository - .. autofunction:: pygit2.discover_repository + .. autoclass:: pygit2.Repository - :members: + :members: path, workdir, is_bare, is_empty, revparse_single, read, write, + create_blob, create_blob_fromfile, create_commit, + create_reference, create_tag, TreeBuilder, walk, + listall_references, lookup_reference, packall_references, head, + head_is_detached, head_is_orphaned, index, status, status_file, + config + + To open an existing repository:: + + >>> from pygit2 import Repository + >>> repo = Repository('pygit2/.git') diff --git a/src/pygit2.c b/src/pygit2.c index 7e2b8ccbf..0213d88ed 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -58,6 +58,11 @@ extern PyTypeObject RefLogEntryType; extern PyTypeObject SignatureType; + +PyDoc_STRVAR(init_repository__doc__, + "init_repository(path, bare) -> Repository\n\n" + "Creates a new Git repository in the given path."); + PyObject * init_repository(PyObject *self, PyObject *args) { @@ -86,6 +91,11 @@ init_repository(PyObject *self, PyObject *args) return NULL; }; + +PyDoc_STRVAR(discover_repository__doc__, + "discover_repository(path[, across_fs[, ceiling_dirs]]) -> str\n\n" + "Look for a git repository and return its path."); + PyObject * discover_repository(PyObject *self, PyObject *args) { @@ -107,10 +117,9 @@ discover_repository(PyObject *self, PyObject *args) }; PyMethodDef module_methods[] = { - {"init_repository", init_repository, METH_VARARGS, - "Creates a new Git repository in the given folder."}, + {"init_repository", init_repository, METH_VARARGS, init_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, - "Look for a git repository and return its path."}, + discover_repository__doc__}, {NULL} }; diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 4dc7c34d3..09ac3028c 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -194,8 +194,12 @@ Repository_as_iter(Repository *self) return PyObject_GetIter(accum); } + +PyDoc_STRVAR(Repository_head__doc__, + "Current head reference of the repository."); + PyObject * -Repository_head(Repository *self) +Repository_get_head(Repository *self) { git_reference *head; const git_oid *oid; @@ -219,11 +223,9 @@ Repository_head(Repository *self) } -PyDoc_STRVAR( - Repository_head_is_detached_doc, - "A repository's HEAD is detached when it points directly to a commit\n" - "instead of a branch.\n" -); +PyDoc_STRVAR(Repository_head_is_detached__doc__, + "A repository's HEAD is detached when it points directly to a commit " + "instead of a branch."); PyObject * Repository_head_is_detached(Repository *self) @@ -235,11 +237,9 @@ Repository_head_is_detached(Repository *self) } -PyDoc_STRVAR( - Repository_head_is_orphaned_doc, - "An orphan branch is one named from HEAD but which doesn't exist in\n" - "the refs namespace, because it doesn't have any commit to point to.\n" -); +PyDoc_STRVAR(Repository_head_is_orphaned__doc__, + "An orphan branch is one named from HEAD but which doesn't exist in the " + "refs namespace, because it doesn't have any commit to point to."); PyObject * Repository_head_is_orphaned(Repository *self) @@ -251,10 +251,8 @@ Repository_head_is_orphaned(Repository *self) } -PyDoc_STRVAR( - Repository_is_empty_doc, - "Check if a repository is empty\n" -); +PyDoc_STRVAR(Repository_is_empty__doc__, + "Check if a repository is empty."); PyObject * Repository_is_empty(Repository *self) @@ -266,10 +264,8 @@ Repository_is_empty(Repository *self) } -PyDoc_STRVAR( - Repository_is_bare_doc, - "Check if a repository is a bare repository.\n" -); +PyDoc_STRVAR(Repository_is_bare__doc__, + "Check if a repository is a bare repository."); PyObject * Repository_is_bare(Repository *self) @@ -403,6 +399,9 @@ Repository_write(Repository *self, PyObject *args) return git_oid_to_python(oid.id); } + +PyDoc_STRVAR(Repository_index__doc__, "Index file."); + PyObject * Repository_get_index(Repository *self, void *closure) { @@ -434,12 +433,21 @@ Repository_get_index(Repository *self, void *closure) return self->index; } + +PyDoc_STRVAR(Repository_path__doc__, + "The normalized path to the git repository."); + PyObject * Repository_get_path(Repository *self, void *closure) { return to_path(git_repository_path(self->repo)); } + +PyDoc_STRVAR(Repository_workdir__doc__, + "The normalized path to the working directory of the repository. " + "If the repository is bare, None will be returned."); + PyObject * Repository_get_workdir(Repository *self, void *closure) { @@ -452,6 +460,13 @@ Repository_get_workdir(Repository *self, void *closure) return to_path(c_path); } + +PyDoc_STRVAR(Repository_config__doc__, + "Get the configuration file for this repository.\n\n" + "If a configuration file has not been set, the default config set for the " + "repository will be returned, including global and system configurations " + "(if they are available)."); + PyObject * Repository_get_config(Repository *self, void *closure) { @@ -568,6 +583,12 @@ Repository_create_blob_fromfile(Repository *self, PyObject *args) return git_oid_to_python(oid.id); } + +PyDoc_STRVAR(Repository_create_commit__doc__, + "create_commit(reference, author, committer, message, tree, parents" + "[, encoding]) -> bytes\n\n" + "Create a new commit object, return its SHA."); + PyObject * Repository_create_commit(Repository *self, PyObject *args) { @@ -906,7 +927,7 @@ Repository_TreeBuilder(Repository *self, PyObject *args) PyMethodDef Repository_methods[] = { {"create_commit", (PyCFunction)Repository_create_commit, METH_VARARGS, - "Create a new commit object, return its SHA."}, + Repository_create_commit__doc__}, {"create_tag", (PyCFunction)Repository_create_tag, METH_VARARGS, "Create a new tag object, return its SHA."}, {"walk", (PyCFunction)Repository_walk, METH_VARARGS, @@ -948,27 +969,21 @@ PyMethodDef Repository_methods[] = { }; PyGetSetDef Repository_getseters[] = { - {"index", (getter)Repository_get_index, NULL, "index file. ", NULL}, - {"path", (getter)Repository_get_path, NULL, - "The normalized path to the git repository.", NULL}, - {"head", (getter)Repository_head, NULL, - "Current head reference of the repository.", NULL}, + {"index", (getter)Repository_get_index, NULL, Repository_index__doc__, + NULL}, + {"path", (getter)Repository_get_path, NULL, Repository_path__doc__, NULL}, + {"head", (getter)Repository_get_head, NULL, Repository_head__doc__, NULL}, {"head_is_detached", (getter)Repository_head_is_detached, NULL, - Repository_head_is_detached_doc}, + Repository_head_is_detached__doc__}, {"head_is_orphaned", (getter)Repository_head_is_orphaned, NULL, - Repository_head_is_orphaned_doc}, + Repository_head_is_orphaned__doc__}, {"is_empty", (getter)Repository_is_empty, NULL, - Repository_is_empty_doc}, - {"is_bare", (getter)Repository_is_bare, NULL, - Repository_is_bare_doc}, - {"config", (getter)Repository_get_config, NULL, - "Get the configuration file for this repository.\n\n" - "If a configuration file has not been set, the default " - "config set for the repository will be returned, including " - "global and system configurations (if they are available).", NULL}, + Repository_is_empty__doc__}, + {"is_bare", (getter)Repository_is_bare, NULL, Repository_is_bare__doc__}, + {"config", (getter)Repository_get_config, NULL, Repository_config__doc__, + NULL}, {"workdir", (getter)Repository_get_workdir, NULL, - "The normalized path to the working directory of the repository. " - "If the repository is bare, None will be returned.", NULL}, + Repository_workdir__doc__, NULL}, {NULL} }; @@ -989,6 +1004,11 @@ PyMappingMethods Repository_as_mapping = { 0, /* mp_ass_subscript */ }; + +PyDoc_STRVAR(Repository__doc__, + "Repository(path) -> Repository\n\n" + "Git repository."); + PyTypeObject RepositoryType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Repository", /* tp_name */ @@ -1012,7 +1032,7 @@ PyTypeObject RepositoryType = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "Git repository", /* tp_doc */ + Repository__doc__, /* tp_doc */ (traverseproc)Repository_traverse, /* tp_traverse */ (inquiry)Repository_clear, /* tp_clear */ 0, /* tp_richcompare */ From 8cdb8c06bed514d9f83b53a0c9c94efcac1cf409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 28 Jan 2013 22:39:19 +0100 Subject: [PATCH 0334/2237] docs: working on the repository chapter (continue) --- src/pygit2/repository.c | 33 ++++++++++++++++++++++++++------- src/pygit2/walker.c | 2 +- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 09ac3028c..b1d04f794 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -339,6 +339,11 @@ Repository_read_raw(git_repository *repo, const git_oid *oid, size_t len) return obj; } + +PyDoc_STRVAR(Repository_read__doc__, + "read(oid) -> type, data, size\n\n" + "Read raw object data from the repository."); + PyObject * Repository_read(Repository *self, PyObject *py_hex) { @@ -365,6 +370,13 @@ Repository_read(Repository *self, PyObject *py_hex) return tuple; } + +PyDoc_STRVAR(Repository_write__doc__, + "write(type, data) -> oid\n\n" + "Write raw object data into the repository. First arg is the object " + "type, the second one a buffer with data. Return the object id (sha) " + "of the created object."); + PyObject * Repository_write(Repository *self, PyObject *args) { @@ -498,6 +510,11 @@ Repository_get_config(Repository *self, void *closure) return self->config; } + +PyDoc_STRVAR(Repository_walk__doc__, + "walk(oid, sort_mode) -> iterator\n\n" + "Generator that traverses the history starting from the given commit."); + PyObject * Repository_walk(Repository *self, PyObject *args) { @@ -666,6 +683,11 @@ Repository_create_commit(Repository *self, PyObject *args) return py_result; } + +PyDoc_STRVAR(Repository_create_tag__doc__, + "create_tag(name, oid, type, tagger, message) -> bytes\n\n" + "Create a new tag object, return its SHA."); + PyObject * Repository_create_tag(Repository *self, PyObject *args) { @@ -929,15 +951,12 @@ PyMethodDef Repository_methods[] = { {"create_commit", (PyCFunction)Repository_create_commit, METH_VARARGS, Repository_create_commit__doc__}, {"create_tag", (PyCFunction)Repository_create_tag, METH_VARARGS, - "Create a new tag object, return its SHA."}, + Repository_create_tag__doc__}, {"walk", (PyCFunction)Repository_walk, METH_VARARGS, - "Generator that traverses the history starting from the given commit."}, - {"read", (PyCFunction)Repository_read, METH_O, - "Read raw object data from the repository."}, + Repository_walk__doc__}, + {"read", (PyCFunction)Repository_read, METH_O, Repository_read__doc__}, {"write", (PyCFunction)Repository_write, METH_VARARGS, - "Write raw object data into the repository. First arg is the object\n" - "type, the second one a buffer with data. Return the object id (sha)\n" - "of the created object."}, + Repository_write__doc__}, {"listall_references", (PyCFunction)Repository_listall_references, METH_VARARGS, "Return a list with all the references in the repository."}, diff --git a/src/pygit2/walker.c b/src/pygit2/walker.c index 878b506ea..3069e9ef5 100644 --- a/src/pygit2/walker.c +++ b/src/pygit2/walker.c @@ -145,7 +145,7 @@ PyMethodDef Walker_methods[] = { PyTypeObject WalkerType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Walker", /* tp_name */ + "_pygit2.Walker", /* tp_name */ sizeof(Walker), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Walker_dealloc, /* tp_dealloc */ From ea8b076b704cfda06852973bd2b7eec1f60a2e14 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 1 Feb 2013 13:37:53 +0100 Subject: [PATCH 0335/2237] fixed api changes for index.c 11d9f6b304 in libgit2 changed api for git_index_find() --- include/pygit2/utils.h | 1 + src/pygit2/index.c | 35 ++++++++++++++++++++--------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/include/pygit2/utils.h b/include/pygit2/utils.h index b7fd1b93f..359b17844 100644 --- a/include/pygit2/utils.h +++ b/include/pygit2/utils.h @@ -51,6 +51,7 @@ #if PY_MAJOR_VERSION == 2 #define to_path(x) to_bytes(x) #define to_encoding(x) to_bytes(x) + #define PyLong_FromSize_t PyInt_FromSize_t #else #define to_path(x) to_unicode(x, Py_FileSystemDefaultEncoding, "strict") #define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict") diff --git a/src/pygit2/index.c b/src/pygit2/index.c index ad7c9f257..4ee1ecf3d 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -169,17 +169,18 @@ PyObject * Index_find(Index *self, PyObject *py_path) { char *path; - long idx; + size_t idx; + int err; path = PyString_AsString(py_path); if (!path) return NULL; - idx = (long)git_index_find(self->index, path); - if (idx < 0) - return Error_set_str(idx, path); + err = git_index_find(&idx, self->index, path); + if (err < 0) + return Error_set_str(err, path); - return PyInt_FromLong(idx); + return PyLong_FromSize_t(idx); } @@ -219,35 +220,39 @@ Index_write(Index *self) } /* This is an internal function, used by Index_getitem and Index_setitem */ -int +size_t Index_get_position(Index *self, PyObject *value) { char *path; - int idx; + size_t idx; + int err; /* Case 1: integer */ if (PyInt_Check(value)) { - idx = (int)PyInt_AsLong(value); - if (idx == -1 && PyErr_Occurred()) + err = (int)PyInt_AsLong(value); + if (err == -1 && PyErr_Occurred()) return -1; - if (idx < 0) { + if (err < 0) { PyErr_SetObject(PyExc_ValueError, value); return -1; } - return idx; + return err; } /* Case 2: byte or text string */ path = py_path_to_c_str(value); if (!path) return -1; - idx = git_index_find(self->index, path); - if (idx < 0) { - Error_set_str(idx, path); + + err = git_index_find(&idx, self->index, path); + if (err < 0) { + Error_set_str(err, path); free(path); return -1; } + free(path); + return idx; } @@ -260,7 +265,7 @@ Index_contains(Index *self, PyObject *value) path = py_path_to_c_str(value); if (!path) return -1; - idx = git_index_find(self->index, path); + idx = git_index_find(NULL, self->index, path); if (idx == GIT_ENOTFOUND) { free(path); return 0; From 5aead84b2d78c425ac0e5bd42a494e9d8675ef74 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 1 Feb 2013 13:43:48 +0100 Subject: [PATCH 0336/2237] variable renaming --- src/pygit2/index.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 4ee1ecf3d..9f3903815 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -260,18 +260,18 @@ int Index_contains(Index *self, PyObject *value) { char *path; - int idx; + int err; path = py_path_to_c_str(value); if (!path) return -1; - idx = git_index_find(NULL, self->index, path); - if (idx == GIT_ENOTFOUND) { + err = git_index_find(NULL, self->index, path); + if (err == GIT_ENOTFOUND) { free(path); return 0; } - if (idx < 0) { - Error_set_str(idx, path); + if (err < 0) { + Error_set_str(err, path); free(path); return -1; } From 1b7c809c55933ad020645b6d7454fa6d5d877774 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 1 Feb 2013 15:17:40 +0100 Subject: [PATCH 0337/2237] added GIT_EINVALIDSPEC as error code and use error msg for expected failures --- src/pygit2/error.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/pygit2/error.c b/src/pygit2/error.c index e0c702a92..6514b6a9d 100644 --- a/src/pygit2/error.c +++ b/src/pygit2/error.c @@ -50,6 +50,10 @@ PyObject * Error_type(int type) case GIT_EBUFS: return PyExc_ValueError; + /** Invalid input spec */ + case GIT_EINVALIDSPEC: + return PyExc_ValueError; + /** Skip and passthrough the given ODB backend */ case GIT_PASSTHROUGH: return GitError; @@ -79,17 +83,7 @@ PyObject* Error_set(int err) { assert(err < 0); - if(err != GIT_ERROR) { //expected failure - PyErr_SetNone(Error_type(err)); - } else { //critical failure - const git_error* error = giterr_last(); - char* message = (error == NULL) ? - "(No error information given)" : error->message; - - PyErr_SetString(Error_type(err), message); - } - - return NULL; + return Error_set_exc(Error_type(err)); } PyObject* Error_set_exc(PyObject* exception) From 21229cb298e014cb8719a4df1a6a7662f90b0469 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 1 Feb 2013 15:18:35 +0100 Subject: [PATCH 0338/2237] fix error message text --- test/test_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_config.py b/test/test_config.py index cc64a53e4..d5981ae43 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -98,8 +98,8 @@ def test_read(self): self.assertRaises(TypeError, lambda: config[()]) self.assertRaises(TypeError, lambda: config[-4]) - self.assertRaisesWithArg(pygit2.GitError, - "Invalid variable name: 'abc'", lambda: config['abc']) + self.assertRaisesWithArg(ValueError, + "Invalid config item name 'abc'", lambda: config['abc']) self.assertRaisesWithArg(KeyError, 'abc.def', lambda: config['abc.def']) self.assertTrue('core.bare' in config) From 120bb0c9a3ced325f1d378e6b06fa1266956f84e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 2 Feb 2013 00:04:04 +0100 Subject: [PATCH 0339/2237] docs: document arguments in a few more methods --- src/pygit2.c | 1 + src/pygit2/repository.c | 58 ++++++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index 0213d88ed..fdadb522f 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -238,6 +238,7 @@ moduleinit(PyObject* m) PyModule_AddIntConstant(m, "GIT_REF_OID", GIT_REF_OID); PyModule_AddIntConstant(m, "GIT_REF_SYMBOLIC", GIT_REF_SYMBOLIC); PyModule_AddIntConstant(m, "GIT_REF_PACKED", GIT_REF_PACKED); + PyModule_AddIntConstant(m, "GIT_REF_LISTALL", GIT_REF_LISTALL); /* Git status flags */ PyModule_AddIntConstant(m, "GIT_STATUS_CURRENT", GIT_STATUS_CURRENT); diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index b1d04f794..6ef9bd5ab 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -290,6 +290,13 @@ Repository_getitem(Repository *self, PyObject *value) return lookup_object_prefix(self, &oid, len, GIT_OBJ_ANY); } + +PyDoc_STRVAR(Repository_revparse_single__doc__, + "revparse_single(revision) -> Object\n\n" + "Find an object, as specified by a revision string. See " + "`man gitrevisions`, or the documentation for `git rev-parse` for " + "information on the syntax accepted."); + PyObject * Repository_revparse_single(Repository *self, PyObject *py_spec) { @@ -563,39 +570,42 @@ Repository_walk(Repository *self, PyObject *args) } +PyDoc_STRVAR(Repository_create_blob__doc__, + "create_blob(data) -> bytes\n\n" + "Create a new blob from memory."); + PyObject * -Repository_create_blob(Repository *self, PyObject *args) +Repository_create_blob(Repository *self, PyObject *data) { git_oid oid; - const char* raw; + char *raw; Py_ssize_t size; int err; - if (!PyArg_ParseTuple(args, "s#", &raw, &size)) - return NULL; + if (PyString_AsStringAndSize(data, &raw, &size)) + return NULL; err = git_blob_create_frombuffer(&oid, self->repo, (const void*)raw, size); - if (err < 0) - return Error_set(err); + return Error_set(err); return git_oid_to_python(oid.id); } PyObject * -Repository_create_blob_fromfile(Repository *self, PyObject *args) +Repository_create_blob_fromfile(Repository *self, PyObject *py_path) { git_oid oid; const char* path; int err; - if (!PyArg_ParseTuple(args, "s", &path)) - return NULL; + path = PyString_AsString(py_path); + if (path == NULL) + return NULL; err = git_blob_create_fromworkdir(&oid, self->repo, path); - if (err < 0) - return Error_set(err); + return Error_set(err); return git_oid_to_python(oid.id); } @@ -720,6 +730,11 @@ Repository_create_tag(Repository *self, PyObject *args) return git_oid_to_python(oid.id); } + +PyDoc_STRVAR(Repository_listall_references__doc__, + "listall_references([flags]) -> (str, ...)\n\n" + "Return a tuple with all the references in the repository."); + PyObject * Repository_listall_references(Repository *self, PyObject *args) { @@ -758,6 +773,11 @@ Repository_listall_references(Repository *self, PyObject *args) return py_result; } + +PyDoc_STRVAR(Repository_lookup_reference__doc__, + "lookup_reference(name) -> Reference\n\n" + "Lookup a reference by its name in a repository."); + PyObject * Repository_lookup_reference(Repository *self, PyObject *py_name) { @@ -958,19 +978,15 @@ PyMethodDef Repository_methods[] = { {"write", (PyCFunction)Repository_write, METH_VARARGS, Repository_write__doc__}, {"listall_references", (PyCFunction)Repository_listall_references, - METH_VARARGS, - "Return a list with all the references in the repository."}, + METH_VARARGS, Repository_listall_references__doc__}, {"lookup_reference", (PyCFunction)Repository_lookup_reference, METH_O, - "Lookup a reference by its name in a repository."}, + Repository_lookup_reference__doc__}, {"revparse_single", (PyCFunction)Repository_revparse_single, METH_O, - "Find an object, as specified by a revision string. See " - "`man gitrevisions`, or the documentation for `git rev-parse` for " - "information on the syntax accepted."}, - {"create_blob", (PyCFunction)Repository_create_blob, - METH_VARARGS, - "Create a new blob from memory"}, + Repository_revparse_single__doc__}, + {"create_blob", (PyCFunction)Repository_create_blob, METH_O, + Repository_create_blob__doc__}, {"create_blob_fromfile", (PyCFunction)Repository_create_blob_fromfile, - METH_VARARGS, + METH_O, "Create a new blob from file"}, {"create_reference", (PyCFunction)Repository_create_reference, METH_VARARGS|METH_KEYWORDS, From 7d3b76ea83c16c23477eb940ac97c38eb7d05941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 2 Feb 2013 00:12:20 +0100 Subject: [PATCH 0340/2237] Revert mistake --- src/pygit2/repository.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 6ef9bd5ab..fd31aaa0f 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -593,14 +593,13 @@ Repository_create_blob(Repository *self, PyObject *data) } PyObject * -Repository_create_blob_fromfile(Repository *self, PyObject *py_path) +Repository_create_blob_fromfile(Repository *self, PyObject *args) { git_oid oid; const char* path; int err; - path = PyString_AsString(py_path); - if (path == NULL) + if (!PyArg_ParseTuple(args, "s", &path)) return NULL; err = git_blob_create_fromworkdir(&oid, self->repo, path); @@ -986,7 +985,7 @@ PyMethodDef Repository_methods[] = { {"create_blob", (PyCFunction)Repository_create_blob, METH_O, Repository_create_blob__doc__}, {"create_blob_fromfile", (PyCFunction)Repository_create_blob_fromfile, - METH_O, + METH_VARARGS, "Create a new blob from file"}, {"create_reference", (PyCFunction)Repository_create_reference, METH_VARARGS|METH_KEYWORDS, From e110f1c75906f1ce22f4ee163e36eaed025e6dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 2 Feb 2013 08:50:55 +0100 Subject: [PATCH 0341/2237] Revert another mistake Forgot the Python 3 changes to the API. --- src/pygit2/repository.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index fd31aaa0f..57d0922c2 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -575,14 +575,14 @@ PyDoc_STRVAR(Repository_create_blob__doc__, "Create a new blob from memory."); PyObject * -Repository_create_blob(Repository *self, PyObject *data) +Repository_create_blob(Repository *self, PyObject *args) { git_oid oid; - char *raw; + const char *raw; Py_ssize_t size; int err; - if (PyString_AsStringAndSize(data, &raw, &size)) + if (!PyArg_ParseTuple(args, "s#", &raw, &size)) return NULL; err = git_blob_create_frombuffer(&oid, self->repo, (const void*)raw, size); @@ -982,7 +982,7 @@ PyMethodDef Repository_methods[] = { Repository_lookup_reference__doc__}, {"revparse_single", (PyCFunction)Repository_revparse_single, METH_O, Repository_revparse_single__doc__}, - {"create_blob", (PyCFunction)Repository_create_blob, METH_O, + {"create_blob", (PyCFunction)Repository_create_blob, METH_VARARGS, Repository_create_blob__doc__}, {"create_blob_fromfile", (PyCFunction)Repository_create_blob_fromfile, METH_VARARGS, From 43558261a53a0980c2c24f13ca9e1555a5280811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 2 Feb 2013 09:00:08 +0100 Subject: [PATCH 0342/2237] Add Python 3.3 to the list of supported versions --- .travis.yml | 1 + README.rst | 3 ++- docs/index.rst | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a1cdbec6d..f9d59318c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ python: - "2.7" - "3.1" - "3.2" + - "3.3" env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib diff --git a/README.rst b/README.rst index 9c462403a..4a6764c17 100644 --- a/README.rst +++ b/README.rst @@ -7,7 +7,8 @@ pygit2 - libgit2 bindings in Python :target: http://travis-ci.org/libgit2/pygit2 Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2 -implements the core of Git. Pygit2 works with Python 2.6, 2.7, 3.1 and 3.2 +implements the core of Git. Pygit2 works with Python 2.6, 2.7, 3.1, 3.2 and +3.3 Pygit2 links: diff --git a/docs/index.rst b/docs/index.rst index a92433e71..dbd866d5e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,7 +10,8 @@ Welcome to pygit2's documentation! :target: http://travis-ci.org/libgit2/pygit2 Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2 -implements the core of Git. Pygit2 works with Python 2.6, 2.7, 3.1 and 3.2 +implements the core of Git. Pygit2 works with Python 2.6, 2.7, 3.1, 3.2 and +3.3 Pygit2 links: From 4b720472601e76e4bd2d0097def7613feb17b7f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 2 Feb 2013 11:02:28 +0100 Subject: [PATCH 0343/2237] docs: finish first round on the repository chapter --- docs/repository.rst | 9 ++--- src/pygit2/repository.c | 84 +++++++++++++++++++++++++++-------------- 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/docs/repository.rst b/docs/repository.rst index e1f84a6e5..36d64ca36 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -16,11 +16,10 @@ The repository .. autoclass:: pygit2.Repository :members: path, workdir, is_bare, is_empty, revparse_single, read, write, - create_blob, create_blob_fromfile, create_commit, - create_reference, create_tag, TreeBuilder, walk, - listall_references, lookup_reference, packall_references, head, - head_is_detached, head_is_orphaned, index, status, status_file, - config + create_blob, create_blob_fromfile, create_commit, create_tag, + TreeBuilder, walk, create_reference, listall_references, + lookup_reference, packall_references, head, head_is_detached, + head_is_orphaned, index, status, status_file, config To open an existing repository:: diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 57d0922c2..5db0d1b17 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -592,6 +592,11 @@ Repository_create_blob(Repository *self, PyObject *args) return git_oid_to_python(oid.id); } + +PyDoc_STRVAR(Repository_create_blob_fromfile__doc__, + "create_blob_fromfile(path) -> bytes\n\n" + "Create a new blob from file."); + PyObject * Repository_create_blob_fromfile(Repository *self, PyObject *args) { @@ -801,21 +806,29 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) return wrap_reference(c_reference); } -PyDoc_STRVAR( - Repository_create_reference_doc, - "Create a new reference \"name\" which points to a object or another reference\n\n" - "Arguments: (name, source, force=False, symbolic=False)\n\n" - "With force=True references will be overridden. Otherwise an exception is raised" - "You can create either a normal reference or a symbolic one:\n" - " * normal reference: source has to be a valid sha hash\n" - " * symbolic reference: source has to be a valid existing reference name\n\n" - "Examples:\n" - " repo.create_reference('refs/heads/foo', repo.head.hex)\n" - " repo.create_reference('refs/tags/foo', 'refs/heads/master', symbolic = True)\n" -); +PyDoc_STRVAR(Repository_create_reference__doc__, + "create_reference(name, source, force=False, symbolic=False) -> Reference\n" + "\n" + "Create a new reference \"name\" which points to a object or another " + "reference\n" + "\n" + "Keyword arguments:\n" + "\n" + "force -- if True references will be overridden, otherwise (the default)" + " an exception is raised.\n" + "\n" + "symbolic -- if True a symbolic reference will be created, then source has" + " to be a valid existing reference name; if False (the default)" + " a normal reference will be created, then source must has to be" + " a valid SHA hash.\n" + "\n" + "Examples::\n" + "\n" + " repo.create_reference('refs/heads/foo', repo.head.hex)\n" + " repo.create_reference('refs/tags/foo', 'refs/heads/master', symbolic=True)\n"); PyObject * -Repository_create_reference(Repository *self, PyObject *args, PyObject* keywds) +Repository_create_reference(Repository *self, PyObject *args, PyObject *kw) { PyObject *py_obj; git_reference *c_reference; @@ -825,7 +838,7 @@ Repository_create_reference(Repository *self, PyObject *args, PyObject* keywds) static char *kwlist[] = {"name", "source", "force", "symbolic", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "sO|ii", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kw, "sO|ii", kwlist, &c_name, &py_obj, &force, &symbolic)) return NULL; @@ -856,20 +869,28 @@ Repository_create_reference(Repository *self, PyObject *args, PyObject* keywds) } +PyDoc_STRVAR(Repository_packall_references__doc__, + "packall_references()\n\n" + "Pack all the loose references in the repository."); + PyObject * -Repository_packall_references(Repository *self, PyObject *args) +Repository_packall_references(Repository *self, PyObject *args) { int err; - /* 1- Pack */ err = git_reference_packall(self->repo); if (err < 0) return Error_set(err); - /* 2- Return None */ Py_RETURN_NONE; } + +PyDoc_STRVAR(Repository_status__doc__, + "status() -> {str: int}\n\n" + "Reads the status of the repository and returns a dictionary with file " + "paths as keys and status flags as values. See pygit2.GIT_STATUS_*."); + int read_status_cb(const char *path, unsigned int status_flags, void *payload) { @@ -894,6 +915,11 @@ Repository_status(Repository *self, PyObject *args) return payload_dict; } + +PyDoc_STRVAR(Repository_status_file__doc__, + "status_file(path) -> int\n\n" + "Returns the status of the given file path."); + PyObject * Repository_status_file(Repository *self, PyObject *value) { @@ -914,6 +940,11 @@ Repository_status_file(Repository *self, PyObject *value) return PyInt_FromLong(status); } + +PyDoc_STRVAR(Repository_TreeBuilder__doc__, + "TreeBuilder([tree]) -> TreeBuilder\n\n" + "Create a TreeBuilder object for this repository."); + PyObject * Repository_TreeBuilder(Repository *self, PyObject *args) { @@ -985,20 +1016,17 @@ PyMethodDef Repository_methods[] = { {"create_blob", (PyCFunction)Repository_create_blob, METH_VARARGS, Repository_create_blob__doc__}, {"create_blob_fromfile", (PyCFunction)Repository_create_blob_fromfile, - METH_VARARGS, - "Create a new blob from file"}, + METH_VARARGS, Repository_create_blob_fromfile__doc__}, {"create_reference", (PyCFunction)Repository_create_reference, - METH_VARARGS|METH_KEYWORDS, - Repository_create_reference_doc}, + METH_VARARGS|METH_KEYWORDS, Repository_create_reference__doc__}, {"packall_references", (PyCFunction)Repository_packall_references, - METH_NOARGS, "Pack all the loose references in the repository."}, - {"status", (PyCFunction)Repository_status, METH_NOARGS, "Reads the " - "status of the repository and returns a dictionary with file paths " - "as keys and status flags as values.\nSee pygit2.GIT_STATUS_*."}, + METH_NOARGS, Repository_packall_references__doc__}, + {"status", (PyCFunction)Repository_status, METH_NOARGS, + Repository_status__doc__}, {"status_file", (PyCFunction)Repository_status_file, METH_O, - "Returns the status of the given file path."}, + Repository_status_file__doc__}, {"TreeBuilder", (PyCFunction)Repository_TreeBuilder, METH_VARARGS, - "Create a TreeBuilder object for this repository."}, + Repository_TreeBuilder__doc__}, {NULL} }; @@ -1045,7 +1073,7 @@ PyDoc_STRVAR(Repository__doc__, PyTypeObject RepositoryType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Repository", /* tp_name */ + "_pygit2.Repository", /* tp_name */ sizeof(Repository), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Repository_dealloc, /* tp_dealloc */ From c0543b5db466c85885c226c42c297220dc832397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 2 Feb 2013 22:20:03 +0100 Subject: [PATCH 0344/2237] docs: use PyDoc_STRVAR in the Object type --- src/pygit2/object.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/pygit2/object.c b/src/pygit2/object.c index d20f7f174..dcb559dd2 100644 --- a/src/pygit2/object.c +++ b/src/pygit2/object.c @@ -48,6 +48,10 @@ Object_dealloc(Object* self) PyObject_Del(self); } + +PyDoc_STRVAR(Object_get_oid__doc__, + "The object id, a byte string 20 bytes long."); + PyObject * Object_get_oid(Object *self) { @@ -59,6 +63,10 @@ Object_get_oid(Object *self) return git_oid_to_python(oid->id); } + +PyDoc_STRVAR(Object_get_hex__doc__, + "Hexadecimal representation of the object id, a text string 40 chars long."); + PyObject * Object_get_hex(Object *self) { @@ -70,12 +78,21 @@ Object_get_hex(Object *self) return git_oid_to_py_str(oid); } + +PyDoc_STRVAR(Object_get_type__doc__, + "One of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_TAG " + "constants."); + PyObject * Object_get_type(Object *self) { return PyInt_FromLong(git_object_type(self->obj)); } + +PyDoc_STRVAR(Object_read_raw__doc__, + "Returns the byte string with the raw contents of the of the object."); + PyObject * Object_read_raw(Object *self) { @@ -99,28 +116,24 @@ Object_read_raw(Object *self) } PyGetSetDef Object_getseters[] = { - {"oid", (getter)Object_get_oid, NULL, - "The object id, a byte string 20 bytes long.", NULL}, - {"hex", (getter)Object_get_hex, NULL, - "Hexadecimal representation of the object id, a text string 40 chars " - "long.", - NULL}, - {"type", (getter)Object_get_type, NULL, - "One of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, GIT_OBJ_BLOB or " - "GIT_OBJ_TAG constants.", - NULL}, + {"oid", (getter)Object_get_oid, NULL, Object_get_oid__doc__, NULL}, + {"hex", (getter)Object_get_hex, NULL, Object_get_hex__doc__, NULL}, + {"type", (getter)Object_get_type, NULL, Object_get_type__doc__, NULL}, {NULL} }; PyMethodDef Object_methods[] = { {"read_raw", (PyCFunction)Object_read_raw, METH_NOARGS, - "Returns the byte string with the raw contents of the of the object."}, + Object_read_raw__doc__}, {NULL} }; + +PyDoc_STRVAR(Object__doc__, "Base class for Git objects."); + PyTypeObject ObjectType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Object", /* tp_name */ + "_pygit2.Object", /* tp_name */ sizeof(Object), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Object_dealloc, /* tp_dealloc */ @@ -139,7 +152,7 @@ PyTypeObject ObjectType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Object objects", /* tp_doc */ + Object__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ From bf81dfe193adf4ebad6b859255e65bb369524458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 3 Feb 2013 11:21:03 +0100 Subject: [PATCH 0345/2237] Define GETTER helper macro To save typing, and increase coding consistency. --- include/pygit2/utils.h | 7 ++++ src/pygit2/commit.c | 79 ++++++++++++++++++++++++++---------------- 2 files changed, 57 insertions(+), 29 deletions(-) diff --git a/include/pygit2/utils.h b/include/pygit2/utils.h index 359b17844..e3e68d84b 100644 --- a/include/pygit2/utils.h +++ b/include/pygit2/utils.h @@ -95,4 +95,11 @@ 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) + +/* Helpers to make shorter PyGetSetDef blocks */ +#define GETTER(type, attr)\ + {#attr, (getter) type ## _ ## attr ## __get__, NULL,\ + type ## _ ## attr ## __doc__, NULL} + + #endif diff --git a/src/pygit2/commit.c b/src/pygit2/commit.c index ab13948b4..919a92f5f 100644 --- a/src/pygit2/commit.c +++ b/src/pygit2/commit.c @@ -34,8 +34,11 @@ extern PyTypeObject TreeType; + +PyDoc_STRVAR(Commit_message_encoding__doc__, "Message encoding."); + PyObject * -Commit_get_message_encoding(Commit *commit) +Commit_message_encoding__get__(Commit *commit) { const char *encoding; @@ -46,8 +49,11 @@ Commit_get_message_encoding(Commit *commit) return to_encoding(encoding); } + +PyDoc_STRVAR(Commit_message__doc__, "The commit message, a text string."); + PyObject * -Commit_get_message(Commit *commit) +Commit_message__get__(Commit *commit) { const char *message, *encoding; @@ -56,26 +62,38 @@ Commit_get_message(Commit *commit) return to_unicode(message, encoding, "strict"); } + +PyDoc_STRVAR(Commit__message__doc__, "Message (bytes)."); + PyObject * -Commit_get_raw_message(Commit *commit) +Commit__message__get__(Commit *commit) { return PyString_FromString(git_commit_message(commit->commit)); } + +PyDoc_STRVAR(Commit_commit_time__doc__, "Commit time."); + PyObject * -Commit_get_commit_time(Commit *commit) +Commit_commit_time__get__(Commit *commit) { return PyLong_FromLong(git_commit_time(commit->commit)); } + +PyDoc_STRVAR(Commit_commit_time_offset__doc__, "Commit time offset."); + PyObject * -Commit_get_commit_time_offset(Commit *commit) +Commit_commit_time_offset__get__(Commit *commit) { return PyLong_FromLong(git_commit_time_offset(commit->commit)); } + +PyDoc_STRVAR(Commit_committer__doc__, "The committer of the commit."); + PyObject * -Commit_get_committer(Commit *self) +Commit_committer__get__(Commit *self) { const git_signature *signature; const char *encoding; @@ -86,8 +104,11 @@ Commit_get_committer(Commit *self) return build_signature((Object*)self, signature, encoding); } + +PyDoc_STRVAR(Commit_author__doc__, "The author of the commit."); + PyObject * -Commit_get_author(Commit *self) +Commit_author__get__(Commit *self) { const git_signature *signature; const char *encoding; @@ -98,8 +119,11 @@ Commit_get_author(Commit *self) return build_signature((Object*)self, signature, encoding); } + +PyDoc_STRVAR(Commit_tree__doc__, "The tree object attached to the commit."); + PyObject * -Commit_get_tree(Commit *commit) +Commit_tree__get__(Commit *commit) { git_tree *tree; Tree *py_tree; @@ -121,8 +145,11 @@ Commit_get_tree(Commit *commit) return (PyObject*)py_tree; } + +PyDoc_STRVAR(Commit_parents__doc__, "The list of parent commits."); + PyObject * -Commit_get_parents(Commit *commit) +Commit_parents__get__(Commit *commit) { unsigned int i, parent_count; const git_oid *parent_oid; @@ -154,30 +181,24 @@ Commit_get_parents(Commit *commit) } PyGetSetDef Commit_getseters[] = { - {"message_encoding", (getter)Commit_get_message_encoding, NULL, - "message encoding", NULL}, - {"message", (getter)Commit_get_message, NULL, - "The commit message, a text string.", NULL}, - {"_message", (getter)Commit_get_raw_message, NULL, "message (bytes)", - NULL}, - {"commit_time", (getter)Commit_get_commit_time, NULL, "commit time", - NULL}, - {"commit_time_offset", (getter)Commit_get_commit_time_offset, NULL, - "commit time offset", NULL}, - {"committer", (getter)Commit_get_committer, NULL, - "The committer of the commit.", NULL}, - {"author", (getter)Commit_get_author, NULL, - "The author of the commit.", NULL}, - {"tree", (getter)Commit_get_tree, NULL, - "The tree object attached to the commit.", NULL}, - {"parents", (getter)Commit_get_parents, NULL, - "The list of parent commits.", NULL}, + GETTER(Commit, message_encoding), + GETTER(Commit, message), + GETTER(Commit, _message), + GETTER(Commit, commit_time), + GETTER(Commit, commit_time_offset), + GETTER(Commit, committer), + GETTER(Commit, author), + GETTER(Commit, tree), + GETTER(Commit, parents), {NULL} }; + +PyDoc_STRVAR(Commit__doc__, "Commit objects."); + PyTypeObject CommitType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Commit", /* tp_name */ + "_pygit2.Commit", /* tp_name */ sizeof(Commit), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ @@ -196,7 +217,7 @@ PyTypeObject CommitType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Commit objects", /* tp_doc */ + Commit__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ From 6a5e4879d9699308ec6135700eace8c59320eb39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 3 Feb 2013 12:07:25 +0100 Subject: [PATCH 0346/2237] Define the METHOD macro and deploy it --- include/pygit2/utils.h | 5 ++- src/pygit2/index.c | 50 +++++++++++++----------- src/pygit2/object.c | 21 +++++----- src/pygit2/repository.c | 86 ++++++++++++++++------------------------- 4 files changed, 74 insertions(+), 88 deletions(-) diff --git a/include/pygit2/utils.h b/include/pygit2/utils.h index e3e68d84b..903966aeb 100644 --- a/include/pygit2/utils.h +++ b/include/pygit2/utils.h @@ -96,7 +96,10 @@ char * py_str_to_c_str(PyObject *value, const char *encoding); py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding) -/* Helpers to make shorter PyGetSetDef blocks */ +/* Helpers to make shorter PyMethodDef and PyGetSetDef blocks */ +#define METHOD(type, name, args)\ + {#name, (PyCFunction) type ## _ ## name, args, type ## _ ## name ## __doc__} + #define GETTER(type, attr)\ {#attr, (getter) type ## _ ## attr ## __get__, NULL,\ type ## _ ## attr ## __doc__, NULL} diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 9f3903815..afb79f9ba 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -46,8 +46,7 @@ Index_init(Index *self, PyObject *args, PyObject *kwds) int err; if (kwds) { - PyErr_SetString(PyExc_TypeError, - "Index takes no keyword arguments"); + PyErr_SetString(PyExc_TypeError, "Index takes no keyword arguments"); return -1; } @@ -79,6 +78,7 @@ Index_traverse(Index *self, visitproc visit, void *arg) return 0; } + PyDoc_STRVAR(Index_add__doc__, "add(path)\n\n" "Add or update an index entry from a file in disk."); @@ -99,6 +99,7 @@ Index_add(Index *self, PyObject *args) Py_RETURN_NONE; } + PyDoc_STRVAR(Index_clear__doc__, "clear()\n\n" "Clear the contents (all the entries) of an index object."); @@ -160,13 +161,14 @@ Index_diff(Index *self, PyObject *args) return (PyObject*)py_diff; } -PyDoc_STRVAR(Index_find__doc__, + +PyDoc_STRVAR(Index__find__doc__, "_find(path) -> integer\n\n" "Find the first index of any entries which point to given path in the " "index file."); PyObject * -Index_find(Index *self, PyObject *py_path) +Index__find(Index *self, PyObject *py_path) { char *path; size_t idx; @@ -416,17 +418,15 @@ Index_write_tree(Index *self) } PyMethodDef Index_methods[] = { - {"add", (PyCFunction)Index_add, METH_VARARGS, Index_add__doc__}, - {"remove", (PyCFunction)Index_remove, METH_VARARGS, Index_remove__doc__}, - {"clear", (PyCFunction)Index_clear, METH_NOARGS, Index_clear__doc__}, - {"diff", (PyCFunction)Index_diff, METH_VARARGS, Index_diff__doc__}, - {"_find", (PyCFunction)Index_find, METH_O, Index_find__doc__}, - {"read", (PyCFunction)Index_read, METH_NOARGS, Index_read__doc__}, - {"write", (PyCFunction)Index_write, METH_NOARGS, Index_write__doc__}, - {"read_tree", (PyCFunction)Index_read_tree, METH_O, - Index_read_tree__doc__}, - {"write_tree", (PyCFunction)Index_write_tree, METH_NOARGS, - Index_write_tree__doc__}, + METHOD(Index, add, METH_VARARGS), + METHOD(Index, remove, METH_VARARGS), + METHOD(Index, clear, METH_NOARGS), + METHOD(Index, diff, METH_VARARGS), + METHOD(Index, _find, METH_O), + METHOD(Index, read, METH_NOARGS), + METHOD(Index, write, METH_NOARGS), + METHOD(Index, read_tree, METH_O), + METHOD(Index, write_tree, METH_NOARGS), {NULL} }; @@ -550,43 +550,47 @@ IndexEntry_dealloc(IndexEntry *self) PyObject_Del(self); } + PyDoc_STRVAR(IndexEntry_mode__doc__, "Mode."); PyObject * -IndexEntry_get_mode(IndexEntry *self) +IndexEntry_mode__get__(IndexEntry *self) { return PyInt_FromLong(self->entry->mode); } + PyDoc_STRVAR(IndexEntry_path__doc__, "Path."); PyObject * -IndexEntry_get_path(IndexEntry *self) +IndexEntry_path__get__(IndexEntry *self) { return to_path(self->entry->path); } + PyDoc_STRVAR(IndexEntry_oid__doc__, "Object id."); PyObject * -IndexEntry_get_oid(IndexEntry *self) +IndexEntry_oid__get__(IndexEntry *self) { return git_oid_to_python(self->entry->oid.id); } + PyDoc_STRVAR(IndexEntry_hex__doc__, "Hex id."); PyObject * -IndexEntry_get_hex(IndexEntry *self) +IndexEntry_hex__get__(IndexEntry *self) { return git_oid_to_py_str(&self->entry->oid); } PyGetSetDef IndexEntry_getseters[] = { - {"mode", (getter)IndexEntry_get_mode, NULL, IndexEntry_mode__doc__, NULL}, - {"path", (getter)IndexEntry_get_path, NULL, IndexEntry_path__doc__, NULL}, - {"oid", (getter)IndexEntry_get_oid, NULL, IndexEntry_oid__doc__, NULL}, - {"hex", (getter)IndexEntry_get_hex, NULL, IndexEntry_hex__doc__, NULL}, + GETTER(IndexEntry, mode), + GETTER(IndexEntry, path), + GETTER(IndexEntry, oid), + GETTER(IndexEntry, hex), {NULL}, }; diff --git a/src/pygit2/object.c b/src/pygit2/object.c index dcb559dd2..1b64be563 100644 --- a/src/pygit2/object.c +++ b/src/pygit2/object.c @@ -49,11 +49,11 @@ Object_dealloc(Object* self) } -PyDoc_STRVAR(Object_get_oid__doc__, +PyDoc_STRVAR(Object_oid__doc__, "The object id, a byte string 20 bytes long."); PyObject * -Object_get_oid(Object *self) +Object_oid__get__(Object *self) { const git_oid *oid; @@ -64,11 +64,11 @@ Object_get_oid(Object *self) } -PyDoc_STRVAR(Object_get_hex__doc__, +PyDoc_STRVAR(Object_hex__doc__, "Hexadecimal representation of the object id, a text string 40 chars long."); PyObject * -Object_get_hex(Object *self) +Object_hex__get__(Object *self) { const git_oid *oid; @@ -79,12 +79,12 @@ Object_get_hex(Object *self) } -PyDoc_STRVAR(Object_get_type__doc__, +PyDoc_STRVAR(Object_type__doc__, "One of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_TAG " "constants."); PyObject * -Object_get_type(Object *self) +Object_type__get__(Object *self) { return PyInt_FromLong(git_object_type(self->obj)); } @@ -116,15 +116,14 @@ Object_read_raw(Object *self) } PyGetSetDef Object_getseters[] = { - {"oid", (getter)Object_get_oid, NULL, Object_get_oid__doc__, NULL}, - {"hex", (getter)Object_get_hex, NULL, Object_get_hex__doc__, NULL}, - {"type", (getter)Object_get_type, NULL, Object_get_type__doc__, NULL}, + GETTER(Object, oid), + GETTER(Object, hex), + GETTER(Object, type), {NULL} }; PyMethodDef Object_methods[] = { - {"read_raw", (PyCFunction)Object_read_raw, METH_NOARGS, - Object_read_raw__doc__}, + METHOD(Object, read_raw, METH_NOARGS), {NULL} }; diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 5db0d1b17..79c704b0d 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -199,7 +199,7 @@ PyDoc_STRVAR(Repository_head__doc__, "Current head reference of the repository."); PyObject * -Repository_get_head(Repository *self) +Repository_head__get__(Repository *self) { git_reference *head; const git_oid *oid; @@ -228,7 +228,7 @@ PyDoc_STRVAR(Repository_head_is_detached__doc__, "instead of a branch."); PyObject * -Repository_head_is_detached(Repository *self) +Repository_head_is_detached__get__(Repository *self) { if (git_repository_head_detached(self->repo) > 0) Py_RETURN_TRUE; @@ -242,7 +242,7 @@ PyDoc_STRVAR(Repository_head_is_orphaned__doc__, "refs namespace, because it doesn't have any commit to point to."); PyObject * -Repository_head_is_orphaned(Repository *self) +Repository_head_is_orphaned__get__(Repository *self) { if (git_repository_head_orphan(self->repo) > 0) Py_RETURN_TRUE; @@ -255,7 +255,7 @@ PyDoc_STRVAR(Repository_is_empty__doc__, "Check if a repository is empty."); PyObject * -Repository_is_empty(Repository *self) +Repository_is_empty__get__(Repository *self) { if (git_repository_is_empty(self->repo) > 0) Py_RETURN_TRUE; @@ -268,7 +268,7 @@ PyDoc_STRVAR(Repository_is_bare__doc__, "Check if a repository is a bare repository."); PyObject * -Repository_is_bare(Repository *self) +Repository_is_bare__get__(Repository *self) { if (git_repository_is_bare(self->repo) > 0) Py_RETURN_TRUE; @@ -422,7 +422,7 @@ Repository_write(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_index__doc__, "Index file."); PyObject * -Repository_get_index(Repository *self, void *closure) +Repository_index__get__(Repository *self, void *closure) { int err; git_index *index; @@ -457,7 +457,7 @@ PyDoc_STRVAR(Repository_path__doc__, "The normalized path to the git repository."); PyObject * -Repository_get_path(Repository *self, void *closure) +Repository_path__get__(Repository *self, void *closure) { return to_path(git_repository_path(self->repo)); } @@ -468,7 +468,7 @@ PyDoc_STRVAR(Repository_workdir__doc__, "If the repository is bare, None will be returned."); PyObject * -Repository_get_workdir(Repository *self, void *closure) +Repository_workdir__get__(Repository *self, void *closure) { const char *c_path; @@ -487,7 +487,7 @@ PyDoc_STRVAR(Repository_config__doc__, "(if they are available)."); PyObject * -Repository_get_config(Repository *self, void *closure) +Repository_config__get__(Repository *self, void *closure) { int err; git_config *config; @@ -998,54 +998,34 @@ Repository_TreeBuilder(Repository *self, PyObject *args) } PyMethodDef Repository_methods[] = { - {"create_commit", (PyCFunction)Repository_create_commit, METH_VARARGS, - Repository_create_commit__doc__}, - {"create_tag", (PyCFunction)Repository_create_tag, METH_VARARGS, - Repository_create_tag__doc__}, - {"walk", (PyCFunction)Repository_walk, METH_VARARGS, - Repository_walk__doc__}, - {"read", (PyCFunction)Repository_read, METH_O, Repository_read__doc__}, - {"write", (PyCFunction)Repository_write, METH_VARARGS, - Repository_write__doc__}, - {"listall_references", (PyCFunction)Repository_listall_references, - METH_VARARGS, Repository_listall_references__doc__}, - {"lookup_reference", (PyCFunction)Repository_lookup_reference, METH_O, - Repository_lookup_reference__doc__}, - {"revparse_single", (PyCFunction)Repository_revparse_single, METH_O, - Repository_revparse_single__doc__}, - {"create_blob", (PyCFunction)Repository_create_blob, METH_VARARGS, - Repository_create_blob__doc__}, - {"create_blob_fromfile", (PyCFunction)Repository_create_blob_fromfile, - METH_VARARGS, Repository_create_blob_fromfile__doc__}, - {"create_reference", (PyCFunction)Repository_create_reference, - METH_VARARGS|METH_KEYWORDS, Repository_create_reference__doc__}, - {"packall_references", (PyCFunction)Repository_packall_references, - METH_NOARGS, Repository_packall_references__doc__}, - {"status", (PyCFunction)Repository_status, METH_NOARGS, - Repository_status__doc__}, - {"status_file", (PyCFunction)Repository_status_file, METH_O, - Repository_status_file__doc__}, - {"TreeBuilder", (PyCFunction)Repository_TreeBuilder, METH_VARARGS, - Repository_TreeBuilder__doc__}, + METHOD(Repository, create_blob, METH_VARARGS), + METHOD(Repository, create_blob_fromfile, METH_VARARGS), + METHOD(Repository, create_commit, METH_VARARGS), + METHOD(Repository, create_tag, METH_VARARGS), + METHOD(Repository, TreeBuilder, METH_VARARGS), + METHOD(Repository, walk, METH_VARARGS), + METHOD(Repository, read, METH_O), + METHOD(Repository, write, METH_VARARGS), + METHOD(Repository, create_reference, METH_VARARGS|METH_KEYWORDS), + METHOD(Repository, listall_references, METH_VARARGS), + METHOD(Repository, lookup_reference, METH_O), + METHOD(Repository, packall_references, METH_NOARGS), + METHOD(Repository, revparse_single, METH_O), + METHOD(Repository, status, METH_NOARGS), + METHOD(Repository, status_file, METH_O), {NULL} }; PyGetSetDef Repository_getseters[] = { - {"index", (getter)Repository_get_index, NULL, Repository_index__doc__, - NULL}, - {"path", (getter)Repository_get_path, NULL, Repository_path__doc__, NULL}, - {"head", (getter)Repository_get_head, NULL, Repository_head__doc__, NULL}, - {"head_is_detached", (getter)Repository_head_is_detached, NULL, - Repository_head_is_detached__doc__}, - {"head_is_orphaned", (getter)Repository_head_is_orphaned, NULL, - Repository_head_is_orphaned__doc__}, - {"is_empty", (getter)Repository_is_empty, NULL, - Repository_is_empty__doc__}, - {"is_bare", (getter)Repository_is_bare, NULL, Repository_is_bare__doc__}, - {"config", (getter)Repository_get_config, NULL, Repository_config__doc__, - NULL}, - {"workdir", (getter)Repository_get_workdir, NULL, - Repository_workdir__doc__, NULL}, + GETTER(Repository, index), + GETTER(Repository, path), + GETTER(Repository, head), + GETTER(Repository, head_is_detached), + GETTER(Repository, head_is_orphaned), + GETTER(Repository, is_empty), + GETTER(Repository, is_bare), + GETTER(Repository, config), + GETTER(Repository, workdir), {NULL} }; From 4933b8b199a80e6de7e6f4167cc42f8daa596466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 3 Feb 2013 12:08:36 +0100 Subject: [PATCH 0347/2237] travis: drop 3.1 since travis does not support it anymore --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f9d59318c..2279b1581 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ language: python python: - "2.6" - "2.7" - - "3.1" - "3.2" - "3.3" From 0b5b9fb389e702978291c5b592b5072cd6d7a0b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 4 Feb 2013 11:49:57 +0100 Subject: [PATCH 0348/2237] Use PyDoc_STRVAR in signature.c --- src/pygit2/signature.c | 57 +++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/src/pygit2/signature.c b/src/pygit2/signature.c index 720ed3842..db25968fd 100644 --- a/src/pygit2/signature.c +++ b/src/pygit2/signature.c @@ -90,8 +90,11 @@ Signature_dealloc(Signature *self) Py_TYPE(self)->tp_free((PyObject*)self); } + +PyDoc_STRVAR(Signature__encoding__doc__, "Encoding."); + PyObject * -Signature_get_encoding(Signature *self) +Signature__encoding__get__(Signature *self) { const char *encoding; @@ -102,57 +105,77 @@ Signature_get_encoding(Signature *self) return to_encoding(encoding); } + +PyDoc_STRVAR(Signature__name__doc__, "Name (bytes)."); + PyObject * -Signature_get_raw_name(Signature *self) +Signature__name__get__(Signature *self) { return to_bytes(self->signature->name); } + +PyDoc_STRVAR(Signature__email__doc__, "Email (bytes)."); + PyObject * -Signature_get_raw_email(Signature *self) +Signature__email__get__(Signature *self) { return to_bytes(self->signature->email); } + +PyDoc_STRVAR(Signature_name__doc__, "Name."); + PyObject * -Signature_get_name(Signature *self) +Signature_name__get__(Signature *self) { return to_unicode(self->signature->name, self->encoding, "strict"); } + +PyDoc_STRVAR(Signature_email__doc__, "Email address."); + PyObject * -Signature_get_email(Signature *self) +Signature_email__get__(Signature *self) { return to_unicode(self->signature->email, self->encoding, "strict"); } + +PyDoc_STRVAR(Signature_time__doc__, "Unix time."); + PyObject * -Signature_get_time(Signature *self) +Signature_time__get__(Signature *self) { return PyInt_FromLong(self->signature->when.time); } + +PyDoc_STRVAR(Signature_offset__doc__, "Offset from UTC in minutes"); + PyObject * -Signature_get_offset(Signature *self) +Signature_offset__get__(Signature *self) { return PyInt_FromLong(self->signature->when.offset); } PyGetSetDef Signature_getseters[] = { - {"_encoding", (getter)Signature_get_encoding, NULL, "encoding", NULL}, - {"_name", (getter)Signature_get_raw_name, NULL, "Name (bytes)", NULL}, - {"_email", (getter)Signature_get_raw_email, NULL, "Email (bytes)", NULL}, - {"name", (getter)Signature_get_name, NULL, "Name", NULL}, - {"email", (getter)Signature_get_email, NULL, "Email address", NULL}, - {"time", (getter)Signature_get_time, NULL, "Unix time", NULL}, - {"offset", (getter)Signature_get_offset, NULL, - "Offset from UTC in minutes", NULL}, + GETTER(Signature, _encoding), + GETTER(Signature, _name), + GETTER(Signature, _email), + GETTER(Signature, name), + GETTER(Signature, email), + GETTER(Signature, time), + GETTER(Signature, offset), {NULL} }; + +PyDoc_STRVAR(Signature__doc__, "Signature."); + PyTypeObject SignatureType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Signature", /* tp_name */ + "_pygit2.Signature", /* tp_name */ sizeof(Signature), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Signature_dealloc, /* tp_dealloc */ @@ -171,7 +194,7 @@ PyTypeObject SignatureType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ - "Signature", /* tp_doc */ + Signature__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ From ad76120e8f09a235966d588183c9ebade2fb73d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 4 Feb 2013 12:15:04 +0100 Subject: [PATCH 0349/2237] Split treebuilder.c from tree.c --- include/pygit2/tree.h | 4 - include/pygit2/treebuilder.h | 41 +++++++++ src/pygit2/tree.c | 131 +---------------------------- src/pygit2/treebuilder.c | 159 +++++++++++++++++++++++++++++++++++ 4 files changed, 203 insertions(+), 132 deletions(-) create mode 100644 include/pygit2/treebuilder.h create mode 100644 src/pygit2/treebuilder.c diff --git a/include/pygit2/tree.h b/include/pygit2/tree.h index 68218f75c..e1cea650b 100644 --- a/include/pygit2/tree.h +++ b/include/pygit2/tree.h @@ -43,8 +43,4 @@ TreeEntry* Tree_getitem_by_index(Tree *self, PyObject *py_index); TreeEntry* Tree_getitem(Tree *self, PyObject *value); PyObject* Tree_diff_tree(Tree *self, PyObject *args); -PyObject* TreeBuilder_insert(TreeBuilder *self, PyObject *args); -PyObject* TreeBuilder_write(TreeBuilder *self); -PyObject* TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename); -PyObject* TreeBuilder_clear(TreeBuilder *self); #endif diff --git a/include/pygit2/treebuilder.h b/include/pygit2/treebuilder.h new file mode 100644 index 000000000..982239288 --- /dev/null +++ b/include/pygit2/treebuilder.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef INCLUDE_pygit2_treebuilder_h +#define INCLUDE_pygit2_treebuilder_h + +#define PY_SSIZE_T_CLEAN +#include +#include +#include + +PyObject* TreeBuilder_insert(TreeBuilder *self, PyObject *args); +PyObject* TreeBuilder_write(TreeBuilder *self); +PyObject* TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename); +PyObject* TreeBuilder_clear(TreeBuilder *self); + +#endif diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index fcbe88595..c50bca482 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -101,7 +101,7 @@ PyMethodDef TreeEntry_methods[] = { PyTypeObject TreeEntryType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.TreeEntry", /* tp_name */ + "_pygit2.TreeEntry", /* tp_name */ sizeof(TreeEntry), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)TreeEntry_dealloc, /* tp_dealloc */ @@ -348,7 +348,7 @@ PyMethodDef Tree_methods[] = { PyTypeObject TreeType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Tree", /* tp_name */ + "_pygit2.Tree", /* tp_name */ sizeof(Tree), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ @@ -388,131 +388,6 @@ PyTypeObject TreeType = { }; - -void -TreeBuilder_dealloc(TreeBuilder* self) -{ - Py_XDECREF(self->repo); - git_treebuilder_free(self->bld); - PyObject_Del(self); -} - -PyObject * -TreeBuilder_insert(TreeBuilder *self, PyObject *args) -{ - PyObject *py_oid; - int len, err, attr; - git_oid oid; - const char *fname; - - if (!PyArg_ParseTuple(args, "sOi", &fname, &py_oid, &attr)) { - return NULL; - } - - len = py_str_to_git_oid(py_oid, &oid); - if (len < 0) { - return NULL; - } - - err = git_treebuilder_insert(NULL, self->bld, fname, &oid, attr); - if (err < 0) { - Error_set(err); - return NULL; - } - - Py_RETURN_NONE; -} - -PyObject * -TreeBuilder_write(TreeBuilder *self) -{ - int err; - git_oid oid; - - err = git_treebuilder_write(&oid, self->repo->repo, self->bld); - if (err < 0) - return Error_set(err); - - return git_oid_to_python(&oid); -} - -PyObject * -TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename) -{ - char *filename = py_path_to_c_str(py_filename); - int err = 0; - - if (filename == NULL) - return NULL; - - err = git_treebuilder_remove(self->bld, filename); - free(filename); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; -} - -PyObject * -TreeBuilder_clear(TreeBuilder *self) -{ - git_treebuilder_clear(self->bld); - Py_RETURN_NONE; -} - -PyMethodDef TreeBuilder_methods[] = { - {"insert", (PyCFunction)TreeBuilder_insert, METH_VARARGS, - "Insert or replace an entry in the treebuilder"}, - {"write", (PyCFunction)TreeBuilder_write, METH_NOARGS, - "Write the tree to the given repository"}, - {"remove", (PyCFunction)TreeBuilder_remove, METH_O, - "Remove an entry from the builder"}, - {"clear", (PyCFunction)TreeBuilder_clear, METH_NOARGS, - "Clear all the entries in the builder"}, - {NULL, NULL, 0, NULL} -}; - -PyTypeObject TreeBuilderType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.TreeBuilder", /* tp_name */ - sizeof(TreeBuilder), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)TreeBuilder_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "TreeBuilder objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - TreeBuilder_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* 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 */ -}; - void TreeIter_dealloc(TreeIter *self) { @@ -535,7 +410,7 @@ TreeIter_iternext(TreeIter *self) PyTypeObject TreeIterType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.TreeIter", /* tp_name */ + "_pygit2.TreeIter", /* tp_name */ sizeof(TreeIter), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)TreeIter_dealloc , /* tp_dealloc */ diff --git a/src/pygit2/treebuilder.c b/src/pygit2/treebuilder.c new file mode 100644 index 000000000..0d8fdac94 --- /dev/null +++ b/src/pygit2/treebuilder.c @@ -0,0 +1,159 @@ +/* + * 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 +#include + + +void +TreeBuilder_dealloc(TreeBuilder *self) +{ + Py_XDECREF(self->repo); + git_treebuilder_free(self->bld); + PyObject_Del(self); +} + +PyObject * +TreeBuilder_insert(TreeBuilder *self, PyObject *args) +{ + PyObject *py_oid; + int len, err, attr; + git_oid oid; + const char *fname; + + if (!PyArg_ParseTuple(args, "sOi", &fname, &py_oid, &attr)) { + return NULL; + } + + len = py_str_to_git_oid(py_oid, &oid); + if (len < 0) { + return NULL; + } + + err = git_treebuilder_insert(NULL, self->bld, fname, &oid, attr); + if (err < 0) { + Error_set(err); + return NULL; + } + + Py_RETURN_NONE; +} + +PyObject * +TreeBuilder_write(TreeBuilder *self) +{ + int err; + git_oid oid; + + err = git_treebuilder_write(&oid, self->repo->repo, self->bld); + if (err < 0) + return Error_set(err); + + return git_oid_to_python(&oid); +} + +PyObject * +TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename) +{ + char *filename = py_path_to_c_str(py_filename); + int err = 0; + + if (filename == NULL) + return NULL; + + err = git_treebuilder_remove(self->bld, filename); + free(filename); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + +PyObject * +TreeBuilder_clear(TreeBuilder *self) +{ + git_treebuilder_clear(self->bld); + Py_RETURN_NONE; +} + +PyMethodDef TreeBuilder_methods[] = { + {"insert", (PyCFunction)TreeBuilder_insert, METH_VARARGS, + "Insert or replace an entry in the treebuilder"}, + {"write", (PyCFunction)TreeBuilder_write, METH_NOARGS, + "Write the tree to the given repository"}, + {"remove", (PyCFunction)TreeBuilder_remove, METH_O, + "Remove an entry from the builder"}, + {"clear", (PyCFunction)TreeBuilder_clear, METH_NOARGS, + "Clear all the entries in the builder"}, + {NULL, NULL, 0, NULL} +}; + +PyTypeObject TreeBuilderType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.TreeBuilder", /* tp_name */ + sizeof(TreeBuilder), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)TreeBuilder_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "TreeBuilder objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + TreeBuilder_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* 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 */ +}; From b0a93435eb0ab075aa2307c49653bc93da505464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 4 Feb 2013 12:43:16 +0100 Subject: [PATCH 0350/2237] Use PyDoc_STRVAR in tree.c --- src/pygit2/tree.c | 72 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index c50bca482..195bd6953 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -47,20 +47,29 @@ TreeEntry_dealloc(TreeEntry *self) PyObject_Del(self); } + +PyDoc_STRVAR(TreeEntry_filemode__doc__, "Filemode."); + PyObject * -TreeEntry_get_filemode(TreeEntry *self) +TreeEntry_filemode__get__(TreeEntry *self) { return PyInt_FromLong(git_tree_entry_filemode(self->entry)); } + +PyDoc_STRVAR(TreeEntry_name__doc__, "Name."); + PyObject * -TreeEntry_get_name(TreeEntry *self) +TreeEntry_name__get__(TreeEntry *self) { return to_path(git_tree_entry_name(self->entry)); } + +PyDoc_STRVAR(TreeEntry_oid__doc__, "Object id."); + PyObject * -TreeEntry_get_oid(TreeEntry *self) +TreeEntry_oid__get__(TreeEntry *self) { const git_oid *oid; @@ -68,12 +77,20 @@ TreeEntry_get_oid(TreeEntry *self) return git_oid_to_python(oid->id); } + +PyDoc_STRVAR(TreeEntry_hex__doc__, "Hex oid."); + PyObject * -TreeEntry_get_hex(TreeEntry *self) +TreeEntry_hex__get__(TreeEntry *self) { return git_oid_to_py_str(git_tree_entry_id(self->entry)); } + +PyDoc_STRVAR(TreeEntry_to_object__doc__, + "to_object() -> Object\n\n" + "Look up the corresponding object in the repo."); + PyObject * TreeEntry_to_object(TreeEntry *self) { @@ -86,19 +103,21 @@ TreeEntry_to_object(TreeEntry *self) } PyGetSetDef TreeEntry_getseters[] = { - {"filemode", (getter)TreeEntry_get_filemode, NULL, "filemode", NULL}, - {"name", (getter)TreeEntry_get_name, NULL, "name", NULL}, - {"oid", (getter)TreeEntry_get_oid, NULL, "object id", NULL}, - {"hex", (getter)TreeEntry_get_hex, NULL, "hex oid", NULL}, + GETTER(TreeEntry, filemode), + GETTER(TreeEntry, name), + GETTER(TreeEntry, oid), + GETTER(TreeEntry, hex), {NULL} }; PyMethodDef TreeEntry_methods[] = { - {"to_object", (PyCFunction)TreeEntry_to_object, METH_NOARGS, - "Look up the corresponding object in the repo."}, - {NULL, NULL, 0, NULL} + METHOD(TreeEntry, to_object, METH_NOARGS), + {NULL} }; + +PyDoc_STRVAR(TreeEntry__doc__, "TreeEntry objects."); + PyTypeObject TreeEntryType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.TreeEntry", /* tp_name */ @@ -120,7 +139,7 @@ PyTypeObject TreeEntryType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "TreeEntry objects", /* tp_doc */ + TreeEntry__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -266,8 +285,21 @@ Tree_getitem(Tree *self, PyObject *value) return wrap_tree_entry(entry, self); } + +PyDoc_STRVAR(Tree_diff__doc__, + "diff([obj, flags]) -> Diff\n\n" + "Get changes between current tree instance with another tree, an index " + "or the working dir.\n" + "\n" + "Arguments:\n" + "\n" + "obj -- if not given compare diff against working dir. " + " Possible valid arguments are instances of Tree or Index.\n" + "\n" + "flags -- "); + PyObject * -Tree_diff_tree(Tree *self, PyObject *args) +Tree_diff(Tree *self, PyObject *args) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; @@ -335,17 +367,13 @@ PyMappingMethods Tree_as_mapping = { }; PyMethodDef Tree_methods[] = { - { - "diff", (PyCFunction)Tree_diff_tree, METH_VARARGS, - "Get changes between current tree instance with another tree, an " - "index or the working dir.\n\n" - "@param obj : if not given compare diff against working dir. " - "Possible valid arguments are instances of Tree or Index.\n" - "@returns Diff instance" - }, + METHOD(Tree, diff, METH_VARARGS), {NULL} }; + +PyDoc_STRVAR(Tree__doc__, "Tree objects."); + PyTypeObject TreeType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Tree", /* tp_name */ @@ -367,7 +395,7 @@ PyTypeObject TreeType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Tree objects", /* tp_doc */ + Tree__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ From 5343d28ca50d89591ceb3f82d097703246b57421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 4 Feb 2013 12:51:28 +0100 Subject: [PATCH 0351/2237] Flat directory hierarchy: move src/pygit2/ to src/ --- setup.py | 6 ++---- src/{pygit2 => }/blob.c | 0 src/{pygit2 => }/commit.c | 0 src/{pygit2 => }/config.c | 0 src/{pygit2 => }/diff.c | 0 src/{pygit2 => }/error.c | 0 src/{pygit2 => }/index.c | 0 src/{pygit2 => }/object.c | 0 src/{pygit2 => }/oid.c | 0 src/{pygit2 => }/reference.c | 0 src/{pygit2 => }/repository.c | 0 src/{pygit2 => }/signature.c | 0 src/{pygit2 => }/tag.c | 0 src/{pygit2 => }/tree.c | 0 src/{pygit2 => }/treebuilder.c | 0 src/{pygit2 => }/utils.c | 0 src/{pygit2 => }/walker.c | 0 17 files changed, 2 insertions(+), 4 deletions(-) rename src/{pygit2 => }/blob.c (100%) rename src/{pygit2 => }/commit.c (100%) rename src/{pygit2 => }/config.c (100%) rename src/{pygit2 => }/diff.c (100%) rename src/{pygit2 => }/error.c (100%) rename src/{pygit2 => }/index.c (100%) rename src/{pygit2 => }/object.c (100%) rename src/{pygit2 => }/oid.c (100%) rename src/{pygit2 => }/reference.c (100%) rename src/{pygit2 => }/repository.c (100%) rename src/{pygit2 => }/signature.c (100%) rename src/{pygit2 => }/tag.c (100%) rename src/{pygit2 => }/tree.c (100%) rename src/{pygit2 => }/treebuilder.c (100%) rename src/{pygit2 => }/utils.c (100%) rename src/{pygit2 => }/walker.c (100%) diff --git a/setup.py b/setup.py index 9f2d518b0..b4daa38d4 100644 --- a/setup.py +++ b/setup.py @@ -57,10 +57,8 @@ libgit2_bin = os.path.join(libgit2_path, 'bin') libgit2_include = os.path.join(libgit2_path, 'include') libgit2_lib = os.getenv('LIBGIT2_LIB', os.path.join(libgit2_path, 'lib')) -pygit2_exts = [os.path.join('src', 'pygit2.c')] + [ - os.path.join('src', 'pygit2', entry) - for entry in os.listdir('src/pygit2') - if entry.endswith('.c')] +pygit2_exts = [ os.path.join('src', name) for name in os.listdir('src') + if name.endswith('.c') ] class TestCommand(Command): """Command for running unittests without install.""" diff --git a/src/pygit2/blob.c b/src/blob.c similarity index 100% rename from src/pygit2/blob.c rename to src/blob.c diff --git a/src/pygit2/commit.c b/src/commit.c similarity index 100% rename from src/pygit2/commit.c rename to src/commit.c diff --git a/src/pygit2/config.c b/src/config.c similarity index 100% rename from src/pygit2/config.c rename to src/config.c diff --git a/src/pygit2/diff.c b/src/diff.c similarity index 100% rename from src/pygit2/diff.c rename to src/diff.c diff --git a/src/pygit2/error.c b/src/error.c similarity index 100% rename from src/pygit2/error.c rename to src/error.c diff --git a/src/pygit2/index.c b/src/index.c similarity index 100% rename from src/pygit2/index.c rename to src/index.c diff --git a/src/pygit2/object.c b/src/object.c similarity index 100% rename from src/pygit2/object.c rename to src/object.c diff --git a/src/pygit2/oid.c b/src/oid.c similarity index 100% rename from src/pygit2/oid.c rename to src/oid.c diff --git a/src/pygit2/reference.c b/src/reference.c similarity index 100% rename from src/pygit2/reference.c rename to src/reference.c diff --git a/src/pygit2/repository.c b/src/repository.c similarity index 100% rename from src/pygit2/repository.c rename to src/repository.c diff --git a/src/pygit2/signature.c b/src/signature.c similarity index 100% rename from src/pygit2/signature.c rename to src/signature.c diff --git a/src/pygit2/tag.c b/src/tag.c similarity index 100% rename from src/pygit2/tag.c rename to src/tag.c diff --git a/src/pygit2/tree.c b/src/tree.c similarity index 100% rename from src/pygit2/tree.c rename to src/tree.c diff --git a/src/pygit2/treebuilder.c b/src/treebuilder.c similarity index 100% rename from src/pygit2/treebuilder.c rename to src/treebuilder.c diff --git a/src/pygit2/utils.c b/src/utils.c similarity index 100% rename from src/pygit2/utils.c rename to src/utils.c diff --git a/src/pygit2/walker.c b/src/walker.c similarity index 100% rename from src/pygit2/walker.c rename to src/walker.c From 7d3ba87cea20588526a505cb644ce657bc239b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 4 Feb 2013 19:01:14 +0100 Subject: [PATCH 0352/2237] Finish deploying PyDoc_STRVAR --- include/pygit2/types.h | 2 +- include/pygit2/utils.h | 17 +++- src/blob.c | 20 +++-- src/config.c | 92 ++++++++++++------- src/diff.c | 72 +++++++++------ src/index.c | 56 ++++++------ src/reference.c | 195 ++++++++++++++++++++++++----------------- src/tag.c | 42 ++++++--- src/tree.c | 59 +++++++------ src/treebuilder.c | 39 ++++++--- src/walker.c | 38 +++++--- 11 files changed, 398 insertions(+), 234 deletions(-) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index e4e4f5d65..97f83cfd4 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -116,7 +116,7 @@ typedef struct { PyObject *oid_old; PyObject *oid_new; PyObject *committer; - char *msg; + char *message; } RefLogEntry; typedef struct { diff --git a/include/pygit2/utils.h b/include/pygit2/utils.h index 903966aeb..d6ffc81e2 100644 --- a/include/pygit2/utils.h +++ b/include/pygit2/utils.h @@ -101,8 +101,21 @@ char * py_str_to_c_str(PyObject *value, const char *encoding); {#name, (PyCFunction) type ## _ ## name, args, type ## _ ## name ## __doc__} #define GETTER(type, attr)\ - {#attr, (getter) type ## _ ## attr ## __get__, NULL,\ - type ## _ ## attr ## __doc__, NULL} + { #attr,\ + (getter) type ## _ ## attr ## __get__,\ + NULL,\ + type ## _ ## attr ## __doc__,\ + NULL} + +#define GETSET(type, attr)\ + { #attr,\ + (getter) type ## _ ## attr ## __get__,\ + (setter) type ## _ ## attr ## __set__,\ + type ## _ ## attr ## __doc__,\ + NULL} + +#define MEMBER(type, attr, attr_type, docstr)\ + {#attr, attr_type, offsetof(type, attr), 0, PyDoc_STR(docstr)} #endif diff --git a/src/blob.c b/src/blob.c index 9ec60ddd1..bac818432 100644 --- a/src/blob.c +++ b/src/blob.c @@ -31,22 +31,31 @@ #include #include + +PyDoc_STRVAR(Blob_size__doc__, "Size."); + PyObject * -Blob_get_size(Blob *self) +Blob_size__get__(Blob *self) { return PyInt_FromLong(git_blob_rawsize(self->blob)); } +PyDoc_STRVAR(Blob_data__doc__, + "Raw data. This is the same as Blob.read_raw()"); + PyGetSetDef Blob_getseters[] = { - {"data", (getter)Object_read_raw, NULL, "raw data", NULL}, - {"size", (getter)Blob_get_size, NULL, "size", NULL}, + GETTER(Blob, size), + {"data", (getter)Object_read_raw, NULL, Blob_data__doc__, NULL}, {NULL} }; + +PyDoc_STRVAR(Blob__doc__, "Blob objects."); + PyTypeObject BlobType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Blob", /* tp_name */ + "_pygit2.Blob", /* tp_name */ sizeof(Blob), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ @@ -65,7 +74,7 @@ PyTypeObject BlobType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Blob objects", /* tp_doc */ + Blob__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -84,4 +93,3 @@ PyTypeObject BlobType = { 0, /* tp_alloc */ 0, /* tp_new */ }; - diff --git a/src/config.c b/src/config.c index 6a84f6300..847f9f82e 100644 --- a/src/config.c +++ b/src/config.c @@ -98,6 +98,11 @@ Config_open(char *c_path) { return (PyObject *)config; } + +PyDoc_STRVAR(Config_get_global_config__doc__, + "get_global_config() -> Config\n\n" + "Return an object representing the global configuration file."); + PyObject * Config_get_global_config(void) { @@ -116,6 +121,11 @@ Config_get_global_config(void) return Config_open(path); } + +PyDoc_STRVAR(Config_get_system_config__doc__, + "get_system_config() -> Config\n\n" + "Return an object representing the system configuration file."); + PyObject * Config_get_system_config(void) { @@ -254,6 +264,15 @@ Config_foreach_callback_wrapper(const git_config_entry *entry, void *c_payload) return c_result; } + +PyDoc_STRVAR(Config_foreach__doc__, + "foreach(callback[, payload]) -> int\n\n" + "Perform an operation on each config variable.\n\n" + "The callback must be of type Callable and receives the normalized name " + "and value of each variable in the config backend, and an optional payload " + "passed to this method. As soon as one of the callbacks returns an integer " + "other than 0, this function returns that value."); + PyObject * Config_foreach(Config *self, PyObject *args) { @@ -261,13 +280,12 @@ Config_foreach(Config *self, PyObject *args) PyObject *py_callback; PyObject *py_payload; - if (!PyArg_ParseTuple(args, "O|O", &py_callback, &py_payload)) return NULL; - if (!PyCallable_Check(py_callback)) { - PyErr_SetString(PyExc_TypeError,"Argument 'callback' is not callable"); + PyErr_SetString(PyExc_TypeError, + "Argument 'callback' is not callable"); return NULL; } @@ -277,6 +295,11 @@ Config_foreach(Config *self, PyObject *args) return PyInt_FromLong((long)ret); } + +PyDoc_STRVAR(Config_add_file__doc__, + "add_file(path, level=0, force=0)\n\n" + "Add a config file instance to an existing config."); + PyObject * Config_add_file(Config *self, PyObject *args, PyObject *kwds) { @@ -286,9 +309,8 @@ Config_add_file(Config *self, PyObject *args, PyObject *kwds) unsigned int level = 0; int force = 0; - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "s|Ii", keywords, - &path, &level, &force)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|Ii", keywords, + &path, &level, &force)) return NULL; err = git_config_add_file_ondisk(self->config, path, level, force); @@ -300,6 +322,13 @@ Config_add_file(Config *self, PyObject *args, PyObject *kwds) Py_RETURN_NONE; } + +PyDoc_STRVAR(Config_get_multivar__doc__, + "get_multivar(name[, regex]) -> [str, ...]\n\n" + "Get each value of a multivar ''name'' as a list. The optional ''regex'' " + "parameter is expected to be a regular expression to filter the variables " + " we're interested in."); + int Config_get_multivar_fn_wrapper(const git_config_entry *value, void *data) { @@ -325,8 +354,10 @@ Config_get_multivar(Config *self, PyObject *args) if (!PyArg_ParseTuple(args, "s|s", &name, ®ex)) return NULL; - if ((err = git_config_get_multivar(self->config, name, regex, - Config_get_multivar_fn_wrapper, (void *)list)) < 0) { + err = git_config_get_multivar(self->config, name, regex, + Config_get_multivar_fn_wrapper, + (void *)list); + if (err < 0) { if (err == GIT_ENOTFOUND) Error_set(err); else @@ -337,6 +368,12 @@ Config_get_multivar(Config *self, PyObject *args) return list; } + +PyDoc_STRVAR(Config_set_multivar__doc__, + "set_multivar(name, regex, value)\n\n" + "Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression " + "to indicate which values to replace"); + PyObject * Config_set_multivar(Config *self, PyObject *args) { @@ -347,7 +384,9 @@ Config_set_multivar(Config *self, PyObject *args) if (!PyArg_ParseTuple(args, "sss", &name, ®ex, &value)) return NULL; - if ((err = git_config_set_multivar(self->config, name, regex, value)) < 0) { + + err = git_config_set_multivar(self->config, name, regex, value); + if (err < 0) { if (err == GIT_ENOTFOUND) Error_set(err); else @@ -359,27 +398,12 @@ Config_set_multivar(Config *self, PyObject *args) } PyMethodDef Config_methods[] = { - {"get_system_config", (PyCFunction)Config_get_system_config, - METH_NOARGS | METH_STATIC, - "Return an object representing the system configuration file."}, - {"get_global_config", (PyCFunction)Config_get_global_config, - METH_NOARGS | METH_STATIC, - "Return an object representing the global configuration file."}, - {"foreach", (PyCFunction)Config_foreach, METH_VARARGS, - "Perform an operation on each config variable.\n\n" - "The callback must be of type Callable and receives the normalized name " - "and value of each variable in the config backend, and an optional " - "payload passed to this method. As soon as one of the callbacks returns " - "an integer other than 0, this function returns that value."}, - {"add_file", (PyCFunction)Config_add_file, METH_VARARGS | METH_KEYWORDS, - "Add a config file instance to an existing config."}, - {"get_multivar", (PyCFunction)Config_get_multivar, METH_VARARGS, - "Get each value of a multivar ''name'' as a list. The optional ''regex'' " - "parameter is expected to be a regular expression to filter the which " - "variables we're interested in."}, - {"set_multivar", (PyCFunction)Config_set_multivar, METH_VARARGS, - "Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression " - "to indicate which values to replace"}, + METHOD(Config, get_system_config, METH_NOARGS | METH_STATIC), + METHOD(Config, get_global_config, METH_NOARGS | METH_STATIC), + METHOD(Config, foreach, METH_VARARGS), + METHOD(Config, add_file, METH_VARARGS | METH_KEYWORDS), + METHOD(Config, get_multivar, METH_VARARGS), + METHOD(Config, set_multivar, METH_VARARGS), {NULL} }; @@ -400,6 +424,9 @@ PyMappingMethods Config_as_mapping = { (objobjargproc)Config_setitem, /* mp_ass_subscript */ }; + +PyDoc_STRVAR(Config__doc__, "Configuration management."); + PyTypeObject ConfigType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Config", /* tp_name */ @@ -420,9 +447,8 @@ PyTypeObject ConfigType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | - Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "Configuration management", /* tp_doc */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Config__doc__, /* tp_doc */ (traverseproc)Config_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ diff --git a/src/diff.c b/src/diff.c index e31a08bd7..94eb6a9c1 100644 --- a/src/diff.c +++ b/src/diff.c @@ -163,8 +163,8 @@ static int diff_hunk_cb( return 0; }; -static int diff_file_cb(const git_diff_delta *delta, float progress, -void *cb_data) +static int +diff_file_cb(const git_diff_delta *delta, float progress, void *cb_data) { PyObject *files, *file; @@ -193,8 +193,11 @@ void *cb_data) return 0; } + +PyDoc_STRVAR(Diff_changes__doc__, "Raw changes."); + PyObject * -Diff_changes(Diff *self) +Diff_changes__get__(Diff *self) { if (self->diff_changes == NULL) { @@ -226,8 +229,11 @@ static int diff_print_cb( return 0; } + +PyDoc_STRVAR(Diff_patch__doc__, "Patch."); + PyObject * -Diff_patch(Diff *self) +Diff_patch__get__(Diff *self) { PyObject *patch = PyBytes_FromString(""); @@ -280,21 +286,24 @@ Hunk_dealloc(Hunk *self) } PyMemberDef Hunk_members[] = { - {"header", T_STRING, offsetof(Hunk, header), 0, "header"}, - {"old_start", T_INT, offsetof(Hunk, old_start), 0, "old start"}, - {"old_lines", T_INT, offsetof(Hunk, old_lines), 0, "old lines"}, - {"old_mode", T_INT, offsetof(Hunk, old_mode), 0, "old mode"}, - {"old_file", T_STRING, offsetof(Hunk, old_file), 0, "old file"}, - {"old_oid", T_OBJECT, offsetof(Hunk, old_oid), 0, "old_oid"}, - {"new_start", T_INT, offsetof(Hunk, new_start), 0, "new start"}, - {"new_lines", T_INT, offsetof(Hunk, new_lines), 0, "new lines"}, - {"new_mode", T_INT, offsetof(Hunk, new_mode), 0, "new mode"}, - {"new_file", T_STRING, offsetof(Hunk, new_file), 0, "old file"}, - {"new_oid", T_OBJECT, offsetof(Hunk, new_oid), 0, "new_oid"}, - {"data", T_OBJECT, offsetof(Hunk, data), 0, "data"}, + MEMBER(Hunk, header, T_STRING, "Header."), + MEMBER(Hunk, old_start, T_INT, "Old start."), + MEMBER(Hunk, old_lines, T_INT, "Old lines."), + MEMBER(Hunk, old_mode, T_INT, "Old mode."), + MEMBER(Hunk, old_file, T_STRING, "Old file."), + MEMBER(Hunk, old_oid, T_OBJECT, "Old oid."), + MEMBER(Hunk, new_start, T_INT, "New start."), + MEMBER(Hunk, new_lines, T_INT, "New lines."), + MEMBER(Hunk, new_mode, T_INT, "New mode."), + MEMBER(Hunk, new_file, T_STRING, "New file."), + MEMBER(Hunk, new_oid, T_OBJECT, "New oid."), + MEMBER(Hunk, data, T_OBJECT, "Data."), {NULL} }; + +PyDoc_STRVAR(Hunk__doc__, "Hunk object."); + PyTypeObject HunkType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Hunk", /* tp_name */ @@ -316,7 +325,7 @@ PyTypeObject HunkType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Hunk object", /* tp_doc */ + Hunk__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -336,6 +345,11 @@ PyTypeObject HunkType = { 0, /* tp_new */ }; + +PyDoc_STRVAR(Diff_merge__doc__, + "merge(diff)\n\n" + "Merge one diff into another."); + PyObject * Diff_merge(Diff *self, PyObject *args) { @@ -356,6 +370,11 @@ Diff_merge(Diff *self, PyObject *args) Py_RETURN_NONE; } + +PyDoc_STRVAR(Diff_find_similar__doc__, + "find_similar([flags])\n\n" + "Find renamed files in diff."); + PyObject * Diff_find_similar(Diff *self, PyObject *args) { @@ -384,22 +403,23 @@ Diff_dealloc(Diff *self) } PyGetSetDef Diff_getseters[] = { - {"changes", (getter)Diff_changes, NULL, "raw changes", NULL}, - {"patch", (getter)Diff_patch, NULL, "patch", NULL}, + GETTER(Diff, changes), + GETTER(Diff, patch), {NULL} }; static PyMethodDef Diff_methods[] = { - {"merge", (PyCFunction)Diff_merge, METH_VARARGS, - "Merge one diff into another."}, - {"find_similar", (PyCFunction)Diff_find_similar, METH_VARARGS, - "Find renamed files in diff."}, - {NULL, NULL, 0, NULL} + METHOD(Diff, merge, METH_VARARGS), + METHOD(Diff, find_similar, METH_VARARGS), + {NULL} }; + +PyDoc_STRVAR(Diff__doc__, "Diff objects."); + PyTypeObject DiffType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Diff", /* tp_name */ + "_pygit2.Diff", /* tp_name */ sizeof(Diff), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Diff_dealloc, /* tp_dealloc */ @@ -418,7 +438,7 @@ PyTypeObject DiffType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Diff objects", /* tp_doc */ + Diff__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ diff --git a/src/index.c b/src/index.c index afb79f9ba..2c6793bc7 100644 --- a/src/index.c +++ b/src/index.c @@ -513,35 +513,37 @@ IndexIter_iternext(IndexIter *self) return wrap_index_entry(index_entry, self->owner); } + +PyDoc_STRVAR(IndexIter__doc__, "Index iterator."); + PyTypeObject IndexIterType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.IndexIter", /* tp_name */ - sizeof(IndexIter), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)IndexIter_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)IndexIter_iternext, /* tp_iternext */ + "_pygit2.IndexIter", /* tp_name */ + sizeof(IndexIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)IndexIter_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + IndexIter__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)IndexIter_iternext, /* tp_iternext */ }; void diff --git a/src/reference.c b/src/reference.c index be572b40f..cccbfdb52 100644 --- a/src/reference.c +++ b/src/reference.c @@ -51,17 +51,16 @@ void RefLogIter_dealloc(RefLogIter *self) PyObject* RefLogIter_iternext(PyObject *self) { RefLogIter *p = (RefLogIter *) self; + const git_reflog_entry *entry; + char oid_old[40], oid_new[40]; if (p->i < p->size) { - char oid_old[40], oid_new[40]; RefLogEntry *py_entry; git_signature *signature; - const git_reflog_entry *entry = git_reflog_entry_byindex(p->reflog, p->i); - - py_entry = (RefLogEntry*) PyType_GenericNew( - &RefLogEntryType, NULL, NULL - ); + entry = git_reflog_entry_byindex(p->reflog, p->i); + py_entry = (RefLogEntry*) PyType_GenericNew(&RefLogEntryType, NULL, + NULL); git_oid_fmt(oid_old, git_reflog_entry_id_old(entry)); git_oid_fmt(oid_new, git_reflog_entry_id_new(entry)); @@ -69,7 +68,7 @@ PyObject* RefLogIter_iternext(PyObject *self) py_entry->oid_new = PyUnicode_FromStringAndSize(oid_new, 40); py_entry->oid_old = PyUnicode_FromStringAndSize(oid_old, 40); - py_entry->msg = strdup(git_reflog_entry_message(entry)); + py_entry->message = strdup(git_reflog_entry_message(entry)); signature = git_signature_dup( git_reflog_entry_committer(entry) @@ -90,37 +89,37 @@ PyObject* RefLogIter_iternext(PyObject *self) return NULL; } + +PyDoc_STRVAR(RefLogIterType__doc__, "Internal reflog iterator object."); + PyTypeObject RefLogIterType = { PyVarObject_HEAD_INIT(NULL, 0) - "_libgit2.RefLogIter", /*tp_name*/ - sizeof(RefLogIter), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)RefLogIter_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 */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - /* tp_flags: Py_TPFLAGS_HAVE_ITER tells python to - use tp_iter and tp_iternext fields. */ - "Internal reflog iterator object.", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter: __iter__() method */ - (iternextfunc) RefLogIter_iternext /* tp_iternext: next() method */ + "_libgit2.RefLogIter", /* tp_name */ + sizeof(RefLogIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)RefLogIter_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + RefLogIterType__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)RefLogIter_iternext /* tp_iternext */ }; void @@ -130,6 +129,11 @@ Reference_dealloc(Reference *self) PyObject_Del(self); } + +PyDoc_STRVAR(Reference_delete__doc__, + "delete()\n\n" + "Delete this reference. It will no longer be valid!"); + PyObject * Reference_delete(Reference *self, PyObject *args) { @@ -143,9 +147,14 @@ Reference_delete(Reference *self, PyObject *args) return Error_set(err); self->reference = NULL; /* Invalidate the pointer */ - Py_RETURN_NONE; /* Return None */ + Py_RETURN_NONE; } + +PyDoc_STRVAR(Reference_rename__doc__, + "rename(new_name)\n\n" + "Rename the reference."); + PyObject * Reference_rename(Reference *self, PyObject *py_name) { @@ -165,9 +174,14 @@ Reference_rename(Reference *self, PyObject *py_name) if (err < 0) return Error_set(err); - Py_RETURN_NONE; /* Return None */ + Py_RETURN_NONE; } + +PyDoc_STRVAR(Reference_reload__doc__, + "reload()\n\n" + "Reload the reference from the file-system."); + PyObject * Reference_reload(Reference *self) { @@ -185,6 +199,10 @@ Reference_reload(Reference *self) } +PyDoc_STRVAR(Reference_resolve__doc__, + "resolve() -> Reference\n\n" + "Resolve a symbolic reference and return a direct reference."); + PyObject * Reference_resolve(Reference *self, PyObject *args) { @@ -212,8 +230,11 @@ Reference_resolve(Reference *self, PyObject *args) return wrap_reference(c_reference); } + +PyDoc_STRVAR(Reference_target__doc__, "Target."); + PyObject * -Reference_get_target(Reference *self) +Reference_target__get__(Reference *self) { const char * c_name; @@ -235,7 +256,7 @@ Reference_get_target(Reference *self) } int -Reference_set_target(Reference *self, PyObject *py_name) +Reference_target__set__(Reference *self, PyObject *py_name) { char *c_name; int err; @@ -258,15 +279,21 @@ Reference_set_target(Reference *self, PyObject *py_name) return 0; } + +PyDoc_STRVAR(Reference_name__doc__, "The full name of a reference."); + PyObject * -Reference_get_name(Reference *self) +Reference_name__get__(Reference *self) { CHECK_REFERENCE(self); return to_path(git_reference_name(self->reference)); } + +PyDoc_STRVAR(Reference_oid__doc__, "Object id."); + PyObject * -Reference_get_oid(Reference *self) +Reference_oid__get__(Reference *self) { const git_oid *oid; @@ -286,7 +313,7 @@ Reference_get_oid(Reference *self) } int -Reference_set_oid(Reference *self, PyObject *py_hex) +Reference_oid__set__(Reference *self, PyObject *py_hex) { git_oid oid; int err; @@ -294,7 +321,8 @@ Reference_set_oid(Reference *self, PyObject *py_hex) CHECK_REFERENCE_INT(self); /* Get the oid */ - err = py_str_to_git_oid_expand(git_reference_owner(self->reference), py_hex, &oid); + err = py_str_to_git_oid_expand(git_reference_owner(self->reference), + py_hex, &oid); if (err < 0) { Error_set(err); return -1; @@ -310,8 +338,11 @@ Reference_set_oid(Reference *self, PyObject *py_hex) return 0; } + +PyDoc_STRVAR(Reference_hex__doc__, "Hex oid."); + PyObject * -Reference_get_hex(Reference *self) +Reference_hex__get__(Reference *self) { const git_oid *oid; @@ -330,8 +361,12 @@ Reference_get_hex(Reference *self) return git_oid_to_py_str(oid); } + +PyDoc_STRVAR(Reference_type__doc__, + "Type (GIT_REF_OID, GIT_REF_SYMBOLIC or GIT_REF_PACKED)."); + PyObject * -Reference_get_type(Reference *self) +Reference_type__get__(Reference *self) { git_ref_t c_type; @@ -340,6 +375,11 @@ Reference_get_type(Reference *self) return PyInt_FromLong(c_type); } + +PyDoc_STRVAR(Reference_log__doc__, + "log() -> RefLogIter\n\n" + "Retrieves the current reference log."); + PyObject * Reference_log(Reference *self) { @@ -365,7 +405,7 @@ RefLogEntry_init(RefLogEntry *self, PyObject *args, PyObject *kwds) { self->oid_old = Py_None; self->oid_new = Py_None; - self->msg = ""; + self->message = ""; self->committer = Py_None; return 0; @@ -378,24 +418,27 @@ RefLogEntry_dealloc(RefLogEntry *self) Py_XDECREF(self->oid_old); Py_XDECREF(self->oid_new); Py_XDECREF(self->committer); - free(self->msg); + free(self->message); PyObject_Del(self); } PyMemberDef RefLogEntry_members[] = { - {"oid_new", T_OBJECT, offsetof(RefLogEntry, oid_new), 0, "new oid"}, - {"oid_old", T_OBJECT, offsetof(RefLogEntry, oid_old), 0, "old oid"}, - {"message", T_STRING, offsetof(RefLogEntry, msg), 0, "message"}, - {"committer", T_OBJECT, offsetof(RefLogEntry, committer), 0, "committer"}, + MEMBER(RefLogEntry, oid_new, T_OBJECT, "New oid."), + MEMBER(RefLogEntry, oid_old, T_OBJECT, "Old oid."), + MEMBER(RefLogEntry, message, T_STRING, "Message."), + MEMBER(RefLogEntry, committer, T_OBJECT, "Committer."), {NULL} }; + +PyDoc_STRVAR(RefLogEntry__doc__, "Reference log object."); + PyTypeObject RefLogEntryType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.RefLogEntry", /* tp_name */ - sizeof(RefLogEntry), /* tp_basicsize */ + "_pygit2.RefLogEntry", /* tp_name */ + sizeof(RefLogEntry), /* tp_basicsize */ 0, /* tp_itemsize */ - (destructor)RefLogEntry_dealloc, /* tp_dealloc */ + (destructor)RefLogEntry_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -411,7 +454,7 @@ PyTypeObject RefLogEntryType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ - "ReferenceLog object", /* tp_doc */ + RefLogEntry__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -419,48 +462,42 @@ PyTypeObject RefLogEntryType = { 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ - RefLogEntry_members, /* tp_members */ + RefLogEntry_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)RefLogEntry_init, /* tp_init */ + (initproc)RefLogEntry_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyMethodDef Reference_methods[] = { - {"delete", (PyCFunction)Reference_delete, METH_NOARGS, - "Delete this reference. It will no longer be valid!"}, - {"rename", (PyCFunction)Reference_rename, METH_O, - "Rename the reference."}, - {"reload", (PyCFunction)Reference_reload, METH_NOARGS, - "Reload the reference from the file-system."}, - {"resolve", (PyCFunction)Reference_resolve, METH_NOARGS, - "Resolve a symbolic reference and return a direct reference."}, - {"log", (PyCFunction)Reference_log, METH_NOARGS, - "Retrieves the current reference log."}, + METHOD(Reference, delete, METH_NOARGS), + METHOD(Reference, rename, METH_O), + METHOD(Reference, reload, METH_NOARGS), + METHOD(Reference, resolve, METH_NOARGS), + METHOD(Reference, log, METH_NOARGS), {NULL} }; PyGetSetDef Reference_getseters[] = { - {"name", (getter)Reference_get_name, NULL, - "The full name of a reference.", NULL}, - {"oid", (getter)Reference_get_oid, (setter)Reference_set_oid, "object id", - NULL}, - {"hex", (getter)Reference_get_hex, NULL, "hex oid", NULL}, - {"target", (getter)Reference_get_target, (setter)Reference_set_target, - "target", NULL}, - {"type", (getter)Reference_get_type, NULL, - "type (GIT_REF_OID, GIT_REF_SYMBOLIC or GIT_REF_PACKED).", NULL}, + GETTER(Reference, name), + GETSET(Reference, oid), + GETTER(Reference, hex), + GETSET(Reference, target), + GETTER(Reference, type), {NULL} }; + +PyDoc_STRVAR(Reference__doc__, "Reference."); + PyTypeObject ReferenceType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Reference", /* tp_name */ + "_pygit2.Reference", /* tp_name */ sizeof(Reference), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Reference_dealloc, /* tp_dealloc */ @@ -479,7 +516,7 @@ PyTypeObject ReferenceType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ - "Reference", /* tp_doc */ + Reference__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ diff --git a/src/tag.c b/src/tag.c index d4745d21c..5651c3b0c 100644 --- a/src/tag.c +++ b/src/tag.c @@ -34,8 +34,11 @@ #include #include + +PyDoc_STRVAR(Tag_target__doc__, "Tagged object."); + PyObject * -Tag_get_target(Tag *self) +Tag_target__get__(Tag *self) { const git_oid *oid; @@ -43,8 +46,11 @@ Tag_get_target(Tag *self) return git_oid_to_python(oid->id); } + +PyDoc_STRVAR(Tag_name__doc__, "Tag name."); + PyObject * -Tag_get_name(Tag *self) +Tag_name__get__(Tag *self) { const char *name; name = git_tag_name(self->tag); @@ -53,8 +59,11 @@ Tag_get_name(Tag *self) return to_unicode(name, "utf-8", "strict"); } + +PyDoc_STRVAR(Tag_tagger__doc__, "Tagger."); + PyObject * -Tag_get_tagger(Tag *self) +Tag_tagger__get__(Tag *self) { const git_signature *signature = git_tag_tagger(self->tag); if (!signature) @@ -63,8 +72,11 @@ Tag_get_tagger(Tag *self) return build_signature((Object*)self, signature, "utf-8"); } + +PyDoc_STRVAR(Tag_message__doc__, "Tag message."); + PyObject * -Tag_get_message(Tag *self) +Tag_message__get__(Tag *self) { const char *message; message = git_tag_message(self->tag); @@ -73,24 +85,30 @@ Tag_get_message(Tag *self) return to_unicode(message, "utf-8", "strict"); } + +PyDoc_STRVAR(Tag__message__doc__, "Tag message (bytes)."); + PyObject * -Tag_get_raw_message(Tag *self) +Tag__message__get__(Tag *self) { return PyString_FromString(git_tag_message(self->tag)); } PyGetSetDef Tag_getseters[] = { - {"target", (getter)Tag_get_target, NULL, "tagged object", NULL}, - {"name", (getter)Tag_get_name, NULL, "tag name", NULL}, - {"tagger", (getter)Tag_get_tagger, NULL, "tagger", NULL}, - {"message", (getter)Tag_get_message, NULL, "tag message", NULL}, - {"_message", (getter)Tag_get_raw_message, NULL, "tag message (bytes)", NULL}, + GETTER(Tag, target), + GETTER(Tag, name), + GETTER(Tag, tagger), + GETTER(Tag, message), + GETTER(Tag, _message), {NULL} }; + +PyDoc_STRVAR(Tag__doc__, "Tag objects."); + PyTypeObject TagType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Tag", /* tp_name */ + "_pygit2.Tag", /* tp_name */ sizeof(Tag), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ @@ -109,7 +127,7 @@ PyTypeObject TagType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Tag objects", /* tp_doc */ + Tag__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ diff --git a/src/tree.c b/src/tree.c index 195bd6953..feb9a1ab6 100644 --- a/src/tree.c +++ b/src/tree.c @@ -433,36 +433,39 @@ TreeIter_iternext(TreeIter *self) return NULL; self->i += 1; - return (TreeEntry*)wrap_tree_entry(git_tree_entry_dup(tree_entry), self->owner); + return (TreeEntry*)wrap_tree_entry(git_tree_entry_dup(tree_entry), + self->owner); } + +PyDoc_STRVAR(TreeIter__doc__, "Tree iterator."); + PyTypeObject TreeIterType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.TreeIter", /* tp_name */ - sizeof(TreeIter), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)TreeIter_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)TreeIter_iternext, /* tp_iternext */ + "_pygit2.TreeIter", /* tp_name */ + sizeof(TreeIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)TreeIter_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + TreeIter__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)TreeIter_iternext, /* tp_iternext */ }; diff --git a/src/treebuilder.c b/src/treebuilder.c index 0d8fdac94..c1acb9a2c 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -42,6 +42,11 @@ TreeBuilder_dealloc(TreeBuilder *self) PyObject_Del(self); } + +PyDoc_STRVAR(TreeBuilder_insert__doc__, + "insert(name, oid, attr)\n\n" + "Insert or replace an entry in the treebuilder."); + PyObject * TreeBuilder_insert(TreeBuilder *self, PyObject *args) { @@ -68,6 +73,11 @@ TreeBuilder_insert(TreeBuilder *self, PyObject *args) Py_RETURN_NONE; } + +PyDoc_STRVAR(TreeBuilder_write__doc__, + "write() -> bytes\n\n" + "Write the tree to the given repository."); + PyObject * TreeBuilder_write(TreeBuilder *self) { @@ -81,6 +91,11 @@ TreeBuilder_write(TreeBuilder *self) return git_oid_to_python(&oid); } + +PyDoc_STRVAR(TreeBuilder_remove__doc__, + "remove(name)\n\n" + "Remove an entry from the builder."); + PyObject * TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename) { @@ -98,6 +113,11 @@ TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename) Py_RETURN_NONE; } + +PyDoc_STRVAR(TreeBuilder_clear__doc__, + "clear()\n\n" + "Clear all the entries in the builder."); + PyObject * TreeBuilder_clear(TreeBuilder *self) { @@ -106,17 +126,16 @@ TreeBuilder_clear(TreeBuilder *self) } PyMethodDef TreeBuilder_methods[] = { - {"insert", (PyCFunction)TreeBuilder_insert, METH_VARARGS, - "Insert or replace an entry in the treebuilder"}, - {"write", (PyCFunction)TreeBuilder_write, METH_NOARGS, - "Write the tree to the given repository"}, - {"remove", (PyCFunction)TreeBuilder_remove, METH_O, - "Remove an entry from the builder"}, - {"clear", (PyCFunction)TreeBuilder_clear, METH_NOARGS, - "Clear all the entries in the builder"}, - {NULL, NULL, 0, NULL} + METHOD(TreeBuilder, insert, METH_VARARGS), + METHOD(TreeBuilder, write, METH_NOARGS), + METHOD(TreeBuilder, remove, METH_O), + METHOD(TreeBuilder, clear, METH_NOARGS), + {NULL} }; + +PyDoc_STRVAR(TreeBuilder__doc__, "TreeBuilder objects"); + PyTypeObject TreeBuilderType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.TreeBuilder", /* tp_name */ @@ -138,7 +157,7 @@ PyTypeObject TreeBuilderType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "TreeBuilder objects", /* tp_doc */ + TreeBuilder__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ diff --git a/src/walker.c b/src/walker.c index 3069e9ef5..f7f0b146d 100644 --- a/src/walker.c +++ b/src/walker.c @@ -43,6 +43,11 @@ Walker_dealloc(Walker *self) PyObject_Del(self); } + +PyDoc_STRVAR(Walker_hide__doc__, + "hide(oid)\n\n" + "Mark a commit (and its ancestors) uninteresting for the output."); + PyObject * Walker_hide(Walker *self, PyObject *py_hex) { @@ -50,7 +55,6 @@ Walker_hide(Walker *self, PyObject *py_hex) git_oid oid; err = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); - if (err < 0) return Error_set(err); @@ -61,6 +65,11 @@ Walker_hide(Walker *self, PyObject *py_hex) Py_RETURN_NONE; } + +PyDoc_STRVAR(Walker_push__doc__, + "push(oid)\n\n" + "Mark a commit to start traversal from."); + PyObject * Walker_push(Walker *self, PyObject *py_hex) { @@ -78,6 +87,11 @@ Walker_push(Walker *self, PyObject *py_hex) Py_RETURN_NONE; } + +PyDoc_STRVAR(Walker_sort__doc__, + "sort(mode)\n\n" + "Change the sorting mode (this resets the walker)."); + PyObject * Walker_sort(Walker *self, PyObject *py_sort_mode) { @@ -92,6 +106,11 @@ Walker_sort(Walker *self, PyObject *py_sort_mode) Py_RETURN_NONE; } + +PyDoc_STRVAR(Walker_reset__doc__, + "reset()\n\n" + "Reset the walking machinery for reuse."); + PyObject * Walker_reset(Walker *self) { @@ -132,17 +151,16 @@ Walker_iternext(Walker *self) } PyMethodDef Walker_methods[] = { - {"hide", (PyCFunction)Walker_hide, METH_O, - "Mark a commit (and its ancestors) uninteresting for the output."}, - {"push", (PyCFunction)Walker_push, METH_O, - "Mark a commit to start traversal from."}, - {"reset", (PyCFunction)Walker_reset, METH_NOARGS, - "Reset the walking machinery for reuse."}, - {"sort", (PyCFunction)Walker_sort, METH_O, - "Change the sorting mode (this resets the walker)."}, + METHOD(Walker, hide, METH_O), + METHOD(Walker, push, METH_O), + METHOD(Walker, reset, METH_NOARGS), + METHOD(Walker, sort, METH_O), {NULL} }; + +PyDoc_STRVAR(Walker__doc__, "Revision walker."); + PyTypeObject WalkerType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Walker", /* tp_name */ @@ -164,7 +182,7 @@ PyTypeObject WalkerType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ - "Revision walker", /* tp_doc */ + Walker__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ From 8e40dbd314057b5594a18139c777fe1081079f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 9 Feb 2013 23:46:37 +0100 Subject: [PATCH 0353/2237] Make doc strings look fine from the interpreter Basically add a new-line character since "help(...)" does not wrap lines automatically. --- docs/install.rst | 2 +- src/config.c | 35 +++++++++------ src/diff.c | 6 ++- src/index.c | 37 ++++++++++------ src/object.c | 2 +- src/pygit2.c | 6 ++- src/reference.c | 15 ++++--- src/repository.c | 107 +++++++++++++++++++++++++++------------------- src/signature.c | 2 +- src/tree.c | 26 ++++++----- src/treebuilder.c | 14 +++--- src/walker.c | 12 ++++-- 12 files changed, 159 insertions(+), 105 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index 8728b9374..8bf6bcd25 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -53,7 +53,7 @@ instructions in the libgit2 ``README.md``):: $ cd libgit2/build $ cmake .. $ cmake --build . - $ sudo cmake --build . --target install + $ sudo cmake --build . --target install $ cd ../.. Now, download and install pygit2. You will probably have to set the diff --git a/src/config.c b/src/config.c index 847f9f82e..687acbde7 100644 --- a/src/config.c +++ b/src/config.c @@ -100,7 +100,8 @@ Config_open(char *c_path) { PyDoc_STRVAR(Config_get_global_config__doc__, - "get_global_config() -> Config\n\n" + "get_global_config() -> Config\n" + "\n" "Return an object representing the global configuration file."); PyObject * @@ -123,7 +124,8 @@ Config_get_global_config(void) PyDoc_STRVAR(Config_get_system_config__doc__, - "get_system_config() -> Config\n\n" + "get_system_config() -> Config\n" + "\n" "Return an object representing the system configuration file."); PyObject * @@ -266,11 +268,13 @@ Config_foreach_callback_wrapper(const git_config_entry *entry, void *c_payload) PyDoc_STRVAR(Config_foreach__doc__, - "foreach(callback[, payload]) -> int\n\n" - "Perform an operation on each config variable.\n\n" - "The callback must be of type Callable and receives the normalized name " - "and value of each variable in the config backend, and an optional payload " - "passed to this method. As soon as one of the callbacks returns an integer " + "foreach(callback[, payload]) -> int\n" + "\n" + "Perform an operation on each config variable.\n" + "\n" + "The callback must be of type Callable and receives the normalized name\n" + "and value of each variable in the config backend, and an optional payload\n" + "passed to this method. As soon as one of the callbacks returns an integer\n" "other than 0, this function returns that value."); PyObject * @@ -297,7 +301,8 @@ Config_foreach(Config *self, PyObject *args) PyDoc_STRVAR(Config_add_file__doc__, - "add_file(path, level=0, force=0)\n\n" + "add_file(path, level=0, force=0)\n" + "\n" "Add a config file instance to an existing config."); PyObject * @@ -324,10 +329,11 @@ Config_add_file(Config *self, PyObject *args, PyObject *kwds) PyDoc_STRVAR(Config_get_multivar__doc__, - "get_multivar(name[, regex]) -> [str, ...]\n\n" - "Get each value of a multivar ''name'' as a list. The optional ''regex'' " - "parameter is expected to be a regular expression to filter the variables " - " we're interested in."); + "get_multivar(name[, regex]) -> [str, ...]\n" + "\n" + "Get each value of a multivar ''name'' as a list. The optional ''regex''\n" + "parameter is expected to be a regular expression to filter the variables\n" + "we're interested in."); int Config_get_multivar_fn_wrapper(const git_config_entry *value, void *data) @@ -370,8 +376,9 @@ Config_get_multivar(Config *self, PyObject *args) PyDoc_STRVAR(Config_set_multivar__doc__, - "set_multivar(name, regex, value)\n\n" - "Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression " + "set_multivar(name, regex, value)\n" + "\n" + "Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression\n" "to indicate which values to replace"); PyObject * diff --git a/src/diff.c b/src/diff.c index 94eb6a9c1..581388e69 100644 --- a/src/diff.c +++ b/src/diff.c @@ -347,7 +347,8 @@ PyTypeObject HunkType = { PyDoc_STRVAR(Diff_merge__doc__, - "merge(diff)\n\n" + "merge(diff)\n" + "\n" "Merge one diff into another."); PyObject * @@ -372,7 +373,8 @@ Diff_merge(Diff *self, PyObject *args) PyDoc_STRVAR(Diff_find_similar__doc__, - "find_similar([flags])\n\n" + "find_similar([flags])\n" + "\n" "Find renamed files in diff."); PyObject * diff --git a/src/index.c b/src/index.c index 2c6793bc7..1a6d3ac34 100644 --- a/src/index.c +++ b/src/index.c @@ -80,7 +80,8 @@ Index_traverse(Index *self, visitproc visit, void *arg) PyDoc_STRVAR(Index_add__doc__, - "add(path)\n\n" + "add(path)\n" + "\n" "Add or update an index entry from a file in disk."); PyObject * @@ -101,7 +102,8 @@ Index_add(Index *self, PyObject *args) PyDoc_STRVAR(Index_clear__doc__, - "clear()\n\n" + "clear()\n" + "\n" "Clear the contents (all the entries) of an index object."); PyObject * @@ -113,9 +115,10 @@ Index_clear(Index *self) PyDoc_STRVAR(Index_diff__doc__, - "diff([tree]) -> Diff\n\n" - "Return a :py:class:`~pygit2.Diff` object with the differences between the " - "index and the working copy. If a :py:class:`~pygit2.Tree` object is " + "diff([tree]) -> Diff\n" + "\n" + "Return a :py:class:`~pygit2.Diff` object with the differences between the\n" + "index and the working copy. If a :py:class:`~pygit2.Tree` object is\n" "passed, return the diferences between the index and the given tree."); PyObject * @@ -163,8 +166,9 @@ Index_diff(Index *self, PyObject *args) PyDoc_STRVAR(Index__find__doc__, - "_find(path) -> integer\n\n" - "Find the first index of any entries which point to given path in the " + "_find(path) -> integer\n" + "\n" + "Find the first index of any entries which point to given path in the\n" "index file."); PyObject * @@ -187,8 +191,9 @@ Index__find(Index *self, PyObject *py_path) PyDoc_STRVAR(Index_read__doc__, - "read()\n\n" - "Update the contents of an existing index object in memory by reading from " + "read()\n" + "\n" + "Update the contents of an existing index object in memory by reading from\n" "the hard disk."); PyObject * @@ -205,8 +210,9 @@ Index_read(Index *self) PyDoc_STRVAR(Index_write__doc__, - "write()\n\n" - "Write an existing index object from memory back to disk using an atomic " + "write()\n" + "\n" + "Write an existing index object from memory back to disk using an atomic\n" "file lock."); PyObject * @@ -334,7 +340,8 @@ Index_getitem(Index *self, PyObject *value) PyDoc_STRVAR(Index_remove__doc__, - "remove(path)\n\n" + "remove(path)\n" + "\n" "Removes an entry from index."); PyObject * @@ -372,7 +379,8 @@ Index_setitem(Index *self, PyObject *key, PyObject *value) PyDoc_STRVAR(Index_read_tree__doc__, - "read_tree(tree)\n\n" + "read_tree(tree)\n" + "\n" "Update the index file from the tree identified by the given oid."); PyObject * @@ -401,7 +409,8 @@ Index_read_tree(Index *self, PyObject *value) PyDoc_STRVAR(Index_write_tree__doc__, - "write_tree() -> str\n\n" + "write_tree() -> str\n" + "\n" "Create a tree object from the index file, return its oid."); PyObject * diff --git a/src/object.c b/src/object.c index 1b64be563..48667e548 100644 --- a/src/object.c +++ b/src/object.c @@ -80,7 +80,7 @@ Object_hex__get__(Object *self) PyDoc_STRVAR(Object_type__doc__, - "One of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_TAG " + "One of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_TAG\n" "constants."); PyObject * diff --git a/src/pygit2.c b/src/pygit2.c index fdadb522f..15d1a3f94 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -60,7 +60,8 @@ extern PyTypeObject SignatureType; PyDoc_STRVAR(init_repository__doc__, - "init_repository(path, bare) -> Repository\n\n" + "init_repository(path, bare) -> Repository\n" + "\n" "Creates a new Git repository in the given path."); PyObject * @@ -93,7 +94,8 @@ init_repository(PyObject *self, PyObject *args) PyDoc_STRVAR(discover_repository__doc__, - "discover_repository(path[, across_fs[, ceiling_dirs]]) -> str\n\n" + "discover_repository(path[, across_fs[, ceiling_dirs]]) -> str\n" + "\n" "Look for a git repository and return its path."); PyObject * diff --git a/src/reference.c b/src/reference.c index cccbfdb52..7695540ab 100644 --- a/src/reference.c +++ b/src/reference.c @@ -131,7 +131,8 @@ Reference_dealloc(Reference *self) PyDoc_STRVAR(Reference_delete__doc__, - "delete()\n\n" + "delete()\n" + "\n" "Delete this reference. It will no longer be valid!"); PyObject * @@ -152,7 +153,8 @@ Reference_delete(Reference *self, PyObject *args) PyDoc_STRVAR(Reference_rename__doc__, - "rename(new_name)\n\n" + "rename(new_name)\n" + "\n" "Rename the reference."); PyObject * @@ -179,7 +181,8 @@ Reference_rename(Reference *self, PyObject *py_name) PyDoc_STRVAR(Reference_reload__doc__, - "reload()\n\n" + "reload()\n" + "\n" "Reload the reference from the file-system."); PyObject * @@ -200,7 +203,8 @@ Reference_reload(Reference *self) PyDoc_STRVAR(Reference_resolve__doc__, - "resolve() -> Reference\n\n" + "resolve() -> Reference\n" + "\n" "Resolve a symbolic reference and return a direct reference."); PyObject * @@ -377,7 +381,8 @@ Reference_type__get__(Reference *self) PyDoc_STRVAR(Reference_log__doc__, - "log() -> RefLogIter\n\n" + "log() -> RefLogIter\n" + "\n" "Retrieves the current reference log."); PyObject * diff --git a/src/repository.c b/src/repository.c index 79c704b0d..f8a5708d6 100644 --- a/src/repository.c +++ b/src/repository.c @@ -208,12 +208,12 @@ Repository_head__get__(Repository *self) err = git_repository_head(&head, self->repo); if(err < 0) { - if(err == GIT_ENOTFOUND) - PyErr_SetString(GitError, "head reference does not exist"); - else - Error_set(err); + if(err == GIT_ENOTFOUND) + PyErr_SetString(GitError, "head reference does not exist"); + else + Error_set(err); - return NULL; + return NULL; } oid = git_reference_target(head); @@ -224,7 +224,7 @@ Repository_head__get__(Repository *self) PyDoc_STRVAR(Repository_head_is_detached__doc__, - "A repository's HEAD is detached when it points directly to a commit " + "A repository's HEAD is detached when it points directly to a commit\n" "instead of a branch."); PyObject * @@ -238,7 +238,7 @@ Repository_head_is_detached__get__(Repository *self) PyDoc_STRVAR(Repository_head_is_orphaned__doc__, - "An orphan branch is one named from HEAD but which doesn't exist in the " + "An orphan branch is one named from HEAD but which doesn't exist in the\n" "refs namespace, because it doesn't have any commit to point to."); PyObject * @@ -292,9 +292,10 @@ Repository_getitem(Repository *self, PyObject *value) PyDoc_STRVAR(Repository_revparse_single__doc__, - "revparse_single(revision) -> Object\n\n" - "Find an object, as specified by a revision string. See " - "`man gitrevisions`, or the documentation for `git rev-parse` for " + "revparse_single(revision) -> Object\n" + "\n" + "Find an object, as specified by a revision string. See\n" + "`man gitrevisions`, or the documentation for `git rev-parse` for\n" "information on the syntax accepted."); PyObject * @@ -348,7 +349,8 @@ Repository_read_raw(git_repository *repo, const git_oid *oid, size_t len) PyDoc_STRVAR(Repository_read__doc__, - "read(oid) -> type, data, size\n\n" + "read(oid) -> type, data, size\n" + "\n" "Read raw object data from the repository."); PyObject * @@ -379,10 +381,11 @@ Repository_read(Repository *self, PyObject *py_hex) PyDoc_STRVAR(Repository_write__doc__, - "write(type, data) -> oid\n\n" - "Write raw object data into the repository. First arg is the object " - "type, the second one a buffer with data. Return the object id (sha) " - "of the created object."); + "write(type, data) -> oid\n" + "\n" + "Write raw object data into the repository. First arg is the object type,\n" + "the second one a buffer with data. Return the object id (sha) of of the\n" + "created object."); PyObject * Repository_write(Repository *self, PyObject *args) @@ -464,8 +467,8 @@ Repository_path__get__(Repository *self, void *closure) PyDoc_STRVAR(Repository_workdir__doc__, - "The normalized path to the working directory of the repository. " - "If the repository is bare, None will be returned."); + "The normalized path to the working directory of the repository. If the\n" + "repository is bare, None will be returned."); PyObject * Repository_workdir__get__(Repository *self, void *closure) @@ -481,9 +484,10 @@ Repository_workdir__get__(Repository *self, void *closure) PyDoc_STRVAR(Repository_config__doc__, - "Get the configuration file for this repository.\n\n" - "If a configuration file has not been set, the default config set for the " - "repository will be returned, including global and system configurations " + "Get the configuration file for this repository.\n" + "\n" + "If a configuration file has not been set, the default config set for the\n" + "repository will be returned, including global and system configurations\n" "(if they are available)."); PyObject * @@ -519,7 +523,8 @@ Repository_config__get__(Repository *self, void *closure) PyDoc_STRVAR(Repository_walk__doc__, - "walk(oid, sort_mode) -> iterator\n\n" + "walk(oid, sort_mode) -> iterator\n" + "\n" "Generator that traverses the history starting from the given commit."); PyObject * @@ -571,7 +576,8 @@ Repository_walk(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_blob__doc__, - "create_blob(data) -> bytes\n\n" + "create_blob(data) -> bytes\n" + "\n" "Create a new blob from memory."); PyObject * @@ -594,7 +600,8 @@ Repository_create_blob(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_blob_fromfile__doc__, - "create_blob_fromfile(path) -> bytes\n\n" + "create_blob_fromfile(path) -> bytes\n" + "\n" "Create a new blob from file."); PyObject * @@ -616,8 +623,8 @@ Repository_create_blob_fromfile(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_commit__doc__, - "create_commit(reference, author, committer, message, tree, parents" - "[, encoding]) -> bytes\n\n" + "create_commit(reference, author, committer, message, tree, parents[, encoding]) -> bytes\n" + "\n" "Create a new commit object, return its SHA."); PyObject * @@ -699,7 +706,8 @@ Repository_create_commit(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_tag__doc__, - "create_tag(name, oid, type, tagger, message) -> bytes\n\n" + "create_tag(name, oid, type, tagger, message) -> bytes\n" + "\n" "Create a new tag object, return its SHA."); PyObject * @@ -736,7 +744,8 @@ Repository_create_tag(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_listall_references__doc__, - "listall_references([flags]) -> (str, ...)\n\n" + "listall_references([flags]) -> (str, ...)\n" + "\n" "Return a tuple with all the references in the repository."); PyObject * @@ -779,7 +788,8 @@ Repository_listall_references(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_lookup_reference__doc__, - "lookup_reference(name) -> Reference\n\n" + "lookup_reference(name) -> Reference\n" + "\n" "Lookup a reference by its name in a repository."); PyObject * @@ -809,23 +819,25 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) PyDoc_STRVAR(Repository_create_reference__doc__, "create_reference(name, source, force=False, symbolic=False) -> Reference\n" "\n" - "Create a new reference \"name\" which points to a object or another " - "reference\n" + "Create a new reference \"name\" which points to a object or another\n" + "reference.\n" "\n" "Keyword arguments:\n" "\n" - "force -- if True references will be overridden, otherwise (the default)" - " an exception is raised.\n" + "force\n" + " If True references will be overridden, otherwise (the default) an\n" + " exception is raised.\n" "\n" - "symbolic -- if True a symbolic reference will be created, then source has" - " to be a valid existing reference name; if False (the default)" - " a normal reference will be created, then source must has to be" - " a valid SHA hash.\n" + "symbolic\n" + " If True a symbolic reference will be created, then source has to be a\n" + " valid existing reference name; if False (the default) a normal\n" + " reference will be created, then source must has to be a valid SHA\n" + " hash.\n" "\n" "Examples::\n" "\n" " repo.create_reference('refs/heads/foo', repo.head.hex)\n" - " repo.create_reference('refs/tags/foo', 'refs/heads/master', symbolic=True)\n"); + " repo.create_reference('refs/tags/foo', 'refs/heads/master', symbolic=True)"); PyObject * Repository_create_reference(Repository *self, PyObject *args, PyObject *kw) @@ -870,8 +882,9 @@ Repository_create_reference(Repository *self, PyObject *args, PyObject *kw) PyDoc_STRVAR(Repository_packall_references__doc__, - "packall_references()\n\n" - "Pack all the loose references in the repository."); + "packall_references()\n" + "\n" + "Pack all the loose references in the repository."); PyObject * Repository_packall_references(Repository *self, PyObject *args) @@ -887,8 +900,9 @@ Repository_packall_references(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_status__doc__, - "status() -> {str: int}\n\n" - "Reads the status of the repository and returns a dictionary with file " + "status() -> {str: int}\n" + "\n" + "Reads the status of the repository and returns a dictionary with file\n" "paths as keys and status flags as values. See pygit2.GIT_STATUS_*."); int @@ -917,7 +931,8 @@ Repository_status(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_status_file__doc__, - "status_file(path) -> int\n\n" + "status_file(path) -> int\n" + "\n" "Returns the status of the given file path."); PyObject * @@ -942,7 +957,8 @@ Repository_status_file(Repository *self, PyObject *value) PyDoc_STRVAR(Repository_TreeBuilder__doc__, - "TreeBuilder([tree]) -> TreeBuilder\n\n" + "TreeBuilder([tree]) -> TreeBuilder\n" + "\n" "Create a TreeBuilder object for this repository."); PyObject * @@ -1048,8 +1064,9 @@ PyMappingMethods Repository_as_mapping = { PyDoc_STRVAR(Repository__doc__, - "Repository(path) -> Repository\n\n" - "Git repository."); + "Repository(path) -> Repository\n" + "\n" + "Git repository."); PyTypeObject RepositoryType = { PyVarObject_HEAD_INIT(NULL, 0) diff --git a/src/signature.c b/src/signature.c index db25968fd..88f2f878d 100644 --- a/src/signature.c +++ b/src/signature.c @@ -151,7 +151,7 @@ Signature_time__get__(Signature *self) } -PyDoc_STRVAR(Signature_offset__doc__, "Offset from UTC in minutes"); +PyDoc_STRVAR(Signature_offset__doc__, "Offset from UTC in minutes."); PyObject * Signature_offset__get__(Signature *self) diff --git a/src/tree.c b/src/tree.c index feb9a1ab6..6514ecdb2 100644 --- a/src/tree.c +++ b/src/tree.c @@ -88,7 +88,8 @@ TreeEntry_hex__get__(TreeEntry *self) PyDoc_STRVAR(TreeEntry_to_object__doc__, - "to_object() -> Object\n\n" + "to_object() -> Object\n" + "\n" "Look up the corresponding object in the repo."); PyObject * @@ -287,16 +288,19 @@ Tree_getitem(Tree *self, PyObject *value) PyDoc_STRVAR(Tree_diff__doc__, - "diff([obj, flags]) -> Diff\n\n" - "Get changes between current tree instance with another tree, an index " - "or the working dir.\n" - "\n" - "Arguments:\n" - "\n" - "obj -- if not given compare diff against working dir. " - " Possible valid arguments are instances of Tree or Index.\n" - "\n" - "flags -- "); + "diff([obj, flags]) -> Diff\n" + "\n" + "Get changes between current tree instance with another tree, an index or\n" + "the working dir.\n" + "\n" + "Arguments:\n" + "\n" + "obj\n" + " If not given compare diff against working dir. Possible valid\n" + " arguments are instances of Tree or Index.\n" + "\n" + "flags\n" + " TODO"); PyObject * Tree_diff(Tree *self, PyObject *args) diff --git a/src/treebuilder.c b/src/treebuilder.c index c1acb9a2c..7c45f42f1 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -44,7 +44,8 @@ TreeBuilder_dealloc(TreeBuilder *self) PyDoc_STRVAR(TreeBuilder_insert__doc__, - "insert(name, oid, attr)\n\n" + "insert(name, oid, attr)\n" + "\n" "Insert or replace an entry in the treebuilder."); PyObject * @@ -75,7 +76,8 @@ TreeBuilder_insert(TreeBuilder *self, PyObject *args) PyDoc_STRVAR(TreeBuilder_write__doc__, - "write() -> bytes\n\n" + "write() -> bytes\n" + "\n" "Write the tree to the given repository."); PyObject * @@ -93,7 +95,8 @@ TreeBuilder_write(TreeBuilder *self) PyDoc_STRVAR(TreeBuilder_remove__doc__, - "remove(name)\n\n" + "remove(name)\n" + "\n" "Remove an entry from the builder."); PyObject * @@ -115,7 +118,8 @@ TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename) PyDoc_STRVAR(TreeBuilder_clear__doc__, - "clear()\n\n" + "clear()\n" + "\n" "Clear all the entries in the builder."); PyObject * @@ -134,7 +138,7 @@ PyMethodDef TreeBuilder_methods[] = { }; -PyDoc_STRVAR(TreeBuilder__doc__, "TreeBuilder objects"); +PyDoc_STRVAR(TreeBuilder__doc__, "TreeBuilder objects."); PyTypeObject TreeBuilderType = { PyVarObject_HEAD_INIT(NULL, 0) diff --git a/src/walker.c b/src/walker.c index f7f0b146d..d1b128cdf 100644 --- a/src/walker.c +++ b/src/walker.c @@ -45,7 +45,8 @@ Walker_dealloc(Walker *self) PyDoc_STRVAR(Walker_hide__doc__, - "hide(oid)\n\n" + "hide(oid)\n" + "\n" "Mark a commit (and its ancestors) uninteresting for the output."); PyObject * @@ -67,7 +68,8 @@ Walker_hide(Walker *self, PyObject *py_hex) PyDoc_STRVAR(Walker_push__doc__, - "push(oid)\n\n" + "push(oid)\n" + "\n" "Mark a commit to start traversal from."); PyObject * @@ -89,7 +91,8 @@ Walker_push(Walker *self, PyObject *py_hex) PyDoc_STRVAR(Walker_sort__doc__, - "sort(mode)\n\n" + "sort(mode)\n" + "\n" "Change the sorting mode (this resets the walker)."); PyObject * @@ -108,7 +111,8 @@ Walker_sort(Walker *self, PyObject *py_sort_mode) PyDoc_STRVAR(Walker_reset__doc__, - "reset()\n\n" + "reset()\n" + "\n" "Reset the walking machinery for reuse."); PyObject * From 149db7c0551ccb11702353b3535500ea59545d89 Mon Sep 17 00:00:00 2001 From: richo Date: Wed, 13 Feb 2013 22:57:43 +1100 Subject: [PATCH 0354/2237] Implement Respository.hashfile This hashes the content of a file, without actually writing it to the db. --- src/pygit2.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/pygit2.c b/src/pygit2.c index 15d1a3f94..8c1938829 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -32,6 +32,7 @@ #include #include #include +#include #include extern PyObject *GitError; @@ -118,10 +119,33 @@ discover_repository(PyObject *self, PyObject *args) return to_path(repo_path); }; +PyDoc_STRVAR(hashfile__doc__, + "hash(path) -> bytes\n" + "\n" + "Returns the oid of a new blob from a file path without actually writing \n" + "to the odb."); +PyObject * +hashfile(PyObject *self, PyObject *args) +{ + git_oid oid; + const char* path; + int err; + + if (!PyArg_ParseTuple(args, "s", &path)) + return NULL; + + err = git_odb_hashfile(&oid, path, GIT_OBJ_BLOB); + if (err < 0) + return Error_set(err); + + return git_oid_to_python(oid.id); +} + PyMethodDef module_methods[] = { {"init_repository", init_repository, METH_VARARGS, init_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, + {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, {NULL} }; From 927b29d70e7093e143b20b553c7dc25e77698865 Mon Sep 17 00:00:00 2001 From: richo Date: Wed, 13 Feb 2013 23:08:16 +1100 Subject: [PATCH 0355/2237] Add tests for Repository.hashfile --- test/test_repository.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/test_repository.py b/test/test_repository.py index 3acf00818..1ca2eff2d 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -31,11 +31,12 @@ from __future__ import unicode_literals import binascii import unittest +import tempfile import os from os.path import join, realpath from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, init_repository, \ - discover_repository, Commit + discover_repository, Commit, hashfile from . import utils @@ -148,6 +149,16 @@ def test_revparse_single(self): self.assertEqual(parent.hex, PARENT_SHA) + def test_hashfile(self): + data = "bazbarfoo" + tempfile_path = tempfile.mkstemp()[1] + with open(tempfile_path, 'w') as fh: + fh.write(data) + hashed_sha1 = hashfile(tempfile_path) + written_sha1 = self.repo.create_blob(data) + self.assertEqual(hashed_sha1, written_sha1) + + class RepositoryTest_II(utils.RepoTestCase): def test_is_empty(self): From ec5cfa413e514b01d29017a7a49e5c6524791e6d Mon Sep 17 00:00:00 2001 From: richo Date: Wed, 13 Feb 2013 22:48:22 +1100 Subject: [PATCH 0356/2237] Implement Repository.hash This takes a string of arbitrary data (That may include null bytes) and returns it's sha1 hash as though it were a blob, without actually writing to the object db. --- src/pygit2.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/pygit2.c b/src/pygit2.c index 8c1938829..07e6bc9c5 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -141,11 +141,37 @@ hashfile(PyObject *self, PyObject *args) return git_oid_to_python(oid.id); } +PyDoc_STRVAR(hash__doc__, + "hash(data) -> bytes\n" + "\n" + "Returns the oid of a new blob from a string without actually writing to \n" + "the odb."); +PyObject * +hash(PyObject *self, PyObject *args) +{ + git_oid oid; + const char *data; + Py_ssize_t size; + int err; + + if (!PyArg_ParseTuple(args, "s#", &data, &size)) + return NULL; + + err = git_odb_hash(&oid, data, size, GIT_OBJ_BLOB); + if (err < 0) { + return Error_set(err); + } + + return git_oid_to_python(oid.id); +} + + PyMethodDef module_methods[] = { {"init_repository", init_repository, METH_VARARGS, init_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, + {"hash", hash, METH_VARARGS, hash__doc__}, {NULL} }; From 4551cb078a9a8fecd73156da8c655b79215f89b6 Mon Sep 17 00:00:00 2001 From: richo Date: Wed, 13 Feb 2013 22:55:26 +1100 Subject: [PATCH 0357/2237] Add tests for hash --- test/test_repository.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/test_repository.py b/test/test_repository.py index 1ca2eff2d..e16c842f2 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -37,6 +37,8 @@ from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, init_repository, \ discover_repository, Commit, hashfile +import pygit2 + from . import utils @@ -148,6 +150,11 @@ def test_revparse_single(self): parent = self.repo.revparse_single('HEAD^') self.assertEqual(parent.hex, PARENT_SHA) + def test_hash(self): + data = "foobarbaz" + hashed_sha1 = pygit2.hash(data) + written_sha1 = self.repo.create_blob(data) + self.assertEqual(hashed_sha1, written_sha1) def test_hashfile(self): data = "bazbarfoo" From 14a67349214393dbe3f4634af642abb522101b15 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 15 Feb 2013 18:08:38 +0100 Subject: [PATCH 0358/2237] 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 97f83cfd4..834d6a85e 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 d6ffc81e2..d596b74e4 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 07e6bc9c5..31d16eae0 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 000000000..43f61bde9 --- /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 f8a5708d6..20b104813 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 e16c842f2..f12567224 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -184,6 +184,26 @@ def test_get_workdir(self): 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): From 5847007075e7bf81b9a06caad0f5162dcc9c054f Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 19:41:00 +0100 Subject: [PATCH 0359/2237] free remote pointer in deallocation --- src/remote.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/remote.c b/src/remote.c index 43f61bde9..e1e745c6e 100644 --- a/src/remote.c +++ b/src/remote.c @@ -58,6 +58,7 @@ Remote_call(Remote *self, PyObject *args, PyObject *kwds) static void Remote_dealloc(Remote *self) { + git_remote_free(self->remote); PyObject_Del(self); } From 5a6a481fbbb01ccd2ce40c65fb5e29038322a337 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 19:47:12 +0100 Subject: [PATCH 0360/2237] remote_create: instantiate object manually for efficiency --- src/repository.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/repository.c b/src/repository.c index 20b104813..9b7142203 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1023,6 +1023,7 @@ PyDoc_STRVAR(Repository_remote_create__doc__, PyObject * Repository_remote_create(Repository *self, PyObject *args) { + Remote *py_remote; git_remote *remote; char *name = NULL, *url = NULL; int err; @@ -1034,7 +1035,11 @@ Repository_remote_create(Repository *self, PyObject *args) if (err < 0) return Error_set(err); - return INSTANCIATE_CLASS(RemoteType, Py_BuildValue("Os", self, name)); + py_remote = (Remote*) PyType_GenericNew(&RemoteType, NULL, NULL); + py_remote->repo = self; + py_remote->remote = remote; + + return (PyObject*) py_remote; } From 401e5c9aafcc9b3e5564d67e16c625d2e0f68aed Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 17:33:57 +0100 Subject: [PATCH 0361/2237] added remote 'origin' to testrepo.tar --- test/data/testrepo.tar | Bin 61440 -> 61440 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/data/testrepo.tar b/test/data/testrepo.tar index a064aa21bd9b68483d83340d58b587ada87eccd9..383d73325e2ee6cf26df7e1a6d3794261efb2f88 100644 GIT binary patch delta 2679 zcmcguO-~b16dh*D*fz95Ix!85Iue4U5@z0XI-^w+Vst~)03ik?gpan+sfCh$NC*ij zKfnb0h-+h95#yo|Vu%Yuj4N0E0A;C59`qg%YbErvtYS7Uh|@OTG|7gQfUgnTrV%eDB-b^D3qm?&gk z@lj=mN8C|m!$NiNZCIw^PWSA%#z7q?<4Q~zSIaO!FrI|fx;pyCF7HVih2u(|O1X5j3?WbVwuYS%qwWcYmlZth3q-Gub@`Pk=Q zQgZF*F7aP&tEKs)0 zcy|mLi|%sobTCjU=C#|}qFYYtI^46B`Jq%{Nzc3U@Mh{OYwR%uw_so)Q%+@J z@wH-Rp`>RsbLo;EOEzAb|1Igw|LU;7MJW;-a=J>(g}1UNQo0wy?Q0_F_0J3NYrG&0JI8U+#HcB647o(^7A2NNrbj4;DR zIj+GysG|niETqXZ!3{`igM36 kI6k1P)gKaR=#cM|hpnTLr`^3i5_h$udR=;z5YP66_Df#T^bS_&y(5CG^37-B|QFkGts3tYXvXm67i}52( zs%YAptZ|9rR(qq(TuwrMimM{7r#P3JWgF z;tJ=E>6(t`cwAxJ<{YA`Z@V(qd~7gt#dc%WQ-hiQH%je_hLwfTB}KVkRg~~=`4eoc ztM;Pv{Y!2zbJqO(qO$(ai>0wXTK026zEQ}R*aquJHk;Hqb4fMXM9gMVbD2%a(0|{miq?&6AaFxEV zjc6@V-NlrP?Ts#TXh~E%M8`@dFt!5oRSvuQgT{X)+hQ4wEkn6zhwFtrBa3)O79mN8 zM)+JNIgNzVpj3_kg3`NEX%>N5m>mwSvu;RW6oF6&lQ?vSuy#XjQIj!Fqq}HCgwcCF zC`B)f9h+wGOhOfj)YSl(f=E5uygF(`g`p#(PM--ai58LQN5+1T3STcuEzTLLFuIYk z8>HH_wqb@Lj9~;0LlNN{Nnsq3Z4<*H#^AWSv~n~{sh!B!DS@;`?UKL&1P;hqL1VlS zkBIm-E*{%Kzt!*XrO#^RNzQg{xj^6|f%n=q&3@*T25dAJUx;W&i*H From c1e645ea1ab7d2fa43ab9a48e3b4ba4e17a27fc6 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 17:34:59 +0100 Subject: [PATCH 0362/2237] moved remote tests to test_remote.py --- test/test_remote.py | 60 +++++++++++++++++++++++++++++++++++++++++ test/test_repository.py | 20 -------------- 2 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 test/test_remote.py diff --git a/test/test_remote.py b/test/test_remote.py new file mode 100644 index 000000000..60796ee03 --- /dev/null +++ b/test/test_remote.py @@ -0,0 +1,60 @@ +# -*- coding: UTF-8 -*- +# +# 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. + +"""Tests for Remote objects.""" + + +import pygit2 +from . import utils + +REMOTE_NAME = 'origin' +REMOTE_URL = 'git://github.com/libgit2/pygit2.git' + +class RepositoryTest(utils.RepoTestCase): + 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(1, len(self.repo.remotes)) + remote = self.repo.remotes[0] + self.assertEqual(REMOTE_NAME, remote.name) + self.assertEqual(REMOTE_URL, remote.url) + + 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]) diff --git a/test/test_repository.py b/test/test_repository.py index f12567224..e16c842f2 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -184,26 +184,6 @@ def test_get_workdir(self): 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): From bc2bb9fc26aca0c4654a14722da307b0c6e3cb93 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 18:29:40 +0100 Subject: [PATCH 0363/2237] added assertRaisesAssign test function --- test/utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/utils.py b/test/utils.py index 36eb23882..760930dfa 100644 --- a/test/utils.py +++ b/test/utils.py @@ -28,6 +28,7 @@ """Test utilities for libgit2.""" from binascii import b2a_hex +import sys import os import shutil import stat @@ -75,6 +76,13 @@ def tearDown(self): del self.repo rmtree(self._temp_dir) + def assertRaisesAssign(self, exc_class, instance, name, value): + try: + setattr(instance, name, value) + except: + self.assertEqual(exc_class, sys.exc_info()[0]) + + def assertRaisesWithArg(self, exc_class, arg, func, *args, **kwargs): try: func(*args, **kwargs) From ffaf0d5adac6834a31c6fa9a5ee7d9429338e237 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 18:30:39 +0100 Subject: [PATCH 0364/2237] added functionality to rename remotes --- src/remote.c | 21 ++++++++++++++++++++- test/test_remote.py | 10 ++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index e1e745c6e..bcd66c2b0 100644 --- a/src/remote.c +++ b/src/remote.c @@ -71,6 +71,25 @@ Remote_name__get__(Remote *self) return PyUnicode_FromString(git_remote_name(self->remote)); } +int +Remote_name__set__(Remote *self, PyObject* py_name) +{ + int err; + char* name; + + name = py_str_to_c_str(py_name, NULL); + if (name != NULL) { + err = git_remote_rename(self->remote, name, NULL, NULL); + + if (err == GIT_OK) + return 0; + + Error_set(err); + } + + return -1; +} + PyDoc_STRVAR(Remote_url__doc__, "Url of the remote refspec"); @@ -82,7 +101,7 @@ Remote_url__get__(Remote *self) PyGetSetDef Remote_getseters[] = { - GETTER(Remote, name), + GETSET(Remote, name), GETTER(Remote, url), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index 60796ee03..36f4eb55a 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -48,6 +48,16 @@ def test_remote_create(self): self.assertRaises(ValueError,self.repo.remote_create, *(name, url)) + def test_remote_rename(self): + remote = self.repo.remotes[0] + + self.assertEqual(REMOTE_NAME, remote.name) + remote.name = 'new' + self.assertEqual('new', remote.name) + + self.assertRaisesAssign(ValueError, remote, 'name', '') + + def test_remote_list(self): self.assertEqual(1, len(self.repo.remotes)) remote = self.repo.remotes[0] From d15093706802575ccc0cf9479bef944b7b4d93ba Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 18:36:38 +0100 Subject: [PATCH 0365/2237] added functionality to set remote urls --- src/remote.c | 22 +++++++++++++++++++++- test/test_remote.py | 11 +++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index bcd66c2b0..f8edbc7d6 100644 --- a/src/remote.c +++ b/src/remote.c @@ -100,9 +100,29 @@ Remote_url__get__(Remote *self) } +int +Remote_url__set__(Remote *self, PyObject* py_url) +{ + int err; + char* url; + + url = py_str_to_c_str(py_url, NULL); + if (url != NULL) { + err = git_remote_set_url(self->remote, url); + + if (err == GIT_OK) + return 0; + + Error_set(err); + } + + return -1; +} + + PyGetSetDef Remote_getseters[] = { GETSET(Remote, name), - GETTER(Remote, url), + GETSET(Remote, url), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index 36f4eb55a..2ca347f94 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -58,6 +58,17 @@ def test_remote_rename(self): self.assertRaisesAssign(ValueError, remote, 'name', '') + def test_remote_set_url(self): + remote = self.repo.remotes[0] + + self.assertEqual(REMOTE_URL, remote.url) + new_url = 'git://github.com/cholin/pygit2.git' + remote.url = new_url + self.assertEqual(new_url, remote.url) + + self.assertRaisesAssign(ValueError, remote, 'url', '') + + def test_remote_list(self): self.assertEqual(1, len(self.repo.remotes)) remote = self.repo.remotes[0] From ee32acdf3967a57cec5f51794c644914554450de Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 18:50:28 +0100 Subject: [PATCH 0366/2237] added remote fetchspec getter --- src/remote.c | 31 ++++++++++++++++++++++++++++++- test/test_remote.py | 8 ++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index f8edbc7d6..dc553f32c 100644 --- a/src/remote.c +++ b/src/remote.c @@ -91,7 +91,7 @@ Remote_name__set__(Remote *self, PyObject* py_name) } -PyDoc_STRVAR(Remote_url__doc__, "Url of the remote refspec"); +PyDoc_STRVAR(Remote_url__doc__, "Url of the remote"); PyObject * Remote_url__get__(Remote *self) @@ -120,9 +120,38 @@ Remote_url__set__(Remote *self, PyObject* py_url) } +PyDoc_STRVAR(Remote_fetchspec__doc__, + "= (source:str, destination:str)\n" + "\n" + "Name of the remote source and destination refspecs\n"); + + +PyObject * +Remote_fetchspec__get__(Remote *self) +{ + PyObject* py_tuple = NULL; + const git_refspec * refspec; + + refspec = git_remote_fetchspec(self->remote); + if (refspec != NULL) { + py_tuple = Py_BuildValue( + "(ss)", + git_refspec_src(refspec), + git_refspec_dst(refspec) + ); + + return py_tuple; + } + + return Error_set(GIT_ENOTFOUND); +} + + PyGetSetDef Remote_getseters[] = { GETSET(Remote, name), GETSET(Remote, url), + GETSET(Remote, url), + GETTER(Remote, fetchspec), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index 2ca347f94..b7595ebad 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -33,6 +33,8 @@ REMOTE_NAME = 'origin' REMOTE_URL = 'git://github.com/libgit2/pygit2.git' +REMOTE_FETCHSPEC_SRC = 'refs/heads/*' +REMOTE_FETCHSPEC_DST = 'refs/remotes/origin/*' class RepositoryTest(utils.RepoTestCase): def test_remote_create(self): @@ -69,6 +71,12 @@ def test_remote_set_url(self): self.assertRaisesAssign(ValueError, remote, 'url', '') + def test_remote_fetchspec(self): + remote = self.repo.remotes[0] + + self.assertEqual(REMOTE_FETCHSPEC_SRC, remote.fetchspec[0]) + self.assertEqual(REMOTE_FETCHSPEC_DST, remote.fetchspec[1]) + def test_remote_list(self): self.assertEqual(1, len(self.repo.remotes)) remote = self.repo.remotes[0] From 487352912971301f598e1bdd5f52683688435d82 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 20:46:21 +0100 Subject: [PATCH 0367/2237] added setter for remote fetchspec --- src/remote.c | 31 +++++++++++++++++++++++++++++-- test/test_remote.py | 6 ++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/remote.c b/src/remote.c index dc553f32c..1aae29e1d 100644 --- a/src/remote.c +++ b/src/remote.c @@ -123,7 +123,7 @@ Remote_url__set__(Remote *self, PyObject* py_url) PyDoc_STRVAR(Remote_fetchspec__doc__, "= (source:str, destination:str)\n" "\n" - "Name of the remote source and destination refspecs\n"); + "Name of the remote source and destination fetch refspecs\n"); PyObject * @@ -146,12 +146,39 @@ Remote_fetchspec__get__(Remote *self) return Error_set(GIT_ENOTFOUND); } +int +Remote_fetchspec__set__(Remote *self, PyObject* py_tuple) +{ + int err; + size_t length = 0; + char* src = NULL, *dst = NULL, *buf = NULL; + + if (!PyArg_ParseTuple(py_tuple, "ss", &src, &dst)) + return -1; + + // length is strlen('+' + src + ':' + dst) and Null-Byte + length = strlen(src) + strlen(dst) + 3; + buf = (char*) calloc(length, sizeof(char)); + if (buf != NULL) { + sprintf(buf, "+%s:%s", src, dst); + err = git_remote_set_fetchspec(self->remote, buf); + free(buf); + + if (err == GIT_OK) + return 0; + + Error_set_exc(PyExc_ValueError); + } + + return -1; +} + PyGetSetDef Remote_getseters[] = { GETSET(Remote, name), GETSET(Remote, url), GETSET(Remote, url), - GETTER(Remote, fetchspec), + GETSET(Remote, fetchspec), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index b7595ebad..7ac7aac8f 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -77,6 +77,12 @@ def test_remote_fetchspec(self): self.assertEqual(REMOTE_FETCHSPEC_SRC, remote.fetchspec[0]) self.assertEqual(REMOTE_FETCHSPEC_DST, remote.fetchspec[1]) + new_fetchspec = ('refs/foo/*','refs/remotes/foo/*') + remote.fetchspec = new_fetchspec + self.assertEqual(new_fetchspec[0], remote.fetchspec[0]) + self.assertEqual(new_fetchspec[1], remote.fetchspec[1]) + + def test_remote_list(self): self.assertEqual(1, len(self.repo.remotes)) remote = self.repo.remotes[0] From 4864e7b24c1a1b944a359b37f379436a854ac126 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 20:46:43 +0100 Subject: [PATCH 0368/2237] removed duplicated GETSET entry in remote.c --- src/remote.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index 1aae29e1d..09bc84776 100644 --- a/src/remote.c +++ b/src/remote.c @@ -177,7 +177,6 @@ Remote_fetchspec__set__(Remote *self, PyObject* py_tuple) PyGetSetDef Remote_getseters[] = { GETSET(Remote, name), GETSET(Remote, url), - GETSET(Remote, url), GETSET(Remote, fetchspec), {NULL} }; From d798d95a8b821d8d47a993309afe6b0df275e2ad Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 21:26:51 +0100 Subject: [PATCH 0369/2237] added basic fetch()-method for remotes --- src/remote.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index 09bc84776..6b706d9e9 100644 --- a/src/remote.c +++ b/src/remote.c @@ -174,6 +174,47 @@ Remote_fetchspec__set__(Remote *self, PyObject* py_tuple) } +PyDoc_STRVAR(Remote_fetch__doc__, + "fetch() -> {'indexed_objects': int, 'received_objects' : int," + " 'received_bytesa' : int}\n" + "\n" + "Negotiate what objects should be downloaded and download the\n" + "packfile with those objects"); + +PyObject * +Remote_fetch(Remote *self, PyObject *args) +{ + PyObject* py_stats; + const git_transfer_progress *stats; + int err; + + err = git_remote_connect(self->remote, GIT_DIRECTION_FETCH); + if (err == GIT_OK) { + err = git_remote_download(self->remote, NULL, NULL); + if (err == GIT_OK) { + stats = git_remote_stats(self->remote); + py_stats = Py_BuildValue("{s:i,s:i,s:i}", + "indexed_objects", stats->indexed_objects, + "received_objects", stats->received_objects, + "received_bytes", stats->received_bytes); + + err = git_remote_update_tips(self->remote); + } + git_remote_disconnect(self->remote); + } + + if (err < 0) + return Error_set(err); + + return (PyObject*) py_stats; +} + + +PyMethodDef Remote_methods[] = { + METHOD(Remote, fetch, METH_NOARGS), + {NULL} +}; + PyGetSetDef Remote_getseters[] = { GETSET(Remote, name), GETSET(Remote, url), @@ -211,7 +252,7 @@ PyTypeObject RemoteType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + Remote_methods, /* tp_methods */ 0, /* tp_members */ Remote_getseters, /* tp_getset */ 0, /* tp_base */ From 3bf32f707a89f180945f882a5b91a49984bd6688 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 21:56:00 +0100 Subject: [PATCH 0370/2237] added remote 'origin' to emptyrepo.tar --- test/data/emptyrepo.tar | Bin 30720 -> 30720 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/data/emptyrepo.tar b/test/data/emptyrepo.tar index 8584ee7a1f2ca2c341e266e7483c8d16e07293a6..20858175bbabadff88e775a3254c03b8596dfc8a 100644 GIT binary patch delta 254 zcmZqpz}WDCaYGlAu%Usuxw(O%i6Mi5p|PQ{8H0l9WJBhZ&5ZIQjGIN}7@0PUW&C8` z%pb-sv{}sOKj&gmGqKI0Ml39wCH4L=Zx+>HX4@>G@SS}zzXaQ2ei4?%{Cq5%`B_TY zB#q1s84Qh(%{MVN0-A3CF`qk{F*dp=H8;N`RY57gC^J1XPbrp*v$QBj!B(LpwYWq- zC9xz?AH*t3Ey&kP&n)5MOiL|E&H&157p0~Z>u01UrWEUIS%Enaql)z*M(JyDZ4MN3 bXWS&j&$O98hm~owNWmM%MR`n%9SYe1q^wC` delta 151 zcmZqpz}WDCaYGl=#4a9oV?$$O1_jf}hRi9O856}AH;X6avPl{mn=lv}8JL)u8km_G z7%&(Zniv}yGAI~8)TKu=ZVrrE!ni4hpJg$B7Sm$>2p}EAwwa%WooTa}*H8A%{4Shq znGFiq0M4!Kbbf4=ddzu7AbhcxG0Zlu|pvn06Ow2 A7ytkO From 9c9e0f975539f9916d97c7bf595d9f3c995c26c4 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 21:56:34 +0100 Subject: [PATCH 0371/2237] added tests for remote.fetch() --- src/remote.c | 2 +- test/test_remote.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index 6b706d9e9..175786f26 100644 --- a/src/remote.c +++ b/src/remote.c @@ -193,7 +193,7 @@ Remote_fetch(Remote *self, PyObject *args) err = git_remote_download(self->remote, NULL, NULL); if (err == GIT_OK) { stats = git_remote_stats(self->remote); - py_stats = Py_BuildValue("{s:i,s:i,s:i}", + py_stats = Py_BuildValue("{s:I,s:I,s:n}", "indexed_objects", stats->indexed_objects, "received_objects", stats->received_objects, "received_bytes", stats->received_bytes); diff --git a/test/test_remote.py b/test/test_remote.py index 7ac7aac8f..87c341efb 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -35,6 +35,8 @@ REMOTE_URL = 'git://github.com/libgit2/pygit2.git' REMOTE_FETCHSPEC_SRC = 'refs/heads/*' REMOTE_FETCHSPEC_DST = 'refs/remotes/origin/*' +REMOTE_REPO_OBJECTS = 19 +REMOTE_REPO_BYTES = 1586 class RepositoryTest(utils.RepoTestCase): def test_remote_create(self): @@ -93,3 +95,12 @@ def test_remote_list(self): 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 EmptyRepositoryTest(utils.EmptyRepoTestCase): + def test_fetch(self): + remote = self.repo.remotes[0] + stats = remote.fetch() + self.assertEqual(stats['received_bytes'], REMOTE_REPO_BYTES) + self.assertEqual(stats['indexed_objects'], REMOTE_REPO_OBJECTS) + self.assertEqual(stats['received_objects'], REMOTE_REPO_OBJECTS) From 0823621bb43a73596ac619b8737a7c7079f88772 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 16 Feb 2013 21:58:42 +0100 Subject: [PATCH 0372/2237] renamed remote_create() to create_remote() --- src/repository.c | 6 +++--- test/test_remote.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/repository.c b/src/repository.c index 9b7142203..4bfb71e65 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1015,13 +1015,13 @@ Repository_TreeBuilder(Repository *self, PyObject *args) } -PyDoc_STRVAR(Repository_remote_create__doc__, +PyDoc_STRVAR(Repository_create_remote__doc__, "remote_create(name, url) -> Remote\n" "\n" "Creates a new remote."); PyObject * -Repository_remote_create(Repository *self, PyObject *args) +Repository_create_remote(Repository *self, PyObject *args) { Remote *py_remote; git_remote *remote; @@ -1082,7 +1082,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), + METHOD(Repository, create_remote, METH_VARARGS), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index 87c341efb..0a156c4d9 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -43,13 +43,13 @@ def test_remote_create(self): name = 'upstream' url = 'git://github.com/libgit2/pygit2.git' - remote = self.repo.remote_create(name, url); + remote = self.repo.create_remote(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)) + self.assertRaises(ValueError,self.repo.create_remote, *(name, url)) def test_remote_rename(self): @@ -93,7 +93,7 @@ def test_remote_list(self): name = 'upstream' url = 'git://github.com/libgit2/pygit2.git' - remote = self.repo.remote_create(name, url); + remote = self.repo.create_remote(name, url); self.assertTrue(remote.name in [x.name for x in self.repo.remotes]) From bd62fded03e5c49bb95e3aef627b9f305449018d Mon Sep 17 00:00:00 2001 From: Christian Boos Date: Sun, 17 Feb 2013 11:33:20 +0100 Subject: [PATCH 0373/2237] Add _pygit2 constants for libgit2 version information. --- src/pygit2.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pygit2.c b/src/pygit2.c index 31d16eae0..e46e67f46 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -179,6 +179,8 @@ PyMethodDef module_methods[] = { PyObject* moduleinit(PyObject* m) { + int libgit2_major, libgit2_minor, libgit2_rev; + if (m == NULL) return NULL; @@ -401,6 +403,12 @@ moduleinit(PyObject* m) PyModule_AddIntConstant(m, "GIT_FILEMODE_LINK", GIT_FILEMODE_LINK); PyModule_AddIntConstant(m, "GIT_FILEMODE_COMMIT", GIT_FILEMODE_COMMIT); + /* libgit2 version info */ + git_libgit2_version(&libgit2_major, &libgit2_minor, &libgit2_rev); + PyModule_AddIntConstant(m, "LIBGIT2_VERSION_MAJOR", libgit2_major); + PyModule_AddIntConstant(m, "LIBGIT2_VERSION_MINOR", libgit2_minor); + PyModule_AddIntConstant(m, "LIBGIT2_VERSION_REV", libgit2_rev); + return m; } From 8f843beecf89489a94b3105cd9dd078974428995 Mon Sep 17 00:00:00 2001 From: Christian Boos Date: Sun, 17 Feb 2013 11:38:40 +0100 Subject: [PATCH 0374/2237] Add pygit2.__libgit2_version__ to retrieve git_libgit2_version() info. --- pygit2/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 6a0e24b53..601bd2131 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -28,3 +28,6 @@ from .version import __version__ from _pygit2 import * import pygit2.utils + +__libgit2_version__ = '%d.%d.%s' % ( + LIBGIT2_VERSION_MAJOR, LIBGIT2_VERSION_MINOR, LIBGIT2_VERSION_REV) From 64ae9e19f4ea4a3362a91f4707032ab2a9f87479 Mon Sep 17 00:00:00 2001 From: Christian Boos Date: Sun, 17 Feb 2013 12:52:36 +0100 Subject: [PATCH 0375/2237] Use libgit2 LIBGIT2_VER* constants directly instead of git_libgit2_version() call. --- pygit2/__init__.py | 3 +-- src/pygit2.c | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 601bd2131..b48f15d46 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -29,5 +29,4 @@ from _pygit2 import * import pygit2.utils -__libgit2_version__ = '%d.%d.%s' % ( - LIBGIT2_VERSION_MAJOR, LIBGIT2_VERSION_MINOR, LIBGIT2_VERSION_REV) +__libgit2_version__ = LIBGIT2_VERSION diff --git a/src/pygit2.c b/src/pygit2.c index e46e67f46..3ee2ff5fc 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -179,8 +179,6 @@ PyMethodDef module_methods[] = { PyObject* moduleinit(PyObject* m) { - int libgit2_major, libgit2_minor, libgit2_rev; - if (m == NULL) return NULL; @@ -404,10 +402,10 @@ moduleinit(PyObject* m) PyModule_AddIntConstant(m, "GIT_FILEMODE_COMMIT", GIT_FILEMODE_COMMIT); /* libgit2 version info */ - git_libgit2_version(&libgit2_major, &libgit2_minor, &libgit2_rev); - PyModule_AddIntConstant(m, "LIBGIT2_VERSION_MAJOR", libgit2_major); - PyModule_AddIntConstant(m, "LIBGIT2_VERSION_MINOR", libgit2_minor); - PyModule_AddIntConstant(m, "LIBGIT2_VERSION_REV", libgit2_rev); + PyModule_AddIntConstant(m, "LIBGIT2_VER_MAJOR", LIBGIT2_VER_MAJOR); + PyModule_AddIntConstant(m, "LIBGIT2_VER_MINOR", LIBGIT2_VER_MINOR); + PyModule_AddIntConstant(m, "LIBGIT2_VER_REVISION", LIBGIT2_VER_REVISION); + PyModule_AddStringConstant(m, "LIBGIT2_VERSION", LIBGIT2_VERSION); return m; } From d8026493cf46baf4766c336b0ffcfa423364fa52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 17 Feb 2013 17:29:38 +0100 Subject: [PATCH 0376/2237] tests: add remote to the test suite --- test/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/__init__.py b/test/__init__.py index 4cd7cc553..16b0d09df 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -35,8 +35,10 @@ import unittest -names = ['blob', 'commit', 'config', 'index', 'refs', 'repository', 'revwalk', - 'tag', 'tree', 'signature', 'status', 'treebuilder', 'diff'] +names = ['blob', 'commit', 'config', 'diff', 'index', 'refs', 'remote', + 'repository', 'revwalk', 'signature', 'status', 'tag', 'tree', + 'treebuilder'] + def test_suite(): # Sometimes importing pygit2 fails, we try this first to get an # informative traceback From 826409b3db4fb10c3ddb5e73bd4271d39bba35a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 17 Feb 2013 17:29:38 +0100 Subject: [PATCH 0377/2237] tests: add remote to the test suite --- test/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/__init__.py b/test/__init__.py index 4cd7cc553..16b0d09df 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -35,8 +35,10 @@ import unittest -names = ['blob', 'commit', 'config', 'index', 'refs', 'repository', 'revwalk', - 'tag', 'tree', 'signature', 'status', 'treebuilder', 'diff'] +names = ['blob', 'commit', 'config', 'diff', 'index', 'refs', 'remote', + 'repository', 'revwalk', 'signature', 'status', 'tag', 'tree', + 'treebuilder'] + def test_suite(): # Sometimes importing pygit2 fails, we try this first to get an # informative traceback From 673053dc7136cc6ba87296e23d3e817796928a99 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 17 Feb 2013 12:54:01 +0100 Subject: [PATCH 0378/2237] added GIT_CHECKOUT_* constants --- src/pygit2.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/pygit2.c b/src/pygit2.c index 31d16eae0..d921b7f97 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -401,6 +401,27 @@ moduleinit(PyObject* m) PyModule_AddIntConstant(m, "GIT_FILEMODE_LINK", GIT_FILEMODE_LINK); PyModule_AddIntConstant(m, "GIT_FILEMODE_COMMIT", GIT_FILEMODE_COMMIT); + /* Different checkout strategies */ + PyModule_AddIntConstant(m, "GIT_CHECKOUT_NONE", GIT_CHECKOUT_NONE); + PyModule_AddIntConstant(m, "GIT_CHECKOUT_SAFE", GIT_CHECKOUT_SAFE); + PyModule_AddIntConstant(m, "GIT_CHECKOUT_SAFE_CREATE", + GIT_CHECKOUT_SAFE_CREATE); + PyModule_AddIntConstant(m, "GIT_CHECKOUT_FORCE", GIT_CHECKOUT_FORCE); + PyModule_AddIntConstant(m, "GIT_CHECKOUT_ALLOW_CONFLICTS", + GIT_CHECKOUT_ALLOW_CONFLICTS); + PyModule_AddIntConstant(m, "GIT_CHECKOUT_REMOVE_UNTRACKED", + GIT_CHECKOUT_REMOVE_UNTRACKED); + PyModule_AddIntConstant(m, "GIT_CHECKOUT_REMOVE_IGNORED", + GIT_CHECKOUT_REMOVE_IGNORED); + PyModule_AddIntConstant(m, "GIT_CHECKOUT_UPDATE_ONLY", + GIT_CHECKOUT_UPDATE_ONLY); + PyModule_AddIntConstant(m, "GIT_CHECKOUT_DONT_UPDATE_INDEX", + GIT_CHECKOUT_DONT_UPDATE_INDEX); + PyModule_AddIntConstant(m, "GIT_CHECKOUT_NO_REFRESH", + GIT_CHECKOUT_NO_REFRESH); + PyModule_AddIntConstant(m, "GIT_CHECKOUT_DISABLE_PATHSPEC_MATC", + GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH); + return m; } From 35230d06b8b2d5bd7a472b93c70a9d8e992ff4b7 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 17 Feb 2013 12:54:32 +0100 Subject: [PATCH 0379/2237] initialize py_stats to NULL --- src/remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index 175786f26..b7a6f081a 100644 --- a/src/remote.c +++ b/src/remote.c @@ -184,7 +184,7 @@ PyDoc_STRVAR(Remote_fetch__doc__, PyObject * Remote_fetch(Remote *self, PyObject *args) { - PyObject* py_stats; + PyObject* py_stats = NULL; const git_transfer_progress *stats; int err; From 270fa59e5b502c5912a8d9a42f1b68354b49b3e0 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 17 Feb 2013 12:54:57 +0100 Subject: [PATCH 0380/2237] added bye.txt and new to testrepo i18n branch --- test/data/testrepo.tar | Bin 61440 -> 61440 bytes test/test_revwalk.py | 3 +++ 2 files changed, 3 insertions(+) diff --git a/test/data/testrepo.tar b/test/data/testrepo.tar index 383d73325e2ee6cf26df7e1a6d3794261efb2f88..9c451b2f262fb4a18590f2f43f96c64d49676495 100644 GIT binary patch delta 1923 zcmcIl4@^~67=M>1piNO2Jrjam5%fJlIQN`$&%KWru`*t!GHV00fsyi<#x@CnHHiD;4_+%05uosI6}tl5m$)v^LWV%Qf^ zXgol02DZj9(-z6)Qe3QDucX$vYgnn$#a1eHchLeqrk#!LEo+UKrR}GNkPukk$JFgL z2cvp*j07?(S;O6ona64EkvZOu1zK6ys(?iPOhTar#HE=wnkfK@2%IDW$%zEfu5uEQ zM5m(4P8B1kB1kIW32Ki_^4DvLjiL06y3)E3asqGQ=W#P{v~l@nZkeh3&wZCy?`@(6#McSYonmDq*tKCLP#R{?(T8HCv*(lJ*RBzpWkrWlV5Cp0nr5#fg#1 zgAoOuOmN})j>d+@6PMpT(S1H1L>)er8(AAaP*ee(V#Yx+w=2foI~%&!-f{q{o> zQ~Z0L7CH2YmSw%!+h_?h+l7Br4^fsgt*NU=GZB)Bq6jLHky8c|6bLU833JFP%24EF zNs&d_WmJ#jg}L1UAq1R3y(%}8Y;)ul0Sr|71Bffk?#Ufb?zT!tItG5O7(TW1F~^k; z1{>2oVwe5QweUwbZg@IBKeKpj^K%alzIAdMe^$Ew`I8?ed|bP>=f&6_Yg&KD@aIi& zR}Z#)bO0%y$r}Sd`KG#k8BL7o({I6I+IH=Xcc;ZH*#AbHc81tm7VW^zWqDA)|(Q6m?yZMMXtS*0oG$vVU!E zR6!+4mRb}^Mv5wmDBd174v@n~pL0`7x4vASo3F>3$Df|qjhXp(-%PiWyL2vjJY4)b{A`x{{OJ1IpIo;7 zBTV+D$rJPbXp}|j%B6`JwQVdQWH&W!njjP{W)*G6rLbU?DbJ_iTA3Oi1!^&D3lDlNId~&ESq}ZF~en+UBsc9#Y&J)rSYa*hCGFqs5uFSZQ=>Ea+}qm zLOstYQZVw4%RFEu3I(T9v6z4wS8d351RS)APq9zk%^X0B0KqGpre z{i~mg78v-hx|J=vP_CL7)P`EgV4dI{kFA~gIu+O*#$%fvG)jJAf7K+d)0NsfhwV(n zRaxm&Y;BB$ Date: Sun, 17 Feb 2013 12:56:11 +0100 Subject: [PATCH 0381/2237] new Repository.checkout method --- src/repository.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/repository.c b/src/repository.c index 4bfb71e65..38451c86c 100644 --- a/src/repository.c +++ b/src/repository.c @@ -45,6 +45,7 @@ extern PyTypeObject TreeBuilderType; extern PyTypeObject ConfigType; extern PyTypeObject DiffType; extern PyTypeObject RemoteType; +extern PyTypeObject ReferenceType; git_otype int_to_loose_object_type(int type_id) @@ -1066,6 +1067,44 @@ Repository_remotes__get__(Repository *self) } +PyDoc_STRVAR(Repository_checkout__doc__, + "checkout(reference:Reference, [strategy:int])\n" + "\n" + "Checks out a tree by a given reference and modifies the HEAD pointer.\n" + "Standard checkout strategy is pygit2.GIT_CHECKOUT_SAFE_CREATE"); + +PyObject * +Repository_checkout(Repository *self, PyObject *args) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + unsigned int strategy = GIT_CHECKOUT_SAFE_CREATE; + Reference* ref; + git_object* object; + const git_oid* id; + int err; + + if (!PyArg_ParseTuple(args, "O!|I", &ReferenceType, &ref, &strategy)) + return NULL; + + CHECK_REFERENCE(ref); + + id = git_reference_target(ref->reference); + err = git_object_lookup(&object, self->repo, id, GIT_OBJ_COMMIT); + if(err == GIT_OK) { + opts.checkout_strategy = strategy; + err = git_checkout_tree(self->repo, object, &opts); + if (err == GIT_OK) + err = git_repository_set_head(self->repo, + git_reference_name(ref->reference)); + } + + if(err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + + PyMethodDef Repository_methods[] = { METHOD(Repository, create_blob, METH_VARARGS), METHOD(Repository, create_blob_fromfile, METH_VARARGS), @@ -1083,6 +1122,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, status, METH_NOARGS), METHOD(Repository, status_file, METH_O), METHOD(Repository, create_remote, METH_VARARGS), + METHOD(Repository, checkout, METH_VARARGS), {NULL} }; From db5e1443b6181b4d6b776b572cfac8b5c5c3ad24 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 17 Feb 2013 12:56:24 +0100 Subject: [PATCH 0382/2237] added tests for Repo.checkout() --- test/test_repository.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/test_repository.py b/test/test_repository.py index e16c842f2..a05d9e396 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -184,6 +184,19 @@ def test_get_workdir(self): expected = realpath(join(self._temp_dir, 'testrepo')) self.assertEqual(directory, expected) + def test_checkout(self): + ref_i18n = self.repo.lookup_reference('refs/heads/i18n') + + self.assertRaises(pygit2.GitError, self.repo.checkout, ref_i18n) + + self.repo.checkout(ref_i18n, pygit2.GIT_CHECKOUT_FORCE) + self.assertEqual(self.repo.head.hex, self.repo[ref_i18n.target].hex) + self.assertTrue('new' in self.repo.head.tree) + self.assertTrue('bye.txt' not in self.repo.status()) + + ref_master = self.repo.lookup_reference('refs/heads/master') + self.repo.checkout(ref_master, pygit2.GIT_CHECKOUT_FORCE) + self.assertTrue('new' not in self.repo.head.tree) class NewRepositoryTest(utils.NoRepoTestCase): def test_new_repo(self): From b828eb926f38d98c7d4a0b41fbb75b3113816fa4 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 17 Feb 2013 14:23:41 +0100 Subject: [PATCH 0383/2237] added functionality to checkout from HEAD --- src/repository.c | 40 ++++++++++++++++++++++++---------------- test/test_repository.py | 20 +++++++++++++++----- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/repository.c b/src/repository.c index 38451c86c..cf816d99f 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1068,34 +1068,42 @@ Repository_remotes__get__(Repository *self) PyDoc_STRVAR(Repository_checkout__doc__, - "checkout(reference:Reference, [strategy:int])\n" + "checkout([strategy:int, reference:Reference])\n" "\n" - "Checks out a tree by a given reference and modifies the HEAD pointer.\n" - "Standard checkout strategy is pygit2.GIT_CHECKOUT_SAFE_CREATE"); + "Checks out a tree by a given reference and modifies the HEAD pointer\n" + "Standard checkout strategy is pygit2.GIT_CHECKOUT_SAFE_CREATE\n" + "If no reference is given, checkout will use HEAD instead."); PyObject * -Repository_checkout(Repository *self, PyObject *args) +Repository_checkout(Repository *self, PyObject *args, PyObject *kw) { git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; unsigned int strategy = GIT_CHECKOUT_SAFE_CREATE; - Reference* ref; + Reference* ref = NULL; git_object* object; const git_oid* id; int err; - if (!PyArg_ParseTuple(args, "O!|I", &ReferenceType, &ref, &strategy)) - return NULL; + static char *kwlist[] = {"strategy", "reference", NULL}; - CHECK_REFERENCE(ref); + if (!PyArg_ParseTupleAndKeywords(args, kw, "|IO!", kwlist, + &strategy, &ReferenceType, &ref)) + return NULL; - id = git_reference_target(ref->reference); - err = git_object_lookup(&object, self->repo, id, GIT_OBJ_COMMIT); - if(err == GIT_OK) { + if (ref != NULL) { // checkout from treeish + id = git_reference_target(ref->reference); + err = git_object_lookup(&object, self->repo, id, GIT_OBJ_COMMIT); + if(err == GIT_OK) { + opts.checkout_strategy = strategy; + err = git_checkout_tree(self->repo, object, &opts); + if (err == GIT_OK) { + err = git_repository_set_head(self->repo, + git_reference_name(ref->reference)); + } + } + } else { // checkout from head opts.checkout_strategy = strategy; - err = git_checkout_tree(self->repo, object, &opts); - if (err == GIT_OK) - err = git_repository_set_head(self->repo, - git_reference_name(ref->reference)); + err = git_checkout_head(self->repo, &opts); } if(err < 0) @@ -1122,7 +1130,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, status, METH_NOARGS), METHOD(Repository, status_file, METH_O), METHOD(Repository, create_remote, METH_VARARGS), - METHOD(Repository, checkout, METH_VARARGS), + METHOD(Repository, checkout, METH_VARARGS|METH_KEYWORDS), {NULL} }; diff --git a/test/test_repository.py b/test/test_repository.py index a05d9e396..e85e6a5ba 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -187,16 +187,26 @@ def test_get_workdir(self): def test_checkout(self): ref_i18n = self.repo.lookup_reference('refs/heads/i18n') - self.assertRaises(pygit2.GitError, self.repo.checkout, ref_i18n) + # checkout i18n with conflicts and default strategy should + # not be possible + self.assertRaises(pygit2.GitError, + lambda: self.repo.checkout(reference=ref_i18n)) - self.repo.checkout(ref_i18n, pygit2.GIT_CHECKOUT_FORCE) + # checkout i18n with GIT_CHECKOUT_FORCE + self.assertTrue('new' not in self.repo.head.tree) + self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE, ref_i18n) self.assertEqual(self.repo.head.hex, self.repo[ref_i18n.target].hex) self.assertTrue('new' in self.repo.head.tree) self.assertTrue('bye.txt' not in self.repo.status()) - ref_master = self.repo.lookup_reference('refs/heads/master') - self.repo.checkout(ref_master, pygit2.GIT_CHECKOUT_FORCE) - self.assertTrue('new' not in self.repo.head.tree) + # some changes to working dir + with open(os.path.join(self.repo.workdir, 'bye.txt'), 'w') as f: + f.write('new content') + + # checkout head + self.assertTrue('bye.txt' in self.repo.status()) + self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE) + self.assertTrue('bye.txt' not in self.repo.status()) class NewRepositoryTest(utils.NoRepoTestCase): def test_new_repo(self): From 1b5bf479ffa0679bbecab70f840a6453fb541100 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 17 Feb 2013 14:59:54 +0100 Subject: [PATCH 0384/2237] added functionality to checkout from index --- src/repository.c | 13 +++++++------ test/test_repository.py | 20 ++++++++++++++++++-- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/repository.c b/src/repository.c index cf816d99f..b187ab6c3 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1082,12 +1082,12 @@ Repository_checkout(Repository *self, PyObject *args, PyObject *kw) Reference* ref = NULL; git_object* object; const git_oid* id; - int err; + int err, head = 0; - static char *kwlist[] = {"strategy", "reference", NULL}; + static char *kwlist[] = {"strategy", "reference", "head", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kw, "|IO!", kwlist, - &strategy, &ReferenceType, &ref)) + if (!PyArg_ParseTupleAndKeywords(args, kw, "|IO!i", kwlist, + &strategy, &ReferenceType, &ref, &head)) return NULL; if (ref != NULL) { // checkout from treeish @@ -1101,9 +1101,10 @@ Repository_checkout(Repository *self, PyObject *args, PyObject *kw) git_reference_name(ref->reference)); } } - } else { // checkout from head + } else { // checkout from head / index opts.checkout_strategy = strategy; - err = git_checkout_head(self->repo, &opts); + err = (!head) ? git_checkout_index(self->repo, NULL, &opts) : + git_checkout_head(self->repo, &opts); } if(err < 0) diff --git a/test/test_repository.py b/test/test_repository.py index e85e6a5ba..10c98854d 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -184,7 +184,7 @@ def test_get_workdir(self): expected = realpath(join(self._temp_dir, 'testrepo')) self.assertEqual(directory, expected) - def test_checkout(self): + def test_checkout_ref(self): ref_i18n = self.repo.lookup_reference('refs/heads/i18n') # checkout i18n with conflicts and default strategy should @@ -199,13 +199,29 @@ def test_checkout(self): self.assertTrue('new' in self.repo.head.tree) self.assertTrue('bye.txt' not in self.repo.status()) + def test_checkout_index(self): # some changes to working dir + with open(os.path.join(self.repo.workdir, 'hello.txt'), 'w') as f: + f.write('new content') + + # checkout index + self.assertTrue('hello.txt' in self.repo.status()) + self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE) + self.assertTrue('hello.txt' not in self.repo.status()) + + def test_checkout_head(self): + # some changes to the index with open(os.path.join(self.repo.workdir, 'bye.txt'), 'w') as f: f.write('new content') + self.repo.index.add('bye.txt') - # checkout head + # checkout from index should not change anything self.assertTrue('bye.txt' in self.repo.status()) self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE) + self.assertTrue('bye.txt' in self.repo.status()) + + # checkout from head will reset index as well + self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE, head=True) self.assertTrue('bye.txt' not in self.repo.status()) class NewRepositoryTest(utils.NoRepoTestCase): From ed6657802967aa9c32816cf38193acd16dba84e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 17 Feb 2013 22:46:11 +0100 Subject: [PATCH 0385/2237] Remove __libgit2_version__ because it is a dup We already have LIBGIT2_VERSION --- pygit2/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index b48f15d46..6a0e24b53 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -28,5 +28,3 @@ from .version import __version__ from _pygit2 import * import pygit2.utils - -__libgit2_version__ = LIBGIT2_VERSION From 4e14c1e5437ee2b05149b07541fa4bd7f262d6ab Mon Sep 17 00:00:00 2001 From: Christian Boos Date: Mon, 18 Feb 2013 20:46:02 +0100 Subject: [PATCH 0386/2237] update version to 0.17.4dev (once 0.17.3 is released, it's no longer 0.17.3) --- pygit2/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygit2/version.py b/pygit2/version.py index 115507691..4ec576460 100644 --- a/pygit2/version.py +++ b/pygit2/version.py @@ -23,4 +23,4 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -__version__ = '0.17.3' +__version__ = '0.17.4dev' From 67f68d22345d966a44c89aad640010d509ae643e Mon Sep 17 00:00:00 2001 From: Christian Boos Date: Mon, 18 Feb 2013 20:51:06 +0100 Subject: [PATCH 0387/2237] Fix setup.py so that bdist_wininst works. Leaving the maintainer's name as a string (as opposed to a unicode) was triggering a UnicodeDecodeError when running bdist_wininst with Python 2.7.3 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b4daa38d4..52d8ed9f9 100644 --- a/setup.py +++ b/setup.py @@ -171,7 +171,7 @@ def get_file_list(self): url='http://github.com/libgit2/pygit2', classifiers=classifiers, license='GPLv2', - maintainer='J. David Ibáñez', + maintainer=u'J. David Ibáñez', maintainer_email='jdavid.ibp@gmail.com', long_description=long_description, packages = ['pygit2'], From c4fa231df3b2f4996ad1b16cdab5d1dc01f98acc Mon Sep 17 00:00:00 2001 From: Christian Boos Date: Mon, 18 Feb 2013 20:58:43 +0100 Subject: [PATCH 0388/2237] setup: Emacs actually don't like UTF-8, prefers utf-8 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 52d8ed9f9..0aa293026 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -# -*- coding: UTF-8 -*- +# -*- coding: utf-8 -*- # coding: UTF-8 # # Copyright 2010-2012 The pygit2 contributors From bcf48321eeb58f40b367d2549581cb5f38c3f464 Mon Sep 17 00:00:00 2001 From: Christian Boos Date: Mon, 18 Feb 2013 21:03:19 +0100 Subject: [PATCH 0389/2237] setup: using u'...' breaks for Python 3.[0-2]. --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0aa293026..819129b7d 100644 --- a/setup.py +++ b/setup.py @@ -44,6 +44,8 @@ sys.path.insert(0, 'pygit2') from version import __version__ +u = lambda s: s if sys.version_info[0] > 2 else unicode(s, 'utf-8') + # Use environment variable LIBGIT2 to set your own libgit2 configuration. libgit2_path = os.getenv("LIBGIT2") @@ -171,7 +173,7 @@ def get_file_list(self): url='http://github.com/libgit2/pygit2', classifiers=classifiers, license='GPLv2', - maintainer=u'J. David Ibáñez', + maintainer=u('J. David Ibáñez'), maintainer_email='jdavid.ibp@gmail.com', long_description=long_description, packages = ['pygit2'], From 933e02ee5c0c7195f04d1f48ea427de2cd8ddb14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 19 Feb 2013 10:53:41 +0100 Subject: [PATCH 0390/2237] Cosmetic fix to setup.py --- setup.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 819129b7d..160d65d0d 100644 --- a/setup.py +++ b/setup.py @@ -39,12 +39,17 @@ from distutils.command.sdist import sdist from distutils import log -# read version from local pygit2/version.py without pulling in +# Read version from local pygit2/version.py without pulling in # pygit2/__init__.py sys.path.insert(0, 'pygit2') from version import __version__ -u = lambda s: s if sys.version_info[0] > 2 else unicode(s, 'utf-8') +# Python 2 support +# See https://github.com/libgit2/pygit2/pull/180 for a discussion about this. +if sys.version_info[0] == 2: + u = lambda s: unicode(s, 'utf-8') +else: + u = str # Use environment variable LIBGIT2 to set your own libgit2 configuration. From 25947338b85a12a1a2bb1b55af5af81a0e0c15c0 Mon Sep 17 00:00:00 2001 From: Valentin Haenel Date: Thu, 28 Feb 2013 19:56:57 +0100 Subject: [PATCH 0391/2237] allow for keyword args in init_repository This implements five ways to init a repo: import pygit2 as git git.init_repository("/tmp/foo/") git.init_repository("/tmp/foo/", True) git.init_repository("/tmp/foo/", False) git.init_repository("/tmp/foo/", bare=True) git.init_repository("/tmp/foo/", bare=False) Since one is still able to pass the bare as a positional argument, this change is perfectly backwards compatible. All existing code will continue to work. --- src/pygit2.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index 3bfbb324a..63fdcb424 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -62,20 +62,20 @@ extern PyTypeObject RemoteType; PyDoc_STRVAR(init_repository__doc__, - "init_repository(path, bare) -> Repository\n" + "init_repository(path, bare=False) -> Repository\n" "\n" "Creates a new Git repository in the given path."); PyObject * -init_repository(PyObject *self, PyObject *args) -{ +init_repository(PyObject *self, PyObject *args, PyObject *kw) { git_repository *repo; Repository *py_repo; const char *path; - unsigned int bare; + unsigned int bare = 0; int err; + static char * kwlist[] = {"path", "bare", NULL}; - if (!PyArg_ParseTuple(args, "sI", &path, &bare)) + if (!PyArg_ParseTupleAndKeywords(args, kw, "s|I", kwlist, &path, &bare)) return NULL; err = git_repository_init(&repo, path, bare); @@ -168,7 +168,7 @@ hash(PyObject *self, PyObject *args) PyMethodDef module_methods[] = { - {"init_repository", init_repository, METH_VARARGS, init_repository__doc__}, + {"init_repository", init_repository, METH_VARARGS|METH_KEYWORDS, init_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, From a41f59deb3aa87d56ae0b907b624a9a7987f0b6d Mon Sep 17 00:00:00 2001 From: Valentin Haenel Date: Thu, 28 Feb 2013 19:58:56 +0100 Subject: [PATCH 0392/2237] tests for keyword arg init_repository --- test/test_repository.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/test_repository.py b/test/test_repository.py index 10c98854d..c874a00e1 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -234,6 +234,29 @@ def test_new_repo(self): assert os.path.exists(os.path.join(self._temp_dir, '.git')) +class InitRepositoryTest(utils.NoRepoTestCase): + # under the assumption that repo.is_bare works + + def test_no_arg(self): + repo = init_repository(self._temp_dir) + self.assertFalse(repo.is_bare) + + def test_pos_arg_false(self): + repo = init_repository(self._temp_dir, False) + self.assertFalse(repo.is_bare) + + def test_pos_arg_true(self): + repo = init_repository(self._temp_dir, True) + self.assertTrue(repo.is_bare) + + def test_keyword_arg_false(self): + repo = init_repository(self._temp_dir, bare=False) + self.assertFalse(repo.is_bare) + + def test_keyword_arg_true(self): + repo = init_repository(self._temp_dir, bare=True) + self.assertTrue(repo.is_bare) + class DiscoverRepositoryTest(utils.NoRepoTestCase): def test_discover_repo(self): repo = init_repository(self._temp_dir, False) From db02a2dbb62abe67b0d3c357e3ef325d5665860f Mon Sep 17 00:00:00 2001 From: Valentin Haenel Date: Thu, 28 Feb 2013 20:04:41 +0100 Subject: [PATCH 0393/2237] document new ways to init a repo --- docs/repository.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/repository.rst b/docs/repository.rst index 36d64ca36..a38f052ff 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -7,9 +7,17 @@ The repository This is how to create non-bare repository:: >>> from pygit2 import init_repository - >>> bare = False - >>> repo = init_repository('test', bare) + >>> repo = init_repository('test') + And this is how to create a bare repository:: + + >>> from pygit2 import init_repository + >>> repo = init_repository('test', bare=True) + + But one can also do:: + + >>> from pygit2 import init_repository + >>> repo = init_repository('test', True) .. autofunction:: pygit2.discover_repository From ce5b884bbeb9e1b623df3e26d8256f90a4a852f0 Mon Sep 17 00:00:00 2001 From: Valentin Haenel Date: Thu, 28 Feb 2013 21:02:05 +0100 Subject: [PATCH 0394/2237] fix syntax hightlighting in docs When using comments in a pycon session, comments must be prefixed with standard console prompt (>>>) for pygments syntax highlighting to work. --- docs/objects.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index 88578b31e..23369de80 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -10,10 +10,10 @@ In the first place Git is a key-value storage system. The values stored are called *objects*, there are four types (commits, trees, blobs and tags), for each type pygit2 has a Python class:: - # Get the last commit + >>> # Get the last commit >>> head = repo.head - # Show commits and trees + >>> # Show commits and trees >>> commit >>> commit.tree @@ -92,12 +92,12 @@ directory in a file system. Each entry points to another tree or a blob. A tree can be iterated, and partially implements the sequence and mapping interfaces:: - # Number of entries + >>> # Number of entries >>> tree = commit.tree >>> len(tree) 6 - # Iteration + >>> # Iteration >>> for entry in tree: ... print(entry.hex, entry.name) ... @@ -108,12 +108,12 @@ interfaces:: 85a67270a49ef16cdd3d328f06a3e4b459f09b27 setup.py 3d8985bbec338eb4d47c5b01b863ee89d044bd53 test - # Get an entry by name + >>> # Get an entry by name >>> entry = tree['pygit2.c'] >>> entry - # Get the object the entry points to + >>> # Get the object the entry points to >>> blob = repo[entry.oid] >>> blob @@ -143,7 +143,7 @@ Blobs A blob is equivalent to a file in a file system.:: - # create a blob out of memory + >>> # create a blob out of memory >>> oid = repo.create_blob('foo bar') >>> blob = repo[oid] From d937da9c8a7e8639f170aec66606e73567c4e181 Mon Sep 17 00:00:00 2001 From: Valentin Haenel Date: Thu, 28 Feb 2013 21:10:23 +0100 Subject: [PATCH 0395/2237] refactor the blob example --- docs/objects.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index 23369de80..fbbf96021 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -146,9 +146,10 @@ A blob is equivalent to a file in a file system.:: >>> # create a blob out of memory >>> oid = repo.create_blob('foo bar') >>> blob = repo[oid] - - Blob.data -- the contents of the blob, a byte string - + >>> blob.data + 'foo bar' + >>> oid + '\x96\xc9\x06um{\x91\xc4S"a|\x92\x95\xe4\xa8\rR\xd1\xc5' .. autoclass:: pygit2.Blob :members: From bdb655787fd7ea5fa260142341fcba50fd508f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 1 Mar 2013 22:11:16 +0100 Subject: [PATCH 0396/2237] Fix build after latest changes in libgit2 --- src/pygit2.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index 3bfbb324a..b14fb49c9 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -354,17 +354,11 @@ moduleinit(PyObject* m) GIT_DIFF_FIND_AND_BREAK_REWRITES); /* Flags for diffed files */ - PyModule_AddIntConstant(m, "GIT_DIFF_FILE_VALID_OID", - GIT_DIFF_FILE_VALID_OID); - PyModule_AddIntConstant(m, "GIT_DIFF_FILE_FREE_PATH", - GIT_DIFF_FILE_FREE_PATH); - PyModule_AddIntConstant(m, "GIT_DIFF_FILE_BINARY", GIT_DIFF_FILE_BINARY); - PyModule_AddIntConstant(m, "GIT_DIFF_FILE_NOT_BINARY", - GIT_DIFF_FILE_NOT_BINARY); - PyModule_AddIntConstant(m, "GIT_DIFF_FILE_FREE_DATA", - GIT_DIFF_FILE_FREE_DATA); - PyModule_AddIntConstant(m, "GIT_DIFF_FILE_UNMAP_DATA", - GIT_DIFF_FILE_UNMAP_DATA); + PyModule_AddIntConstant(m, "GIT_DIFF_FLAG_BINARY", GIT_DIFF_FLAG_BINARY); + PyModule_AddIntConstant(m, "GIT_DIFF_FLAG_NOT_BINARY", + GIT_DIFF_FLAG_NOT_BINARY); + PyModule_AddIntConstant(m, "GIT_DIFF_FLAG_VALID_OID", + GIT_DIFF_FLAG_VALID_OID); /* Flags for diff deltas */ PyModule_AddIntConstant(m, "GIT_DELTA_UNMODIFIED", GIT_DELTA_UNMODIFIED); From a96d49474715220477d06a18be30e386d0e215eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 2 Mar 2013 11:40:42 +0100 Subject: [PATCH 0397/2237] Coding style Including: - Lines longer than 79 chars - Spaces at the end of line - Wrong indentation - Comma not followed by an space - C++ style comments: // --- docs/install.rst | 7 ++-- docs/objects.rst | 11 +----- include/pygit2/repository.h | 8 +++-- include/pygit2/signature.h | 5 ++- include/pygit2/types.h | 4 ++- setup.py | 16 +++++---- src/config.c | 26 +++++++------- src/diff.c | 20 +++++------ src/error.c | 22 ++++++------ src/index.c | 4 +-- src/pygit2.c | 67 +++++++++++++++++++------------------ src/reference.c | 7 ++-- src/remote.c | 54 +++++++++++++++--------------- src/repository.c | 27 +++++++-------- src/tree.c | 2 +- src/utils.c | 4 +-- test/test_blob.py | 2 -- test/test_config.py | 13 +++---- test/test_diff.py | 56 +++++++++++++++++-------------- test/test_refs.py | 11 +++--- test/test_remote.py | 4 +-- test/test_repository.py | 6 ++-- test/test_revwalk.py | 22 ++++++------ test/test_signature.py | 4 +-- test/test_status.py | 44 ++++++++++++------------ test/utils.py | 3 +- 26 files changed, 228 insertions(+), 221 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index 8bf6bcd25..aacad2f58 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -6,12 +6,13 @@ How to Install .. contents:: -First you need to install the latest version of libgit2. -You can find platform-specific instructions to build the library in the libgit2 website: +First you need to install the latest version of libgit2. You can find +platform-specific instructions to build the library in the libgit2 website: http://libgit2.github.com -Also, make sure you have Python 2.6+ installed together with the Python development headers. +Also, make sure you have Python 2.6+ installed together with the Python +development headers. When those are installed, you can install pygit2:: diff --git a/docs/objects.rst b/docs/objects.rst index fbbf96021..3ceb31734 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -118,22 +118,13 @@ interfaces:: >>> blob -This is the interface of a tree entry:: - - TreeEntry.name -- name of the tree entry - TreeEntry.oid -- the id of the git object - TreeEntry.hex -- hexadecimal representation of the oid - TreeEntry.filemode -- the Unix file attributes - TreeEntry.to_object() -- returns the git object (equivalent to repo[entry.oid]) - - .. autoclass:: pygit2.Tree :members: :show-inheritance: :undoc-members: .. autoclass:: pygit2.TreeEntry - :members: + :members: name, oid, hex, filemode, to_object :show-inheritance: :undoc-members: diff --git a/include/pygit2/repository.h b/include/pygit2/repository.h index 0f6616afa..2a35a43d4 100644 --- a/include/pygit2/repository.h +++ b/include/pygit2/repository.h @@ -38,7 +38,8 @@ int Repository_traverse(Repository *self, visitproc visit, void *arg); int Repository_clear(Repository *self); int Repository_contains(Repository *self, PyObject *value); -git_odb_object* Repository_read_raw(git_repository *repo, const git_oid *oid, size_t len); +git_odb_object* +Repository_read_raw(git_repository *repo, const git_oid *oid, size_t len); PyObject* Repository_head(Repository *self); PyObject* Repository_getitem(Repository *self, PyObject *value); @@ -55,7 +56,10 @@ PyObject* Repository_create_commit(Repository *self, PyObject *args); PyObject* Repository_create_tag(Repository *self, PyObject *args); PyObject* Repository_listall_references(Repository *self, PyObject *args); PyObject* Repository_lookup_reference(Repository *self, PyObject *py_name); -PyObject* Repository_create_reference(Repository *self, PyObject *args, PyObject* keywds); + +PyObject* +Repository_create_reference(Repository *self, PyObject *args, PyObject* kw); + PyObject* Repository_packall_references(Repository *self, PyObject *args); PyObject* Repository_status(Repository *self, PyObject *args); PyObject* Repository_status_file(Repository *self, PyObject *value); diff --git a/include/pygit2/signature.h b/include/pygit2/signature.h index 10fbe7e9b..5c705b81b 100644 --- a/include/pygit2/signature.h +++ b/include/pygit2/signature.h @@ -40,6 +40,9 @@ PyObject* Signature_get_name(Signature *self); PyObject* Signature_get_email(Signature *self); PyObject* Signature_get_time(Signature *self); PyObject* Signature_get_offset(Signature *self); -PyObject* build_signature(Object *obj, const git_signature *signature, const char *encoding); + +PyObject* +build_signature(Object *obj, const git_signature *signature, + const char *encoding); #endif diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 834d6a85e..735f5d819 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -137,8 +137,10 @@ typedef struct { } Signature; -PyObject* lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, +PyObject* +lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, git_otype type); + PyObject* lookup_object(Repository *repo, const git_oid *oid, git_otype type); #endif diff --git a/setup.py b/setup.py index 160d65d0d..214b9ceb1 100644 --- a/setup.py +++ b/setup.py @@ -90,13 +90,15 @@ def run(self): import shlex import unittest test_argv0 = [sys.argv[0] + ' test --args='] - #For transfering args to unittest, we have to split args - #by ourself, so that command like: - #python setup.py test --args="-v -f" - #can be executed, and the parameter '-v -f' can be - #transfering to unittest properly. + # For transfering args to unittest, we have to split args by ourself, + # so that command like: + # + # python setup.py test --args="-v -f" + # + # can be executed, and the parameter '-v -f' can be transfering to + # unittest properly. test_argv = test_argv0 + shlex.split(self.args) - unittest.main(module=None, defaultTest='test.test_suite', argv=test_argv) + unittest.main(None, defaultTest='test.test_suite', argv=test_argv) class BuildWithDLLs(build): @@ -112,7 +114,7 @@ def _get_dlls(self): libgit2_dlls.append('git2.dll') elif compiler_type == 'mingw32': libgit2_dlls.append('libgit2.dll') - look_dirs = [libgit2_bin] + os.environ.get("PATH","").split(os.pathsep) + look_dirs = [libgit2_bin] + os.getenv("PATH", "").split(os.pathsep) target = os.path.abspath(self.build_lib) for bin in libgit2_dlls: for look in look_dirs: diff --git a/src/config.c b/src/config.c index 687acbde7..c4c2604ee 100644 --- a/src/config.c +++ b/src/config.c @@ -152,7 +152,7 @@ Config_contains(Config *self, PyObject *py_key) { const char *c_value; char *c_key; - c_key = py_str_to_c_str(py_key,NULL); + c_key = py_str_to_c_str(py_key, NULL); if (c_key == NULL) return -1; @@ -177,7 +177,7 @@ Config_getitem(Config *self, PyObject *py_key) const char *c_charvalue; char *c_key; - if (!(c_key = py_str_to_c_str(py_key,NULL))) + if (!(c_key = py_str_to_c_str(py_key, NULL))) return NULL; err = git_config_get_int64(&c_intvalue, self->config, c_key); @@ -212,7 +212,7 @@ Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) char *c_key; char *py_str; - if (!(c_key = py_str_to_c_str(py_key,NULL))) + if (!(c_key = py_str_to_c_str(py_key, NULL))) return -1; if (!py_value) { @@ -225,7 +225,7 @@ Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) (int64_t)PyInt_AsLong(py_value)); } else { py_value = PyObject_Str(py_value); - py_str = py_str_to_c_str(py_value,NULL); + py_str = py_str_to_c_str(py_value, NULL); err = git_config_set_string(self->config, c_key, py_str); free(py_str); } @@ -257,7 +257,7 @@ Config_foreach_callback_wrapper(const git_config_entry *entry, void *c_payload) if (!args) return -1; - if (!(py_result = PyObject_CallObject(py_callback,args))) + if (!(py_result = PyObject_CallObject(py_callback, args))) return -1; if ((c_result = PyLong_AsLong(py_result) == -1)) @@ -415,14 +415,14 @@ PyMethodDef Config_methods[] = { }; PySequenceMethods Config_as_sequence = { - 0, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - 0, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - (objobjproc)Config_contains,/* sq_contains */ + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)Config_contains, /* sq_contains */ }; PyMappingMethods Config_as_mapping = { diff --git a/src/diff.c b/src/diff.c index 581388e69..4b3732e6b 100644 --- a/src/diff.c +++ b/src/diff.c @@ -54,12 +54,12 @@ static int diff_data_cb( hunks = PyDict_GetItemString(cb_data, "hunks"); if (hunks == NULL) - return -1; + return -1; size = PyList_Size(hunks); hunk = (Hunk *)PyList_GetItem(hunks, size - 1); if (hunk == NULL) - return -1; + return -1; data = Py_BuildValue("(s#,i)", content, content_len, @@ -149,11 +149,10 @@ static int diff_hunk_cb( hunk->new_file = ""; } - if (hunk->data == NULL) { - hunk->data = PyList_New(0); - } + if (hunk->data == NULL) + hunk->data = PyList_New(0); - if(PyList_Append(hunks, (PyObject *)hunk) == 0) { + if (PyList_Append(hunks, (PyObject *)hunk) == 0) { Py_DECREF(hunk); } else { @@ -168,10 +167,10 @@ diff_file_cb(const git_diff_delta *delta, float progress, void *cb_data) { PyObject *files, *file; - if(delta->old_file.path != NULL && delta->new_file.path != NULL) { + if (delta->old_file.path != NULL && delta->new_file.path != NULL) { files = PyDict_GetItemString(cb_data, "files"); - if(files == NULL) { + if (files == NULL) { files = PyList_New(0); PyDict_SetItemString(cb_data, "files", files); Py_DECREF(files); @@ -184,10 +183,9 @@ diff_file_cb(const git_diff_delta *delta, float progress, void *cb_data) delta->similarity ); - if (PyList_Append(files, file) == 0) { - // If success + /* If success */ + if (PyList_Append(files, file) == 0) Py_DECREF(file); - } } return 0; diff --git a/src/error.c b/src/error.c index 6514b6a9d..ce1a053ce 100644 --- a/src/error.c +++ b/src/error.c @@ -32,40 +32,40 @@ PyObject *GitError; PyObject * Error_type(int type) { const git_error* error; - // Expected + /* Expected */ switch (type) { - /** Input does not exist in the scope searched. */ + /* Input does not exist in the scope searched. */ case GIT_ENOTFOUND: return PyExc_KeyError; - /** A reference with this name already exists */ + /* A reference with this name already exists */ case GIT_EEXISTS: return PyExc_ValueError; - /** The given short oid is ambiguous */ + /* The given short oid is ambiguous */ case GIT_EAMBIGUOUS: return PyExc_ValueError; - /** The buffer is too short to satisfy the request */ + /* The buffer is too short to satisfy the request */ case GIT_EBUFS: return PyExc_ValueError; - /** Invalid input spec */ + /* Invalid input spec */ case GIT_EINVALIDSPEC: return PyExc_ValueError; - /** Skip and passthrough the given ODB backend */ + /* Skip and passthrough the given ODB backend */ case GIT_PASSTHROUGH: return GitError; - /** No entries left in ref walker */ + /* No entries left in ref walker */ case GIT_ITEROVER: return PyExc_StopIteration; } - // Critical + /* Critical */ error = giterr_last(); - if(error != NULL) { + if (error != NULL) { switch (error->klass) { case GITERR_NOMEMORY: return PyExc_MemoryError; @@ -107,7 +107,7 @@ PyObject* Error_set_str(int err, const char *str) } error = giterr_last(); - if (error == NULL) //expected error - no error msg set + if (error == NULL) /* Expected error - no error msg set */ return PyErr_Format(Error_type(err), "%s", str); return PyErr_Format(Error_type(err), "%s: %s", str, error->message); diff --git a/src/index.c b/src/index.c index 1a6d3ac34..410c460be 100644 --- a/src/index.c +++ b/src/index.c @@ -371,8 +371,8 @@ Index_setitem(Index *self, PyObject *key, PyObject *value) return -1; } - if(Index_remove(self, Py_BuildValue("(N)", key)) == NULL) - return -1; + if (Index_remove(self, Py_BuildValue("(N)", key)) == NULL) + return -1; return 0; } diff --git a/src/pygit2.c b/src/pygit2.c index 54860b908..dee0e6445 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -168,7 +168,8 @@ hash(PyObject *self, PyObject *args) PyMethodDef module_methods[] = { - {"init_repository", init_repository, METH_VARARGS|METH_KEYWORDS, init_repository__doc__}, + {"init_repository", init_repository, METH_VARARGS|METH_KEYWORDS, + init_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, @@ -337,19 +338,19 @@ moduleinit(PyObject* m) GIT_DIFF_RECURSE_UNTRACKED_DIRS); /* Flags for diff find similar */ - // --find-renames + /* --find-renames */ PyModule_AddIntConstant(m, "GIT_DIFF_FIND_RENAMES", GIT_DIFF_FIND_RENAMES); - // --break-rewrites=N + /* --break-rewrites=N */ PyModule_AddIntConstant(m, "GIT_DIFF_FIND_RENAMES_FROM_REWRITES", GIT_DIFF_FIND_RENAMES_FROM_REWRITES); - // --find-copies + /* --find-copies */ PyModule_AddIntConstant(m, "GIT_DIFF_FIND_COPIES", GIT_DIFF_FIND_COPIES); - // --find-copies-harder + /* --find-copies-harder */ PyModule_AddIntConstant(m, "GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED", GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED); - // --break-rewrites=/M + /* --break-rewrites=/M */ PyModule_AddIntConstant(m, "GIT_DIFF_FIND_AND_BREAK_REWRITES", GIT_DIFF_FIND_AND_BREAK_REWRITES); @@ -427,32 +428,32 @@ moduleinit(PyObject* m) #if PY_MAJOR_VERSION < 3 - PyMODINIT_FUNC - init_pygit2(void) - { - PyObject* m; - m = Py_InitModule3("_pygit2", module_methods, - "Python bindings for libgit2."); - moduleinit(m); - } + PyMODINIT_FUNC + init_pygit2(void) + { + PyObject* m; + m = Py_InitModule3("_pygit2", module_methods, + "Python bindings for libgit2."); + moduleinit(m); + } #else - struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "_pygit2", /* m_name */ - "Python bindings for libgit2.", /* m_doc */ - -1, /* m_size */ - module_methods, /* m_methods */ - NULL, /* m_reload */ - NULL, /* m_traverse */ - NULL, /* m_clear */ - NULL, /* m_free */ - }; - - PyMODINIT_FUNC - PyInit__pygit2(void) - { - PyObject* m; - m = PyModule_Create(&moduledef); - return moduleinit(m); - } + struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_pygit2", /* m_name */ + "Python bindings for libgit2.", /* m_doc */ + -1, /* m_size */ + module_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ + }; + + PyMODINIT_FUNC + PyInit__pygit2(void) + { + PyObject* m; + m = PyModule_Create(&moduledef); + return moduleinit(m); + } #endif diff --git a/src/reference.c b/src/reference.c index 7695540ab..a1d9ca906 100644 --- a/src/reference.c +++ b/src/reference.c @@ -74,10 +74,9 @@ PyObject* RefLogIter_iternext(PyObject *self) git_reflog_entry_committer(entry) ); - if(signature != NULL) - py_entry->committer = build_signature( - (Object*)py_entry, signature, "utf-8" - ); + if (signature != NULL) + py_entry->committer = build_signature( + (Object*)py_entry, signature, "utf-8"); ++(p->i); diff --git a/src/remote.c b/src/remote.c index b7a6f081a..0baae3e87 100644 --- a/src/remote.c +++ b/src/remote.c @@ -156,8 +156,8 @@ Remote_fetchspec__set__(Remote *self, PyObject* py_tuple) if (!PyArg_ParseTuple(py_tuple, "ss", &src, &dst)) return -1; - // length is strlen('+' + src + ':' + dst) and Null-Byte - length = strlen(src) + strlen(dst) + 3; + /* length is strlen('+' + src + ':' + dst) and Null-Byte */ + length = strlen(src) + strlen(dst) + 3; buf = (char*) calloc(length, sizeof(char)); if (buf != NULL) { sprintf(buf, "+%s:%s", src, dst); @@ -165,7 +165,7 @@ Remote_fetchspec__set__(Remote *self, PyObject* py_tuple) free(buf); if (err == GIT_OK) - return 0; + return 0; Error_set_exc(PyExc_ValueError); } @@ -184,29 +184,29 @@ PyDoc_STRVAR(Remote_fetch__doc__, PyObject * Remote_fetch(Remote *self, PyObject *args) { - PyObject* py_stats = NULL; - const git_transfer_progress *stats; - int err; - - err = git_remote_connect(self->remote, GIT_DIRECTION_FETCH); - if (err == GIT_OK) { - err = git_remote_download(self->remote, NULL, NULL); - if (err == GIT_OK) { - stats = git_remote_stats(self->remote); - py_stats = Py_BuildValue("{s:I,s:I,s:n}", - "indexed_objects", stats->indexed_objects, - "received_objects", stats->received_objects, - "received_bytes", stats->received_bytes); - - err = git_remote_update_tips(self->remote); - } - git_remote_disconnect(self->remote); - } - - if (err < 0) - return Error_set(err); - - return (PyObject*) py_stats; + PyObject* py_stats = NULL; + const git_transfer_progress *stats; + int err; + + err = git_remote_connect(self->remote, GIT_DIRECTION_FETCH); + if (err == GIT_OK) { + err = git_remote_download(self->remote, NULL, NULL); + if (err == GIT_OK) { + stats = git_remote_stats(self->remote); + py_stats = Py_BuildValue("{s:I,s:I,s:n}", + "indexed_objects", stats->indexed_objects, + "received_objects", stats->received_objects, + "received_bytes", stats->received_bytes); + + err = git_remote_update_tips(self->remote); + } + git_remote_disconnect(self->remote); + } + + if (err < 0) + return Error_set(err); + + return (PyObject*) py_stats; } @@ -239,7 +239,7 @@ PyTypeObject RemoteType = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc) Remote_call, /* tp_call */ + (ternaryfunc) Remote_call, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ diff --git a/src/repository.c b/src/repository.c index b187ab6c3..7c2784b61 100644 --- a/src/repository.c +++ b/src/repository.c @@ -209,8 +209,8 @@ Repository_head__get__(Repository *self) int err; err = git_repository_head(&head, self->repo); - if(err < 0) { - if(err == GIT_ENOTFOUND) + if (err < 0) { + if (err == GIT_ENOTFOUND) PyErr_SetString(GitError, "head reference does not exist"); else Error_set(err); @@ -856,20 +856,20 @@ Repository_create_reference(Repository *self, PyObject *args, PyObject *kw) &c_name, &py_obj, &force, &symbolic)) return NULL; - if(!symbolic) { + if (!symbolic) { err = py_str_to_git_oid_expand(self->repo, py_obj, &oid); - if (err < 0) { + if (err < 0) return Error_set(err); - } - err = git_reference_create(&c_reference, self->repo, c_name, &oid, force); + err = git_reference_create(&c_reference, self->repo, c_name, &oid, + force); } else { #if PY_MAJOR_VERSION == 2 c_target = PyString_AsString(py_obj); #else c_target = PyString_AsString(PyUnicode_AsASCIIString(py_obj)); #endif - if(c_target == NULL) + if (c_target == NULL) return NULL; err = git_reference_symbolic_create(&c_reference, self->repo, c_name, @@ -981,7 +981,7 @@ Repository_TreeBuilder(Repository *self, PyObject *args) if (PyObject_TypeCheck(py_src, &TreeType)) { Tree *py_tree = (Tree *)py_src; if (py_tree->repo->repo != self->repo) { - //return Error_set(GIT_EINVALIDARGS); + /* return Error_set(GIT_EINVALIDARGS); */ return Error_set(GIT_ERROR); } tree = py_tree->tree; @@ -998,9 +998,8 @@ Repository_TreeBuilder(Repository *self, PyObject *args) } err = git_treebuilder_create(&bld, tree); - if (must_free != NULL) { + if (must_free != NULL) git_tree_free(must_free); - } if (err < 0) return Error_set(err); @@ -1090,10 +1089,10 @@ Repository_checkout(Repository *self, PyObject *args, PyObject *kw) &strategy, &ReferenceType, &ref, &head)) return NULL; - if (ref != NULL) { // checkout from treeish + if (ref != NULL) { /* checkout from treeish */ id = git_reference_target(ref->reference); err = git_object_lookup(&object, self->repo, id, GIT_OBJ_COMMIT); - if(err == GIT_OK) { + if (err == GIT_OK) { opts.checkout_strategy = strategy; err = git_checkout_tree(self->repo, object, &opts); if (err == GIT_OK) { @@ -1101,13 +1100,13 @@ Repository_checkout(Repository *self, PyObject *args, PyObject *kw) git_reference_name(ref->reference)); } } - } else { // checkout from head / index + } else { /* checkout from head / index */ opts.checkout_strategy = strategy; err = (!head) ? git_checkout_index(self->repo, NULL, &opts) : git_checkout_head(self->repo, &opts); } - if(err < 0) + if (err < 0) return Error_set(err); Py_RETURN_NONE; diff --git a/src/tree.c b/src/tree.c index 6514ecdb2..7e1bc0d64 100644 --- a/src/tree.c +++ b/src/tree.c @@ -282,7 +282,7 @@ Tree_getitem(Tree *self, PyObject *value) if (err < 0) return (TreeEntry*)Error_set(err); - // git_tree_entry_dup is already done in git_tree_entry_bypath + /* git_tree_entry_dup is already done in git_tree_entry_bypath */ return wrap_tree_entry(entry, self); } diff --git a/src/utils.c b/src/utils.c index 9aa46ef9c..8ce23cbc4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -32,8 +32,8 @@ extern PyTypeObject ReferenceType; -// py_str_to_c_str() returns a newly allocated C string holding -// the string contained in the value argument. +/* py_str_to_c_str() returns a newly allocated C string holding + * the string contained in the value argument. */ char * py_str_to_c_str(PyObject *value, const char *encoding) { /* Case 1: byte string */ diff --git a/test/test_blob.py b/test/test_blob.py index 133ea7b79..c6ded5353 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -30,8 +30,6 @@ from __future__ import absolute_import from __future__ import unicode_literals import unittest -import tempfile -import os import pygit2 from . import utils diff --git a/test/test_config.py b/test/test_config.py index d5981ae43..e5b5acced 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -78,7 +78,7 @@ def test_new(self): self.assertFalse(config_read['core.bare']) self.assertTrue('core.editor' in config_read) self.assertEqual(config_read['core.editor'], 'ed') - + def test_add(self): config = pygit2.Config() @@ -98,9 +98,10 @@ def test_read(self): self.assertRaises(TypeError, lambda: config[()]) self.assertRaises(TypeError, lambda: config[-4]) - self.assertRaisesWithArg(ValueError, - "Invalid config item name 'abc'", lambda: config['abc']) - self.assertRaisesWithArg(KeyError, 'abc.def', lambda: config['abc.def']) + self.assertRaisesWithArg(ValueError, "Invalid config item name 'abc'", + lambda: config['abc']) + self.assertRaisesWithArg(KeyError, 'abc.def', + lambda: config['abc.def']) self.assertTrue('core.bare' in config) self.assertFalse(config['core.bare']) @@ -117,7 +118,7 @@ def test_read(self): self.assertTrue('this.that' in config) self.assertEqual(len(config.get_multivar('this.that')), 2) l = config.get_multivar('this.that', 'bar') - self.assertEqual(len(l),1) + self.assertEqual(len(l), 1) self.assertEqual(l[0], 'foobar') def test_write(self): @@ -156,7 +157,7 @@ def test_write(self): self.assertTrue('this.that' in config) config.set_multivar('this.that', '^.*beer', 'fool') l = config.get_multivar('this.that', 'fool') - self.assertEqual(len(l),1) + self.assertEqual(len(l), 1) self.assertEqual(l[0], 'fool') config.set_multivar('this.that', 'foo.*', '123456') l = config.get_multivar('this.that', 'foo.*') diff --git a/test/test_diff.py b/test/test_diff.py index e79ddf29f..106ac62da 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -31,6 +31,7 @@ from __future__ import unicode_literals import unittest import pygit2 +from pygit2 import GIT_DIFF_INCLUDE_UNMODIFIED from . import utils @@ -57,26 +58,26 @@ """ DIFF_INDEX_EXPECTED = [ - 'staged_changes', - 'staged_changes_file_deleted', - 'staged_changes_file_modified', - 'staged_delete', - 'staged_delete_file_modified', - 'staged_new', - 'staged_new_file_deleted', - 'staged_new_file_modified' + 'staged_changes', + 'staged_changes_file_deleted', + 'staged_changes_file_modified', + 'staged_delete', + 'staged_delete_file_modified', + 'staged_new', + 'staged_new_file_deleted', + 'staged_new_file_modified' ] DIFF_WORKDIR_EXPECTED = [ - 'file_deleted', - 'modified_file', - 'staged_changes', - 'staged_changes_file_deleted', - 'staged_changes_file_modified', - 'staged_delete', - 'staged_delete_file_modified', - 'subdir/deleted_file', - 'subdir/modified_file' + 'file_deleted', + 'modified_file', + 'staged_changes', + 'staged_changes_file_deleted', + 'staged_changes_file_modified', + 'staged_delete', + 'staged_delete_file_modified', + 'subdir/deleted_file', + 'subdir/modified_file' ] class DiffDirtyTest(utils.DirtyRepoTestCase): @@ -120,7 +121,7 @@ def test_diff_tree(self): # self.assertIsNotNone is 2.7 only self.assertTrue(diff is not None) # self.assertIn is 2.7 only - self.assertTrue(('a','a', 3, 0) in diff.changes['files']) + self.assertTrue(('a', 'a', 3, 0) in diff.changes['files']) self.assertEqual(2, len(diff.changes['hunks'])) hunk = diff.changes['hunks'][0] @@ -140,7 +141,7 @@ def test_diff_tree_opts(self): commit_d = self.repo[COMMIT_SHA1_4] for opt in [pygit2.GIT_DIFF_IGNORE_WHITESPACE, - pygit2.GIT_DIFF_IGNORE_WHITESPACE_EOL]: + pygit2.GIT_DIFF_IGNORE_WHITESPACE_EOL]: diff = commit_c.tree.diff(commit_d.tree, opt) self.assertTrue(diff is not None) self.assertEqual(0, len(diff.changes.get('hunks', list()))) @@ -163,13 +164,13 @@ def test_diff_merge(self): self.assertTrue(diff_c is not None) # assertIn / assertNotIn are 2.7 only - self.assertTrue(('b','b', 3, 0) not in diff_b.changes['files']) - self.assertTrue(('b','b', 3, 0) in diff_c.changes['files']) + self.assertTrue(('b', 'b', 3, 0) not in diff_b.changes['files']) + self.assertTrue(('b', 'b', 3, 0) in diff_c.changes['files']) diff_b.merge(diff_c) # assertIn is 2.7 only - self.assertTrue(('b','b', 3, 0) in diff_b.changes['files']) + self.assertTrue(('b', 'b', 3, 0) in diff_b.changes['files']) hunk = diff_b.changes['hunks'][1] self.assertEqual(hunk.old_start, 1) @@ -201,16 +202,19 @@ def test_diff_oids(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] diff = commit_a.tree.diff(commit_b.tree) - self.assertEqual(diff.changes['hunks'][0].old_oid, '7f129fd57e31e935c6d60a0c794efe4e6927664b') - self.assertEqual(diff.changes['hunks'][0].new_oid, 'af431f20fc541ed6d5afede3e2dc7160f6f01f16') + hunk = diff.changes['hunks'][0] + self.assertEqual(hunk.old_oid, + '7f129fd57e31e935c6d60a0c794efe4e6927664b') + self.assertEqual(hunk.new_oid, + 'af431f20fc541ed6d5afede3e2dc7160f6f01f16') def test_find_similar(self): commit_a = self.repo[COMMIT_SHA1_4] commit_b = self.repo[COMMIT_SHA1_5] - + #~ Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate #~ --find-copies-harder during rename transformion... - diff = commit_a.tree.diff(commit_b.tree, pygit2.GIT_DIFF_INCLUDE_UNMODIFIED) + diff = commit_a.tree.diff(commit_b.tree, GIT_DIFF_INCLUDE_UNMODIFIED) self.assertFalse(('a', 'a.copy', 5, 100) in diff.changes['files']) diff.find_similar(pygit2.GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED) self.assertTrue(('a', 'a.copy', 5, 100) in diff.changes['files']) diff --git a/test/test_refs.py b/test/test_refs.py index 082e53df1..803a901cf 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -49,7 +49,8 @@ def test_list_all_references(self): ['refs/heads/i18n', 'refs/heads/master']) # We add a symbolic reference - repo.create_reference('refs/tags/version1','refs/heads/master', symbolic=True) + repo.create_reference('refs/tags/version1', 'refs/heads/master', + symbolic=True) self.assertEqual(sorted(repo.listall_references()), ['refs/heads/i18n', 'refs/heads/master', 'refs/tags/version1']) @@ -144,8 +145,9 @@ def test_rename(self): def test_reload(self): name = 'refs/tags/version1' - ref = self.repo.create_reference(name, "refs/heads/master", symbolic=True) - ref2 = self.repo.lookup_reference(name) + repo = self.repo + ref = repo.create_reference(name, "refs/heads/master", symbolic=True) + ref2 = repo.lookup_reference(name) ref.delete() self.assertEqual(ref2.name, name) self.assertRaises(KeyError, ref2.reload) @@ -197,7 +199,8 @@ def test_create_symbolic_reference(self): # try to create existing symbolic reference self.assertRaises(ValueError, self.repo.create_reference, - 'refs/tags/beta','refs/heads/master', symbolic=True) + 'refs/tags/beta', 'refs/heads/master', + symbolic=True) # try to create existing symbolic reference with force reference = self.repo.create_reference('refs/tags/beta', diff --git a/test/test_remote.py b/test/test_remote.py index 0a156c4d9..6c66ea13b 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -49,7 +49,7 @@ def test_remote_create(self): self.assertEqual(name, remote.name) self.assertEqual(url, remote.url) - self.assertRaises(ValueError,self.repo.create_remote, *(name, url)) + self.assertRaises(ValueError, self.repo.create_remote, *(name, url)) def test_remote_rename(self): @@ -79,7 +79,7 @@ def test_remote_fetchspec(self): self.assertEqual(REMOTE_FETCHSPEC_SRC, remote.fetchspec[0]) self.assertEqual(REMOTE_FETCHSPEC_DST, remote.fetchspec[1]) - new_fetchspec = ('refs/foo/*','refs/remotes/foo/*') + new_fetchspec = ('refs/foo/*', 'refs/remotes/foo/*') remote.fetchspec = new_fetchspec self.assertEqual(new_fetchspec[0], remote.fetchspec[0]) self.assertEqual(new_fetchspec[1], remote.fetchspec[1]) diff --git a/test/test_repository.py b/test/test_repository.py index c874a00e1..484c53132 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -35,8 +35,8 @@ import os from os.path import join, realpath -from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, init_repository, \ - discover_repository, Commit, hashfile +from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT +from pygit2 import init_repository, discover_repository, Commit, hashfile import pygit2 from . import utils @@ -260,7 +260,7 @@ def test_keyword_arg_true(self): class DiscoverRepositoryTest(utils.NoRepoTestCase): def test_discover_repo(self): repo = init_repository(self._temp_dir, False) - subdir = os.path.join(self._temp_dir, "test1","test2") + subdir = os.path.join(self._temp_dir, "test1", "test2") os.makedirs(subdir) self.assertEqual(repo.path, discover_repository(subdir)) diff --git a/test/test_revwalk.py b/test/test_revwalk.py index 1b6a083e5..caab35688 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -44,23 +44,23 @@ 'acecd5ea2924a4b900e7e149496e1f4b57976e51'] REVLOGS = [ - ('Nico von Geyso','checkout: moving from i18n to master'), - ('Nico von Geyso','commit: added bye.txt and new'), - ('Nico von Geyso','checkout: moving from master to i18n'), - ('J. David Ibañez', 'merge i18n: Merge made by recursive.'), - ('J. David Ibañez', 'commit: Add .gitignore file'), - ('J. David Ibañez', 'checkout: moving from i18n to master'), - ('J. David Ibañez', 'commit: Say hello in French'), - ('J. David Ibañez', 'commit: Say hello in Spanish'), - ('J. David Ibañez', 'checkout: moving from master to i18n'), - ('J. David Ibañez', 'commit (initial): First commit') + ('Nico von Geyso', 'checkout: moving from i18n to master'), + ('Nico von Geyso', 'commit: added bye.txt and new'), + ('Nico von Geyso', 'checkout: moving from master to i18n'), + ('J. David Ibañez', 'merge i18n: Merge made by recursive.'), + ('J. David Ibañez', 'commit: Add .gitignore file'), + ('J. David Ibañez', 'checkout: moving from i18n to master'), + ('J. David Ibañez', 'commit: Say hello in French'), + ('J. David Ibañez', 'commit: Say hello in Spanish'), + ('J. David Ibañez', 'checkout: moving from master to i18n'), + ('J. David Ibañez', 'commit (initial): First commit') ] class RevlogTestTest(utils.RepoTestCase): def test_log(self): ref = self.repo.lookup_reference('HEAD') - for i,entry in enumerate(ref.log()): + for i, entry in enumerate(ref.log()): self.assertEqual(entry.committer.name, REVLOGS[i][0]) self.assertEqual(entry.message, REVLOGS[i][1]) diff --git a/test/test_signature.py b/test/test_signature.py index 2917a4971..9ee80a87c 100644 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -45,8 +45,8 @@ def test_default(self): self.assertEqual(signature.name.encode(encoding), signature._name) def test_ascii(self): - self.assertRaises( - UnicodeEncodeError, Signature, 'Foo Ibáñez', 'foo@example.com') + self.assertRaises(UnicodeEncodeError, + Signature, 'Foo Ibáñez', 'foo@example.com') def test_latin1(self): encoding = 'iso-8859-1' diff --git a/test/test_status.py b/test/test_status.py index e6780a482..73a8127af 100644 --- a/test/test_status.py +++ b/test/test_status.py @@ -36,38 +36,38 @@ EXPECTED = { - "current_file": pygit2.GIT_STATUS_CURRENT, - "file_deleted": pygit2.GIT_STATUS_WT_DELETED, - "modified_file": pygit2.GIT_STATUS_WT_MODIFIED, - "new_file": pygit2.GIT_STATUS_WT_NEW, + "current_file": pygit2.GIT_STATUS_CURRENT, + "file_deleted": pygit2.GIT_STATUS_WT_DELETED, + "modified_file": pygit2.GIT_STATUS_WT_MODIFIED, + "new_file": pygit2.GIT_STATUS_WT_NEW, - "staged_changes": pygit2.GIT_STATUS_INDEX_MODIFIED, - "staged_changes_file_deleted": pygit2.GIT_STATUS_INDEX_MODIFIED | - pygit2.GIT_STATUS_WT_DELETED, - "staged_changes_file_modified": pygit2.GIT_STATUS_INDEX_MODIFIED | - pygit2.GIT_STATUS_WT_MODIFIED, + "staged_changes": pygit2.GIT_STATUS_INDEX_MODIFIED, + "staged_changes_file_deleted": pygit2.GIT_STATUS_INDEX_MODIFIED | + pygit2.GIT_STATUS_WT_DELETED, + "staged_changes_file_modified": pygit2.GIT_STATUS_INDEX_MODIFIED | + pygit2.GIT_STATUS_WT_MODIFIED, - "staged_delete": pygit2.GIT_STATUS_INDEX_DELETED, - "staged_delete_file_modified": pygit2.GIT_STATUS_INDEX_DELETED | - pygit2.GIT_STATUS_WT_NEW, - "staged_new": pygit2.GIT_STATUS_INDEX_NEW, + "staged_delete": pygit2.GIT_STATUS_INDEX_DELETED, + "staged_delete_file_modified": pygit2.GIT_STATUS_INDEX_DELETED | + pygit2.GIT_STATUS_WT_NEW, + "staged_new": pygit2.GIT_STATUS_INDEX_NEW, - "staged_new_file_deleted": pygit2.GIT_STATUS_INDEX_NEW | - pygit2.GIT_STATUS_WT_DELETED, - "staged_new_file_modified": pygit2.GIT_STATUS_INDEX_NEW | - pygit2.GIT_STATUS_WT_MODIFIED, + "staged_new_file_deleted": pygit2.GIT_STATUS_INDEX_NEW | + pygit2.GIT_STATUS_WT_DELETED, + "staged_new_file_modified": pygit2.GIT_STATUS_INDEX_NEW | + pygit2.GIT_STATUS_WT_MODIFIED, - "subdir/current_file": pygit2.GIT_STATUS_CURRENT, - "subdir/deleted_file": pygit2.GIT_STATUS_WT_DELETED, - "subdir/modified_file": pygit2.GIT_STATUS_WT_MODIFIED, - "subdir/new_file": pygit2.GIT_STATUS_WT_NEW, + "subdir/current_file": pygit2.GIT_STATUS_CURRENT, + "subdir/deleted_file": pygit2.GIT_STATUS_WT_DELETED, + "subdir/modified_file": pygit2.GIT_STATUS_WT_MODIFIED, + "subdir/new_file": pygit2.GIT_STATUS_WT_NEW, } class StatusTest(utils.DirtyRepoTestCase): def test_status(self): """ - For every file in the status, check that the flags are correct. + For every file in the status, check that the flags are correct. """ git_status = self.repo.status() for filepath, status in git_status.items(): diff --git a/test/utils.py b/test/utils.py index 760930dfa..c3595b7ed 100644 --- a/test/utils.py +++ b/test/utils.py @@ -63,7 +63,8 @@ def rmtree(path): So we implement our own version of rmtree to address this issue. """ if os.path.exists(path): - shutil.rmtree(path, onerror=lambda func, path, e: force_rm_handle(func, path, e)) + onerror = lambda func, path, e: force_rm_handle(func, path, e) + shutil.rmtree(path, onerror=onerror) class NoRepoTestCase(unittest.TestCase): From 29ce23c0d55406277cd16d502e555c32efc2ee0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 2 Mar 2013 12:16:16 +0100 Subject: [PATCH 0398/2237] Update copyright --- .mailmap | 3 ++- README.rst | 1 + include/pygit2/blob.h | 2 +- include/pygit2/commit.h | 2 +- include/pygit2/config.h | 2 +- include/pygit2/diff.h | 2 +- include/pygit2/error.h | 2 +- include/pygit2/index.h | 2 +- include/pygit2/object.h | 2 +- include/pygit2/oid.h | 2 +- include/pygit2/reference.h | 2 +- include/pygit2/repository.h | 2 +- include/pygit2/signature.h | 2 +- include/pygit2/tag.h | 2 +- include/pygit2/tree.h | 2 +- include/pygit2/treebuilder.h | 2 +- include/pygit2/types.h | 2 +- include/pygit2/utils.h | 2 +- include/pygit2/walker.h | 2 +- pygit2/__init__.py | 2 +- pygit2/utils.py | 2 +- pygit2/version.py | 2 +- setup.py | 2 +- src/blob.c | 2 +- src/commit.c | 2 +- src/config.c | 2 +- src/diff.c | 2 +- src/error.c | 2 +- src/index.c | 2 +- src/object.c | 2 +- src/oid.c | 2 +- src/pygit2.c | 2 +- src/reference.c | 2 +- src/remote.c | 2 +- src/repository.c | 2 +- src/signature.c | 2 +- src/tag.c | 2 +- src/tree.c | 2 +- src/treebuilder.c | 2 +- src/utils.c | 2 +- src/walker.c | 2 +- test/__init__.py | 2 +- test/test_blob.py | 2 +- test/test_commit.py | 2 +- test/test_config.py | 2 +- test/test_diff.py | 2 +- test/test_index.py | 2 +- test/test_refs.py | 2 +- test/test_remote.py | 2 +- test/test_repository.py | 2 +- test/test_revwalk.py | 2 +- test/test_signature.py | 2 +- test/test_status.py | 2 +- test/test_tag.py | 2 +- test/test_tree.py | 2 +- test/test_treebuilder.py | 2 +- test/utils.py | 2 +- 57 files changed, 58 insertions(+), 56 deletions(-) diff --git a/.mailmap b/.mailmap index a38a543bd..f08f7b8db 100644 --- a/.mailmap +++ b/.mailmap @@ -1,4 +1,5 @@ J. David Ibáñez -Martin Lenders Richo Healey Xavier Delannoy +Christian Boos +Martin Lenders diff --git a/README.rst b/README.rst index 4a6764c17..e9bcb41bb 100644 --- a/README.rst +++ b/README.rst @@ -78,6 +78,7 @@ pygit2 project (sorted alphabetically): - Rui Abreu Ferreira - Sarath Lakshman - Sebastian Thiel +- Valentin Haenel - Vicent Marti - W Trevor King - Xavier Delannoy diff --git a/include/pygit2/blob.h b/include/pygit2/blob.h index a55b8c5c3..2edbb682b 100644 --- a/include/pygit2/blob.h +++ b/include/pygit2/blob.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/commit.h b/include/pygit2/commit.h index b9b907034..b94e30370 100644 --- a/include/pygit2/commit.h +++ b/include/pygit2/commit.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/config.h b/include/pygit2/config.h index 62dbc83ba..976ee3d6f 100644 --- a/include/pygit2/config.h +++ b/include/pygit2/config.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/diff.h b/include/pygit2/diff.h index 7f1d0be4d..202bff34c 100644 --- a/include/pygit2/diff.h +++ b/include/pygit2/diff.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/error.h b/include/pygit2/error.h index 7dcf58991..f487763bf 100644 --- a/include/pygit2/error.h +++ b/include/pygit2/error.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/index.h b/include/pygit2/index.h index 2082cc777..7c63c1c0e 100644 --- a/include/pygit2/index.h +++ b/include/pygit2/index.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/object.h b/include/pygit2/object.h index 64bf6128d..314c55a42 100644 --- a/include/pygit2/object.h +++ b/include/pygit2/object.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/oid.h b/include/pygit2/oid.h index 66def6974..4f22337fe 100644 --- a/include/pygit2/oid.h +++ b/include/pygit2/oid.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/reference.h b/include/pygit2/reference.h index 40e7e53cc..ff63bbf1e 100644 --- a/include/pygit2/reference.h +++ b/include/pygit2/reference.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/repository.h b/include/pygit2/repository.h index 2a35a43d4..6d2d31a1d 100644 --- a/include/pygit2/repository.h +++ b/include/pygit2/repository.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/signature.h b/include/pygit2/signature.h index 5c705b81b..fe69b984a 100644 --- a/include/pygit2/signature.h +++ b/include/pygit2/signature.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/tag.h b/include/pygit2/tag.h index 8caa6eeb5..8ac5fd7a0 100644 --- a/include/pygit2/tag.h +++ b/include/pygit2/tag.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/tree.h b/include/pygit2/tree.h index e1cea650b..0e503fdfb 100644 --- a/include/pygit2/tree.h +++ b/include/pygit2/tree.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/treebuilder.h b/include/pygit2/treebuilder.h index 982239288..322efe6a4 100644 --- a/include/pygit2/treebuilder.h +++ b/include/pygit2/treebuilder.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 735f5d819..9c8a4296d 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/utils.h b/include/pygit2/utils.h index d596b74e4..e9768b12d 100644 --- a/include/pygit2/utils.h +++ b/include/pygit2/utils.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/include/pygit2/walker.h b/include/pygit2/walker.h index 82024c711..3a86bda90 100644 --- a/include/pygit2/walker.h +++ b/include/pygit2/walker.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 6a0e24b53..b1549ce30 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/pygit2/utils.py b/pygit2/utils.py index d1909b5f4..79e60407c 100644 --- a/pygit2/utils.py +++ b/pygit2/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/pygit2/version.py b/pygit2/version.py index 4ec576460..5607b073b 100644 --- a/pygit2/version.py +++ b/pygit2/version.py @@ -1,4 +1,4 @@ -# Copyright 2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/setup.py b/setup.py index 214b9ceb1..d52e93551 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # coding: UTF-8 # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/src/blob.c b/src/blob.c index bac818432..446101b60 100644 --- a/src/blob.c +++ b/src/blob.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/commit.c b/src/commit.c index 919a92f5f..2f6dbc424 100644 --- a/src/commit.c +++ b/src/commit.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/config.c b/src/config.c index c4c2604ee..b2167b6c5 100644 --- a/src/config.c +++ b/src/config.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/diff.c b/src/diff.c index 4b3732e6b..50f3b5155 100644 --- a/src/diff.c +++ b/src/diff.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/error.c b/src/error.c index ce1a053ce..a7912fdc8 100644 --- a/src/error.c +++ b/src/error.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/index.c b/src/index.c index 410c460be..dfcb35588 100644 --- a/src/index.c +++ b/src/index.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/object.c b/src/object.c index 48667e548..40e30e4c6 100644 --- a/src/object.c +++ b/src/object.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/oid.c b/src/oid.c index e5641da8d..102424aa3 100644 --- a/src/oid.c +++ b/src/oid.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/pygit2.c b/src/pygit2.c index dee0e6445..af09d7b0d 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/reference.c b/src/reference.c index a1d9ca906..f13ed3111 100644 --- a/src/reference.c +++ b/src/reference.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/remote.c b/src/remote.c index 0baae3e87..42288e7a9 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/repository.c b/src/repository.c index 7c2784b61..a7079dedf 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/signature.c b/src/signature.c index 88f2f878d..b0e85663b 100644 --- a/src/signature.c +++ b/src/signature.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/tag.c b/src/tag.c index 5651c3b0c..ee2b329ae 100644 --- a/src/tag.c +++ b/src/tag.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/tree.c b/src/tree.c index 7e1bc0d64..0b22eee0f 100644 --- a/src/tree.c +++ b/src/tree.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/treebuilder.c b/src/treebuilder.c index 7c45f42f1..1b06b318b 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/utils.c b/src/utils.c index 8ce23cbc4..cfa9cae19 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/src/walker.c b/src/walker.c index d1b128cdf..d2481a174 100644 --- a/src/walker.c +++ b/src/walker.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 The pygit2 contributors + * Copyright 2010-2013 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, diff --git a/test/__init__.py b/test/__init__.py index 16b0d09df..44bc24ba5 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_blob.py b/test/test_blob.py index c6ded5353..221ddcc02 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_commit.py b/test/test_commit.py index 35ec65fe3..13acf982e 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_config.py b/test/test_config.py index e5b5acced..662107045 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_diff.py b/test/test_diff.py index 106ac62da..ff3a54c5b 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_index.py b/test/test_index.py index 8950cbcc0..20d98e232 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_refs.py b/test/test_refs.py index 803a901cf..0b2f61cfb 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_remote.py b/test/test_remote.py index 6c66ea13b..aacdc10bf 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_repository.py b/test/test_repository.py index 484c53132..105d1c5c9 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_revwalk.py b/test/test_revwalk.py index caab35688..dd97b8280 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_signature.py b/test/test_signature.py index 9ee80a87c..cf21b33f1 100644 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_status.py b/test/test_status.py index 73a8127af..c1b350f06 100644 --- a/test/test_status.py +++ b/test/test_status.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_tag.py b/test/test_tag.py index 884784556..de79e6027 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_tree.py b/test/test_tree.py index a88d0d1bf..f3bc6e829 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index de6e85d17..161d40072 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, diff --git a/test/utils.py b/test/utils.py index c3595b7ed..f034b946c 100644 --- a/test/utils.py +++ b/test/utils.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2012 The pygit2 contributors +# Copyright 2010-2013 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, From 9ffc14148e46d5386f95df70b6480188eb8a5644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 3 Mar 2013 14:44:42 +0100 Subject: [PATCH 0399/2237] Now we have a Repository class written in Python --- pygit2/__init__.py | 14 +++++++ pygit2/repository.py | 60 ++++++++++++++++++++++++++++ src/pygit2.c | 18 ++------- src/repository.c | 94 +++++++++++++++++++++++++++----------------- 4 files changed, 135 insertions(+), 51 deletions(-) create mode 100644 pygit2/repository.py diff --git a/pygit2/__init__.py b/pygit2/__init__.py index b1549ce30..09aa8588f 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -26,5 +26,19 @@ # Boston, MA 02110-1301, USA. from .version import __version__ + +# Low level API +import _pygit2 from _pygit2 import * + +# High level API +from repository import Repository import pygit2.utils + + +def init_repository(path, bare=False): + """ + Creates a new Git repository in the given path. + """ + _pygit2.init_repository(path, bare) + return Repository(path) diff --git a/pygit2/repository.py b/pygit2/repository.py new file mode 100644 index 000000000..4de02142d --- /dev/null +++ b/pygit2/repository.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2010-2013 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. + +# Import from pygit2 +from _pygit2 import Repository as _Repository + + +class Repository(_Repository): + + def create_reference(self, name, target, force=False, symbolic=False): + """ + Create a new reference "name" which points to a object or another + reference. + + Keyword arguments: + + force + If True references will be overridden, otherwise (the default) an + exception is raised. + + symbolic + If True a symbolic reference will be created, then source has to + be a valid existing reference name; if False (the default) a + normal reference will be created, then source must has to be a + valid SHA hash. + + Examples:: + + repo.create_reference('refs/heads/foo', repo.head.hex) + repo.create_reference('refs/tags/foo', 'refs/heads/master', + symbolic=True) + """ + if symbolic: + return self.create_symbolic_reference(name, target, force) + + return self.create_direct_reference(name, target, force) diff --git a/src/pygit2.c b/src/pygit2.c index af09d7b0d..5bfd89b82 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -62,36 +62,26 @@ extern PyTypeObject RemoteType; PyDoc_STRVAR(init_repository__doc__, - "init_repository(path, bare=False) -> Repository\n" + "init_repository(path, bare)\n" "\n" "Creates a new Git repository in the given path."); PyObject * init_repository(PyObject *self, PyObject *args, PyObject *kw) { git_repository *repo; - Repository *py_repo; const char *path; - unsigned int bare = 0; + unsigned int bare; int err; - static char * kwlist[] = {"path", "bare", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kw, "s|I", kwlist, &path, &bare)) + if (!PyArg_ParseTuple(args, "sI", &path, &bare)) return NULL; err = git_repository_init(&repo, path, bare); if (err < 0) return Error_set_str(err, path); - py_repo = PyObject_GC_New(Repository, &RepositoryType); - if (py_repo) { - py_repo->repo = repo; - py_repo->index = NULL; - PyObject_GC_Track(py_repo); - return (PyObject*)py_repo; - } - git_repository_free(repo); - return NULL; + Py_RETURN_NONE; }; diff --git a/src/repository.c b/src/repository.c index a7079dedf..1016948c0 100644 --- a/src/repository.c +++ b/src/repository.c @@ -818,64 +818,83 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) return wrap_reference(c_reference); } -PyDoc_STRVAR(Repository_create_reference__doc__, - "create_reference(name, source, force=False, symbolic=False) -> Reference\n" +PyDoc_STRVAR(Repository_create_direct_reference__doc__, + "create_reference(name, target, force) -> Reference\n" "\n" - "Create a new reference \"name\" which points to a object or another\n" - "reference.\n" + "Create a new reference \"name\" which points to an object.\n" "\n" - "Keyword arguments:\n" + "Arguments:\n" "\n" "force\n" " If True references will be overridden, otherwise (the default) an\n" " exception is raised.\n" "\n" - "symbolic\n" - " If True a symbolic reference will be created, then source has to be a\n" - " valid existing reference name; if False (the default) a normal\n" - " reference will be created, then source must has to be a valid SHA\n" - " hash.\n" - "\n" "Examples::\n" "\n" - " repo.create_reference('refs/heads/foo', repo.head.hex)\n" - " repo.create_reference('refs/tags/foo', 'refs/heads/master', symbolic=True)"); + " repo.create_direct_reference('refs/heads/foo', repo.head.hex, False)"); PyObject * -Repository_create_reference(Repository *self, PyObject *args, PyObject *kw) +Repository_create_direct_reference(Repository *self, PyObject *args, + PyObject *kw) { PyObject *py_obj; git_reference *c_reference; char *c_name, *c_target; git_oid oid; - int err = 0, symbolic = 0, force = 0; - - static char *kwlist[] = {"name", "source", "force", "symbolic", NULL}; + int err, force; - if (!PyArg_ParseTupleAndKeywords(args, kw, "sO|ii", kwlist, - &c_name, &py_obj, &force, &symbolic)) + if (!PyArg_ParseTuple(args, "sOi", &c_name, &py_obj, &force)) return NULL; - if (!symbolic) { - err = py_str_to_git_oid_expand(self->repo, py_obj, &oid); - if (err < 0) - return Error_set(err); + err = py_str_to_git_oid_expand(self->repo, py_obj, &oid); + if (err < 0) + return Error_set(err); - err = git_reference_create(&c_reference, self->repo, c_name, &oid, - force); - } else { - #if PY_MAJOR_VERSION == 2 - c_target = PyString_AsString(py_obj); - #else - c_target = PyString_AsString(PyUnicode_AsASCIIString(py_obj)); - #endif - if (c_target == NULL) - return NULL; + err = git_reference_create(&c_reference, self->repo, c_name, &oid, force); + if (err < 0) + return Error_set(err); - err = git_reference_symbolic_create(&c_reference, self->repo, c_name, - c_target, force); - } + return wrap_reference(c_reference); +} + +PyDoc_STRVAR(Repository_create_symbolic_reference__doc__, + "create_symbolic_reference(name, source, force) -> Reference\n" + "\n" + "Create a new reference \"name\" which points to another reference.\n" + "\n" + "Arguments:\n" + "\n" + "force\n" + " If True references will be overridden, otherwise (the default) an\n" + " exception is raised.\n" + "\n" + "Examples::\n" + "\n" + " repo.create_reference('refs/tags/foo', 'refs/heads/master', False)"); + +PyObject * +Repository_create_symbolic_reference(Repository *self, PyObject *args, + PyObject *kw) +{ + PyObject *py_obj; + git_reference *c_reference; + char *c_name, *c_target; + git_oid oid; + int err, force; + + if (!PyArg_ParseTuple(args, "sOi", &c_name, &py_obj, &force)) + return NULL; + + #if PY_MAJOR_VERSION == 2 + c_target = PyString_AsString(py_obj); + #else + c_target = PyString_AsString(PyUnicode_AsASCIIString(py_obj)); + #endif + if (c_target == NULL) + return NULL; + err = git_reference_symbolic_create(&c_reference, self->repo, c_name, + c_target, force); if (err < 0) return Error_set(err); @@ -1122,7 +1141,8 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, walk, METH_VARARGS), METHOD(Repository, read, METH_O), METHOD(Repository, write, METH_VARARGS), - METHOD(Repository, create_reference, METH_VARARGS|METH_KEYWORDS), + METHOD(Repository, create_direct_reference, METH_VARARGS), + METHOD(Repository, create_symbolic_reference, METH_VARARGS), METHOD(Repository, listall_references, METH_VARARGS), METHOD(Repository, lookup_reference, METH_O), METHOD(Repository, packall_references, METH_NOARGS), From e88dc49f1eaa9239053ce52d4e636baef9d75728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 3 Mar 2013 14:49:52 +0100 Subject: [PATCH 0400/2237] _pygit2.init_repository doesn't use keyword args --- src/pygit2.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index 5bfd89b82..6727e65f7 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -158,8 +158,7 @@ hash(PyObject *self, PyObject *args) PyMethodDef module_methods[] = { - {"init_repository", init_repository, METH_VARARGS|METH_KEYWORDS, - init_repository__doc__}, + {"init_repository", init_repository, METH_VARARGS, init_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, From d8b251402947e0d24e07204d2f6f623bbbfb35c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 3 Mar 2013 23:01:42 +0100 Subject: [PATCH 0401/2237] Rename low level reference creation methods The idea is to keep exactly the same name as in libgit2 --- pygit2/repository.py | 4 ++-- src/repository.c | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index 4de02142d..f73de73d6 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -55,6 +55,6 @@ def create_reference(self, name, target, force=False, symbolic=False): symbolic=True) """ if symbolic: - return self.create_symbolic_reference(name, target, force) + return self.git_reference_symbolic_create(name, target, force) - return self.create_direct_reference(name, target, force) + return self.git_reference_create(name, target, force) diff --git a/src/repository.c b/src/repository.c index 1016948c0..68e407261 100644 --- a/src/repository.c +++ b/src/repository.c @@ -818,8 +818,8 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) return wrap_reference(c_reference); } -PyDoc_STRVAR(Repository_create_direct_reference__doc__, - "create_reference(name, target, force) -> Reference\n" +PyDoc_STRVAR(Repository_git_reference_create__doc__, + "git_reference_create(name, target, force) -> Reference\n" "\n" "Create a new reference \"name\" which points to an object.\n" "\n" @@ -831,11 +831,11 @@ PyDoc_STRVAR(Repository_create_direct_reference__doc__, "\n" "Examples::\n" "\n" - " repo.create_direct_reference('refs/heads/foo', repo.head.hex, False)"); + " repo.git_reference_create('refs/heads/foo', repo.head.hex, False)"); PyObject * -Repository_create_direct_reference(Repository *self, PyObject *args, - PyObject *kw) +Repository_git_reference_create(Repository *self, PyObject *args, + PyObject *kw) { PyObject *py_obj; git_reference *c_reference; @@ -857,8 +857,8 @@ Repository_create_direct_reference(Repository *self, PyObject *args, return wrap_reference(c_reference); } -PyDoc_STRVAR(Repository_create_symbolic_reference__doc__, - "create_symbolic_reference(name, source, force) -> Reference\n" +PyDoc_STRVAR(Repository_git_reference_symbolic_create__doc__, + "git_reference_symbolic_create(name, source, force) -> Reference\n" "\n" "Create a new reference \"name\" which points to another reference.\n" "\n" @@ -870,11 +870,11 @@ PyDoc_STRVAR(Repository_create_symbolic_reference__doc__, "\n" "Examples::\n" "\n" - " repo.create_reference('refs/tags/foo', 'refs/heads/master', False)"); + " repo.git_reference_symbolic_create('refs/tags/foo', 'refs/heads/master', False)"); PyObject * -Repository_create_symbolic_reference(Repository *self, PyObject *args, - PyObject *kw) +Repository_git_reference_symbolic_create(Repository *self, PyObject *args, + PyObject *kw) { PyObject *py_obj; git_reference *c_reference; @@ -1141,8 +1141,8 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, walk, METH_VARARGS), METHOD(Repository, read, METH_O), METHOD(Repository, write, METH_VARARGS), - METHOD(Repository, create_direct_reference, METH_VARARGS), - METHOD(Repository, create_symbolic_reference, METH_VARARGS), + METHOD(Repository, git_reference_create, METH_VARARGS), + METHOD(Repository, git_reference_symbolic_create, METH_VARARGS), METHOD(Repository, listall_references, METH_VARARGS), METHOD(Repository, lookup_reference, METH_O), METHOD(Repository, packall_references, METH_NOARGS), From a19a3d2515765b1170fa42753b3bf6a853dec35e Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 4 Mar 2013 10:39:44 +0100 Subject: [PATCH 0402/2237] diff - use old fashioned iterators instead of callback based ones --- include/pygit2/types.h | 10 +- src/diff.c | 279 +++++++++++++++-------------------------- src/pygit2.c | 7 -- test/test_diff.py | 2 +- 4 files changed, 106 insertions(+), 192 deletions(-) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 9c8a4296d..7625b8982 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -76,17 +76,17 @@ typedef struct { typedef struct { PyObject_HEAD - char *header; + const char *header; int old_start; int old_lines; - PyObject *old_oid; + char* old_oid; int old_mode; - char* old_file; + const char* old_file; int new_start; int new_lines; - PyObject *new_oid; + char* new_oid; int new_mode; - char* new_file; + const char* new_file; PyObject *data; } Hunk; diff --git a/src/diff.c b/src/diff.c index 50f3b5155..7440013d0 100644 --- a/src/diff.c +++ b/src/diff.c @@ -40,204 +40,125 @@ extern PyTypeObject IndexType; extern PyTypeObject DiffType; extern PyTypeObject HunkType; -static int diff_data_cb( - const git_diff_delta *delta, - const git_diff_range *range, - char line_origin, - const char *content, - size_t content_len, - void *cb_data) -{ - PyObject *hunks, *data; - Hunk *hunk; - Py_ssize_t size; - - hunks = PyDict_GetItemString(cb_data, "hunks"); - if (hunks == NULL) - return -1; - - size = PyList_Size(hunks); - hunk = (Hunk *)PyList_GetItem(hunks, size - 1); - if (hunk == NULL) - return -1; - data = Py_BuildValue("(s#,i)", - content, content_len, - line_origin - ); - PyList_Append(hunk->data, data); - Py_DECREF(data); - - return 0; -} +PyDoc_STRVAR(Diff_changes__doc__, "Raw changes."); -static int diff_hunk_cb( - const git_diff_delta *delta, - const git_diff_range *range, - const char *header, - size_t header_len, - void *cb_data) +PyObject * +Diff_changes__get__(Diff *self) { - PyObject *hunks; - Hunk *hunk; - int len; - char* old_path = NULL, *new_path = NULL; - char oid[GIT_OID_HEXSZ]; - - - hunks = PyDict_GetItemString(cb_data, "hunks"); - if (hunks == NULL) { - hunks = PyList_New(0); - PyDict_SetItemString(cb_data, "hunks", hunks); - Py_DECREF(hunks); - } - - hunk = (Hunk*)PyType_GenericNew(&HunkType, NULL, NULL); - if (hunk == NULL) - return -1; - - hunk->old_start = range->old_start; - hunk->old_lines = range->old_lines; - hunk->new_start = range->new_start; - hunk->new_lines = range->new_lines; - - hunk->old_mode = delta->old_file.mode; - hunk->new_mode = delta->new_file.mode; - - git_oid_fmt(oid, &delta->old_file.oid); - hunk->old_oid = PyUnicode_FromStringAndSize(oid, GIT_OID_HEXSZ); - git_oid_fmt(oid, &delta->new_file.oid); - hunk->new_oid = PyUnicode_FromStringAndSize(oid, GIT_OID_HEXSZ); - - if (header) { - hunk->header = malloc(header_len+1); - - if (hunk->header == NULL) - return -1; - - memcpy(hunk->header, header, header_len); - hunk->header[header_len] = '\0'; - } - - if (delta->old_file.path != NULL) { - len = strlen(delta->old_file.path) + 1; - old_path = malloc(sizeof(char) * len); - if (old_path == NULL) { - free(hunk->header); - hunk->header = NULL; - return -1; - } - - memcpy(old_path, delta->old_file.path, len); - hunk->old_file = old_path; - } else { - hunk->old_file = ""; - } - - if (delta->new_file.path != NULL) { - len = strlen(delta->new_file.path) + 1; - new_path = malloc(sizeof(char) * len); - if (new_path == NULL) { - free(hunk->header); - free(old_path); - return -1; - } - - memcpy(new_path, delta->new_file.path, len); - hunk->new_file = new_path; - } else { - hunk->new_file = ""; - } - - if (hunk->data == NULL) - hunk->data = PyList_New(0); - - if (PyList_Append(hunks, (PyObject *)hunk) == 0) { - Py_DECREF(hunk); - } - else { - return -1; - } - - return 0; -}; + const git_diff_delta* delta; + const git_diff_range* range; + git_diff_patch* patch; + char buffer[41]; + const char* hunk_content; + size_t amounts, hunk_amounts, i, j, hunk_header_len, hunk_lines; + PyObject *file, *files, *hunks; + Hunk *py_hunk; + int err; -static int -diff_file_cb(const git_diff_delta *delta, float progress, void *cb_data) -{ - PyObject *files, *file; + if (self->diff_changes == NULL) { + self->diff_changes = PyDict_New(); - if (delta->old_file.path != NULL && delta->new_file.path != NULL) { - files = PyDict_GetItemString(cb_data, "files"); + files = PyList_New(0); + PyDict_SetItemString(self->diff_changes, "files", files); - if (files == NULL) { - files = PyList_New(0); - PyDict_SetItemString(cb_data, "files", files); - Py_DECREF(files); + hunks = PyList_New(0); + PyDict_SetItemString(self->diff_changes, "hunks", hunks); + + amounts = git_diff_num_deltas(self->diff); + for (i = 0; i < amounts ; ++i) { + err = git_diff_get_patch(&patch, &delta, self->diff, i); + + if (err == GIT_OK) { + file = Py_BuildValue("(s,s,i,i)", + delta->old_file.path, + delta->new_file.path, + delta->status, + delta->similarity + ); + + PyList_Append(files, file); + } + + hunk_amounts = git_diff_patch_num_hunks(patch); + + for (j=0; j < hunk_amounts; ++j) { + err = git_diff_patch_get_hunk(&range, &hunk_content, + &hunk_header_len, &hunk_lines, patch, j); + + if (err == GIT_OK) { + py_hunk = (Hunk*)PyType_GenericNew(&HunkType, NULL, NULL); + if (py_hunk != NULL) { + py_hunk->old_file = delta->old_file.path; + py_hunk->new_file = delta->new_file.path; + py_hunk->header = hunk_content; + py_hunk->old_start = range->old_start; + py_hunk->old_lines = range->old_lines; + py_hunk->new_start = range->new_start; + py_hunk->new_lines = range->new_lines; + + git_oid_fmt(buffer, &delta->old_file.oid); + py_hunk->old_oid = calloc(41, sizeof(char)); + memcpy(py_hunk->old_oid, buffer, 40); + + git_oid_fmt(buffer, &delta->new_file.oid); + py_hunk->new_oid = calloc(41, sizeof(char)); + memcpy(py_hunk->new_oid, buffer, 40); + + py_hunk->data = Py_BuildValue("(s#,i)", + hunk_content, hunk_header_len, + hunk_lines); + PyList_Append(hunks, (PyObject*) py_hunk); + } + } + } } - - file = Py_BuildValue("(s,s,i,i)", - delta->old_file.path, - delta->new_file.path, - delta->status, - delta->similarity - ); - - /* If success */ - if (PyList_Append(files, file) == 0) - Py_DECREF(file); } - return 0; + return PyDict_Copy(self->diff_changes); } -PyDoc_STRVAR(Diff_changes__doc__, "Raw changes."); +PyDoc_STRVAR(Diff_patch__doc__, "Patch."); PyObject * -Diff_changes__get__(Diff *self) +Diff_patch__get__(Diff *self) { + const git_diff_delta* delta; + git_diff_patch* patch; + char* str = NULL, *buffer = NULL; + int err; + size_t i, len, num, size; + PyObject *py_patch; - if (self->diff_changes == NULL) { - self->diff_changes = PyDict_New(); + num = git_diff_num_deltas(self->diff); + for (i = 0; i < num ; ++i) { + err = git_diff_get_patch(&patch, &delta, self->diff, i); - git_diff_foreach( - self->diff, - &diff_file_cb, - &diff_hunk_cb, - &diff_data_cb, - self->diff_changes - ); - } + if (err < 0 || git_diff_patch_to_str(&str, patch) < 0) + return Error_set(err); - return PyDict_Copy(self->diff_changes); -} + len = strlen(str) + 1; + size = (buffer == NULL) ? len : strlen(buffer) + len; + buffer = realloc(buffer, size * sizeof(char)); -static int diff_print_cb( - const git_diff_delta *delta, - const git_diff_range *range, - char usage, - const char *line, - size_t line_len, - void *cb_data) -{ - PyObject *data = PyBytes_FromStringAndSize(line, line_len); - PyBytes_ConcatAndDel((PyObject **)cb_data, data); - - return 0; -} + if (len == size) + strcpy(buffer, str); + else + strcat(buffer, str); + free(str); + } -PyDoc_STRVAR(Diff_patch__doc__, "Patch."); + py_patch = PyUnicode_FromString(buffer); -PyObject * -Diff_patch__get__(Diff *self) -{ - PyObject *patch = PyBytes_FromString(""); + if (buffer != NULL) + free(buffer); - git_diff_print_patch(self->diff, &diff_print_cb, (void*) &patch); + if (patch != NULL) + git_diff_patch_free; - return patch; + return py_patch; } static int @@ -269,13 +190,13 @@ static void Hunk_dealloc(Hunk *self) { if (self->header != NULL) { - free(self->header); + free((void*) self->header); } if (self->new_file != NULL) { - free(self->new_file); + free((void*) self->new_file); } if (self->old_file != NULL) { - free(self->old_file); + free((void*) self->old_file); } Py_XDECREF(self->old_oid); Py_XDECREF(self->new_oid); @@ -289,12 +210,12 @@ PyMemberDef Hunk_members[] = { MEMBER(Hunk, old_lines, T_INT, "Old lines."), MEMBER(Hunk, old_mode, T_INT, "Old mode."), MEMBER(Hunk, old_file, T_STRING, "Old file."), - MEMBER(Hunk, old_oid, T_OBJECT, "Old oid."), + MEMBER(Hunk, old_oid, T_STRING, "Old oid."), MEMBER(Hunk, new_start, T_INT, "New start."), MEMBER(Hunk, new_lines, T_INT, "New lines."), MEMBER(Hunk, new_mode, T_INT, "New mode."), MEMBER(Hunk, new_file, T_STRING, "New file."), - MEMBER(Hunk, new_oid, T_OBJECT, "New oid."), + MEMBER(Hunk, new_oid, T_STRING, "New oid."), MEMBER(Hunk, data, T_OBJECT, "Data."), {NULL} }; diff --git a/src/pygit2.c b/src/pygit2.c index 6727e65f7..f2acbf0de 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -343,13 +343,6 @@ moduleinit(PyObject* m) PyModule_AddIntConstant(m, "GIT_DIFF_FIND_AND_BREAK_REWRITES", GIT_DIFF_FIND_AND_BREAK_REWRITES); - /* Flags for diffed files */ - PyModule_AddIntConstant(m, "GIT_DIFF_FLAG_BINARY", GIT_DIFF_FLAG_BINARY); - PyModule_AddIntConstant(m, "GIT_DIFF_FLAG_NOT_BINARY", - GIT_DIFF_FLAG_NOT_BINARY); - PyModule_AddIntConstant(m, "GIT_DIFF_FLAG_VALID_OID", - GIT_DIFF_FLAG_VALID_OID); - /* Flags for diff deltas */ PyModule_AddIntConstant(m, "GIT_DELTA_UNMODIFIED", GIT_DELTA_UNMODIFIED); PyModule_AddIntConstant(m, "GIT_DELTA_ADDED", GIT_DELTA_ADDED); diff --git a/test/test_diff.py b/test/test_diff.py index ff3a54c5b..03f49e5d7 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -41,7 +41,7 @@ COMMIT_SHA1_4 = 'ccca47fbb26183e71a7a46d165299b84e2e6c0b3' COMMIT_SHA1_5 = '056e626e51b1fc1ee2182800e399ed8d84c8f082' -PATCH = b"""diff --git a/a b/a +PATCH = """diff --git a/a b/a index 7f129fd..af431f2 100644 --- a/a +++ b/a From 4b89994ff4c51ef3e13726c6f7c7d7585f3c0059 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 4 Mar 2013 11:18:11 +0100 Subject: [PATCH 0403/2237] memory allocation helper macros --- include/pygit2/utils.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/pygit2/utils.h b/include/pygit2/utils.h index e9768b12d..161b1946f 100644 --- a/include/pygit2/utils.h +++ b/include/pygit2/utils.h @@ -119,5 +119,21 @@ char * py_str_to_c_str(PyObject *value, const char *encoding); #define MEMBER(type, attr, attr_type, docstr)\ {#attr, attr_type, offsetof(type, attr), 0, PyDoc_STR(docstr)} +/* Helpers for memory allocation */ + + +#define MALLOC(ptr, size, label) \ + ptr = realloc(ptr, size * sizeof(char));\ + if (ptr == NULL) {\ + err = GIT_ERROR;\ + giterr_set_oom();\ + goto label;\ + }\ + +#define FREE(to_free)\ + if (to_free != NULL) { free(to_free); to_free = NULL; } +#define FREE_FUNC(to_free, fnct)\ + if (to_free != NULL) { fnct(to_free); to_free = NULL; } + #endif From 996663c97d4ed3e44c0611f4d08d488e1a72e3da Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 4 Mar 2013 11:18:58 +0100 Subject: [PATCH 0404/2237] diff - use new memory alloc macros --- src/diff.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/diff.c b/src/diff.c index 7440013d0..de19fdd0d 100644 --- a/src/diff.c +++ b/src/diff.c @@ -127,7 +127,7 @@ Diff_patch__get__(Diff *self) const git_diff_delta* delta; git_diff_patch* patch; char* str = NULL, *buffer = NULL; - int err; + int err = 0; size_t i, len, num, size; PyObject *py_patch; @@ -135,30 +135,29 @@ Diff_patch__get__(Diff *self) for (i = 0; i < num ; ++i) { err = git_diff_get_patch(&patch, &delta, self->diff, i); - if (err < 0 || git_diff_patch_to_str(&str, patch) < 0) - return Error_set(err); + if (err < 0 || (err = git_diff_patch_to_str(&str, patch)) < 0) + goto error; len = strlen(str) + 1; size = (buffer == NULL) ? len : strlen(buffer) + len; - buffer = realloc(buffer, size * sizeof(char)); + MALLOC(buffer, size, error); if (len == size) strcpy(buffer, str); else strcat(buffer, str); - free(str); + FREE(str); } py_patch = PyUnicode_FromString(buffer); - if (buffer != NULL) - free(buffer); - - if (patch != NULL) - git_diff_patch_free; +error: + FREE(str); + FREE(buffer); + FREE_FUNC(patch, git_diff_patch_free); - return py_patch; + return (err < 0) ? Error_set(err) : py_patch; } static int From 2771c8a15069709c78698de0987ba132ce4facdd Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 4 Mar 2013 15:44:17 +0100 Subject: [PATCH 0405/2237] added assertAll and assertAny for test cases --- test/utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/utils.py b/test/utils.py index f034b946c..0cf41d57f 100644 --- a/test/utils.py +++ b/test/utils.py @@ -83,6 +83,11 @@ def assertRaisesAssign(self, exc_class, instance, name, value): except: self.assertEqual(exc_class, sys.exc_info()[0]) + def assertAll(self, func, entries): + return self.assertTrue(all(func(x) for x in entries)) + + def assertAny(self, func, entries): + return self.assertTrue(any(func(x) for x in entries)) def assertRaisesWithArg(self, exc_class, arg, func, *args, **kwargs): try: From 204fbd9cbb93bf1210b5233993c24626a55f5981 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 4 Mar 2013 15:45:17 +0100 Subject: [PATCH 0406/2237] diff - use generator instead of list --- include/pygit2/types.h | 14 ++- src/diff.c | 253 +++++++++++++++++++++++++++++++---------- src/index.c | 2 +- src/pygit2.c | 6 + src/tree.c | 3 +- test/test_diff.py | 39 +++---- 6 files changed, 230 insertions(+), 87 deletions(-) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 7625b8982..dfd08c03d 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -59,14 +59,20 @@ OBJECT_STRUCT(Index, git_index, index) OBJECT_STRUCT(Walker, git_revwalk, walk) OBJECT_STRUCT(Config, git_config, config) OBJECT_STRUCT(Remote, git_remote, remote) +OBJECT_STRUCT(Diff, git_diff_list, list) typedef struct { PyObject_HEAD - Repository *repo; - git_diff_list *diff; - PyObject *diff_changes; -} Diff; + git_diff_list* list; + size_t i; + size_t n; +} DiffIter; +typedef struct { + PyObject_HEAD + PyObject* files; + PyObject* hunks; +} DiffEntry; typedef struct { PyObject_HEAD diff --git a/src/diff.c b/src/diff.c index de19fdd0d..c9b49834a 100644 --- a/src/diff.c +++ b/src/diff.c @@ -40,35 +40,29 @@ extern PyTypeObject IndexType; extern PyTypeObject DiffType; extern PyTypeObject HunkType; +PyTypeObject DiffEntryType; -PyDoc_STRVAR(Diff_changes__doc__, "Raw changes."); - -PyObject * -Diff_changes__get__(Diff *self) +PyObject* +diff_get_patch_byindex(git_diff_list* list, size_t i) { const git_diff_delta* delta; const git_diff_range* range; - git_diff_patch* patch; + git_diff_patch* patch = NULL; + char buffer[41]; const char* hunk_content; - size_t amounts, hunk_amounts, i, j, hunk_header_len, hunk_lines; - PyObject *file, *files, *hunks; - Hunk *py_hunk; + size_t hunk_amounts, j, hunk_header_len, hunk_lines; int err; - if (self->diff_changes == NULL) { - self->diff_changes = PyDict_New(); - - files = PyList_New(0); - PyDict_SetItemString(self->diff_changes, "files", files); - - hunks = PyList_New(0); - PyDict_SetItemString(self->diff_changes, "hunks", hunks); + PyObject *file; + Hunk *py_hunk; + DiffEntry *py_entry = NULL; - amounts = git_diff_num_deltas(self->diff); - for (i = 0; i < amounts ; ++i) { - err = git_diff_get_patch(&patch, &delta, self->diff, i); + err = git_diff_get_patch(&patch, &delta, list, i); + if (err == GIT_OK) { + py_entry = (DiffEntry*) INSTANCIATE_CLASS(DiffEntryType, NULL); + if (py_entry != NULL) { if (err == GIT_OK) { file = Py_BuildValue("(s,s,i,i)", delta->old_file.path, @@ -77,7 +71,7 @@ Diff_changes__get__(Diff *self) delta->similarity ); - PyList_Append(files, file); + PyList_Append((PyObject*) py_entry->files, file); } hunk_amounts = git_diff_patch_num_hunks(patch); @@ -108,16 +102,147 @@ Diff_changes__get__(Diff *self) py_hunk->data = Py_BuildValue("(s#,i)", hunk_content, hunk_header_len, hunk_lines); - PyList_Append(hunks, (PyObject*) py_hunk); + + PyList_Append((PyObject*) py_entry->hunks, + (PyObject*) py_hunk); } } } } } - return PyDict_Copy(self->diff_changes); + if (err < 0) + return Error_set(err); + + return (PyObject*) py_entry; } +PyObject * +DiffEntry_call(DiffEntry *self, PyObject *args, PyObject *kwds) +{ + self->files = PyList_New(0); + if (self->files == NULL) { + Py_XDECREF(self); + return NULL; + } + + self->hunks = PyList_New(0); + if (self->hunks == NULL) { + Py_XDECREF(self); + return NULL; + } + + return (PyObject*) self; +} + +static void +DiffEntry_dealloc(DiffEntry *self) +{ + Py_DECREF((PyObject*) self->files); + Py_DECREF((PyObject*) self->hunks); + PyObject_Del(self); +} + +PyMemberDef DiffEntry_members[] = { + MEMBER(DiffEntry, files, T_OBJECT, "files"), + MEMBER(DiffEntry, hunks, T_OBJECT, "hunks"), + {NULL} +}; + +PyDoc_STRVAR(DiffEntry__doc__, "Diff entry object."); + +PyTypeObject DiffEntryType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.DiffEntry", /* tp_name */ + sizeof(DiffEntry), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)DiffEntry_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) DiffEntry_call, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + DiffEntry__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + DiffEntry_members, /* tp_members */ + 0, /* 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 */ +}; + + +PyObject * +DiffIter_iternext(DiffIter *self) +{ + if (self->i < self->n) + return diff_get_patch_byindex(self->list, self->i++); + + PyErr_SetNone(PyExc_StopIteration); + return NULL; +} + +void +DiffIter_dealloc(DiffIter *self) +{ + Py_CLEAR(self->list); + PyObject_Del(self); +} + + +PyDoc_STRVAR(DiffIter__doc__, "Diff iterator object."); + +PyTypeObject DiffIterType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.DiffIter", /* tp_name */ + sizeof(DiffIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)DiffIter_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + DiffIter__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc) DiffIter_iternext, /* tp_iternext */ +}; + PyDoc_STRVAR(Diff_patch__doc__, "Patch."); @@ -129,11 +254,11 @@ Diff_patch__get__(Diff *self) char* str = NULL, *buffer = NULL; int err = 0; size_t i, len, num, size; - PyObject *py_patch; + PyObject *py_patch = NULL; - num = git_diff_num_deltas(self->diff); + num = git_diff_num_deltas(self->list); for (i = 0; i < num ; ++i) { - err = git_diff_get_patch(&patch, &delta, self->diff, i); + err = git_diff_get_patch(&patch, &delta, self->list, i); if (err < 0 || (err = git_diff_patch_to_str(&str, patch)) < 0) goto error; @@ -160,30 +285,6 @@ Diff_patch__get__(Diff *self) return (err < 0) ? Error_set(err) : py_patch; } -static int -Hunk_init(Hunk *self, PyObject *args, PyObject *kwds) -{ - self->header = NULL; - - self->old_file = NULL; - self->old_start = 0; - self->old_lines = 0; - - self->new_file = NULL; - self->new_start = 0; - self->new_lines = 0; - - self->old_oid = NULL; - self->new_oid = NULL; - - self->data = PyList_New(0); - if (self->data == NULL) { - Py_XDECREF(self); - return -1; - } - - return 0; -} static void Hunk_dealloc(Hunk *self) @@ -258,7 +359,7 @@ PyTypeObject HunkType = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)Hunk_init, /* tp_init */ + 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; @@ -277,15 +378,14 @@ Diff_merge(Diff *self, PyObject *args) if (!PyArg_ParseTuple(args, "O!", &DiffType, &py_diff)) return NULL; + if (py_diff->repo->repo != self->repo->repo) return Error_set(GIT_ERROR); - err = git_diff_merge(self->diff, py_diff->diff); + err = git_diff_merge(self->list, py_diff->list); if (err < 0) return Error_set(err); - Py_XDECREF(self->diff_changes); - self->diff_changes = NULL; Py_RETURN_NONE; } @@ -304,30 +404,61 @@ Diff_find_similar(Diff *self, PyObject *args) if (!PyArg_ParseTuple(args, "|i", &opts.flags)) return NULL; - err = git_diff_find_similar(self->diff, &opts); + err = git_diff_find_similar(self->list, &opts); if (err < 0) return Error_set(err); - Py_XDECREF(self->diff_changes); - self->diff_changes = NULL; Py_RETURN_NONE; } +PyObject * +Diff_iter(Diff *self) +{ + DiffIter *iter; + + iter = PyObject_New(DiffIter, &DiffIterType); + if (iter) { + Py_INCREF(self); + iter->list = self->list; + iter->i = 0; + iter->n = git_diff_num_deltas(self->list); + } + return (PyObject*)iter; +} + +PyObject * +Diff_getitem(Diff *self, PyObject *value) +{ + size_t i; + + if (PyLong_Check(value) < 0) + return NULL; + + i = PyLong_AsUnsignedLong(value); + + return diff_get_patch_byindex(self->list, i); +} + + static void Diff_dealloc(Diff *self) { - git_diff_list_free(self->diff); + git_diff_list_free(self->list); Py_XDECREF(self->repo); - Py_XDECREF(self->diff_changes); PyObject_Del(self); } PyGetSetDef Diff_getseters[] = { - GETTER(Diff, changes), GETTER(Diff, patch), {NULL} }; +PyMappingMethods Diff_as_mapping = { + 0, /* mp_length */ + (binaryfunc)Diff_getitem, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + static PyMethodDef Diff_methods[] = { METHOD(Diff, merge, METH_VARARGS), METHOD(Diff, find_similar, METH_VARARGS), @@ -350,7 +481,7 @@ PyTypeObject DiffType = { 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + &Diff_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ @@ -363,7 +494,7 @@ PyTypeObject DiffType = { 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ + (getiterfunc)Diff_iter, /* tp_iter */ 0, /* tp_iternext */ Diff_methods, /* tp_methods */ 0, /* tp_members */ diff --git a/src/index.c b/src/index.c index dfcb35588..79554fc83 100644 --- a/src/index.c +++ b/src/index.c @@ -158,7 +158,7 @@ Index_diff(Index *self, PyObject *args) if (py_diff) { Py_INCREF(self->repo); py_diff->repo = self->repo; - py_diff->diff = diff; + py_diff->list = diff; } return (PyObject*)py_diff; diff --git a/src/pygit2.c b/src/pygit2.c index f2acbf0de..1c3f42b26 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -41,6 +41,8 @@ extern PyTypeObject RepositoryType; extern PyTypeObject ObjectType; extern PyTypeObject CommitType; extern PyTypeObject DiffType; +extern PyTypeObject DiffIterType; +extern PyTypeObject DiffEntryType; extern PyTypeObject HunkType; extern PyTypeObject TreeType; extern PyTypeObject TreeBuilderType; @@ -197,6 +199,10 @@ moduleinit(PyObject* m) if (PyType_Ready(&DiffType) < 0) return NULL; + if (PyType_Ready(&DiffIterType) < 0) + return NULL; + if (PyType_Ready(&DiffEntryType) < 0) + return NULL; if (PyType_Ready(&HunkType) < 0) return NULL; diff --git a/src/tree.c b/src/tree.c index 0b22eee0f..749329929 100644 --- a/src/tree.c +++ b/src/tree.c @@ -346,8 +346,7 @@ Tree_diff(Tree *self, PyObject *args) if (py_diff) { Py_INCREF(self->repo); py_diff->repo = self->repo; - py_diff->diff = diff; - py_diff->diff_changes = NULL; + py_diff->list = diff; } return (PyObject*)py_diff; diff --git a/test/test_diff.py b/test/test_diff.py index 03f49e5d7..9b3b3d25c 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -31,6 +31,7 @@ from __future__ import unicode_literals import unittest import pygit2 +import itertools from pygit2 import GIT_DIFF_INCLUDE_UNMODIFIED from . import utils @@ -86,16 +87,16 @@ def test_diff_empty_index(self): head = repo[repo.lookup_reference('HEAD').resolve().oid] diff = head.tree.diff(repo.index) - files = [x[1] for x in diff.changes['files']] - self.assertEqual(DIFF_INDEX_EXPECTED, files) + files = [[x[0] for x in entry.files] for entry in diff] + self.assertEqual(DIFF_INDEX_EXPECTED, list(itertools.chain(*files))) def test_workdir_to_tree(self): repo = self.repo head = repo[repo.lookup_reference('HEAD').resolve().oid] diff = head.tree.diff() - files = [x[1] for x in diff.changes['files']] - self.assertEqual(DIFF_WORKDIR_EXPECTED, files) + files = [[x[0] for x in entry.files] for entry in diff] + self.assertEqual(DIFF_WORKDIR_EXPECTED, list(itertools.chain(*files))) class DiffTest(utils.BareRepoTestCase): @@ -109,8 +110,8 @@ def test_diff_empty_index(self): head = repo[repo.lookup_reference('HEAD').resolve().oid] diff = head.tree.diff(repo.index) - files = [x[0].split('/')[0] for x in diff.changes['files']] - self.assertEqual([x.name for x in head.tree], files) + files = [[x[0].split('/')[0] for x in entry.files] for entry in diff] + self.assertEqual([x.name for x in head.tree], list(itertools.chain(*files))) def test_diff_tree(self): commit_a = self.repo[COMMIT_SHA1_1] @@ -121,10 +122,10 @@ def test_diff_tree(self): # self.assertIsNotNone is 2.7 only self.assertTrue(diff is not None) # self.assertIn is 2.7 only - self.assertTrue(('a', 'a', 3, 0) in diff.changes['files']) - self.assertEqual(2, len(diff.changes['hunks'])) + self.assertAny(lambda x: ('a', 'a', 3, 0) in x.files, diff) + self.assertEqual(2, sum(map(lambda x: len(x.hunks), diff))) - hunk = diff.changes['hunks'][0] + hunk = diff[0].hunks[0] self.assertEqual(hunk.old_start, 1) self.assertEqual(hunk.old_lines, 1) self.assertEqual(hunk.new_start, 1) @@ -144,11 +145,11 @@ def test_diff_tree_opts(self): pygit2.GIT_DIFF_IGNORE_WHITESPACE_EOL]: diff = commit_c.tree.diff(commit_d.tree, opt) self.assertTrue(diff is not None) - self.assertEqual(0, len(diff.changes.get('hunks', list()))) + self.assertEqual(0, len(diff[0].hunks)) diff = commit_c.tree.diff(commit_d.tree) self.assertTrue(diff is not None) - self.assertEqual(1, len(diff.changes.get('hunks', list()))) + self.assertEqual(1, len(diff[0].hunks)) def test_diff_merge(self): commit_a = self.repo[COMMIT_SHA1_1] @@ -164,15 +165,15 @@ def test_diff_merge(self): self.assertTrue(diff_c is not None) # assertIn / assertNotIn are 2.7 only - self.assertTrue(('b', 'b', 3, 0) not in diff_b.changes['files']) - self.assertTrue(('b', 'b', 3, 0) in diff_c.changes['files']) + self.assertAll(lambda x:('b', 'b', 3, 0) not in x.files, diff_b) + self.assertAny(lambda x:('b', 'b', 3, 0) in x.files, diff_c) diff_b.merge(diff_c) # assertIn is 2.7 only - self.assertTrue(('b', 'b', 3, 0) in diff_b.changes['files']) + self.assertAny(lambda x:('b', 'b', 3, 0) in x.files, diff_b) - hunk = diff_b.changes['hunks'][1] + hunk = diff_b[1].hunks[0] self.assertEqual(hunk.old_start, 1) self.assertEqual(hunk.old_lines, 1) self.assertEqual(hunk.new_start, 1) @@ -196,13 +197,13 @@ def test_diff_header(self): commit_b = self.repo[COMMIT_SHA1_2] diff = commit_a.tree.diff(commit_b.tree) - self.assertEqual(diff.changes['hunks'][0].header, "@@ -1 +1 @@\n") + self.assertEqual(diff[0].hunks[0].header, "@@ -1 +1 @@\n") def test_diff_oids(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] diff = commit_a.tree.diff(commit_b.tree) - hunk = diff.changes['hunks'][0] + hunk = diff[0].hunks[0] self.assertEqual(hunk.old_oid, '7f129fd57e31e935c6d60a0c794efe4e6927664b') self.assertEqual(hunk.new_oid, @@ -215,9 +216,9 @@ def test_find_similar(self): #~ Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate #~ --find-copies-harder during rename transformion... diff = commit_a.tree.diff(commit_b.tree, GIT_DIFF_INCLUDE_UNMODIFIED) - self.assertFalse(('a', 'a.copy', 5, 100) in diff.changes['files']) + self.assertFalse(('a', 'a.copy', 5, 100) in diff[0].files) diff.find_similar(pygit2.GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED) - self.assertTrue(('a', 'a.copy', 5, 100) in diff.changes['files']) + self.assertAny(lambda x:('a', 'a.copy', 5, 100) in x.files, diff) if __name__ == '__main__': unittest.main() From da4abb78cb3033ecb0ea00e965c137f2e97ba7d2 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 4 Mar 2013 18:37:45 +0100 Subject: [PATCH 0407/2237] added test data for diff.find_similiar --- .../10/2374bdb1e8efca5e66cded18fd8f30571654a5 | Bin 0 -> 209 bytes .../11/19926b06311143cab273f0af84eae77f5b3462 | Bin 0 -> 136 bytes .../19/bf31524643d743751b09cf719456914bbd8bd5 | Bin 0 -> 136 bytes .../55/60f04f38a674decf34d16d7c7476642fa03794 | Bin 0 -> 129 bytes .../78/4855caf26449a1914d2cf62d12b9374d76ae78 | Bin 0 -> 176 bytes .../f5/e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87 | Bin 0 -> 172 bytes test/data/testrepo.git/refs/heads/master | 2 +- test/test_diff.py | 13 ++++++++----- test/test_remote.py | 4 ++-- test/test_repository.py | 4 ++-- 10 files changed, 13 insertions(+), 10 deletions(-) create mode 100644 test/data/testrepo.git/objects/10/2374bdb1e8efca5e66cded18fd8f30571654a5 create mode 100644 test/data/testrepo.git/objects/11/19926b06311143cab273f0af84eae77f5b3462 create mode 100644 test/data/testrepo.git/objects/19/bf31524643d743751b09cf719456914bbd8bd5 create mode 100644 test/data/testrepo.git/objects/55/60f04f38a674decf34d16d7c7476642fa03794 create mode 100644 test/data/testrepo.git/objects/78/4855caf26449a1914d2cf62d12b9374d76ae78 create mode 100644 test/data/testrepo.git/objects/f5/e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87 diff --git a/test/data/testrepo.git/objects/10/2374bdb1e8efca5e66cded18fd8f30571654a5 b/test/data/testrepo.git/objects/10/2374bdb1e8efca5e66cded18fd8f30571654a5 new file mode 100644 index 0000000000000000000000000000000000000000..4cb6eb7dde59cc87984dbcd34132839d06296f3f GIT binary patch literal 209 zcmV;?051P{0qs&dP6ROwrREgx0HZ08C@AUaIDwN`Bkv~hkk}*b?eT!bR&W9eH-2AP zPE`}0U!ES`bZ~?0(;JFb9hhRE#Aw8w(~E6rqN$-P;W;qiKP0 z+rvcqA;r7F4V6-afbBbg(P--gs`BBtFyKKE_TzVG_1&CSyc!N33f6h5DQBN2-*EA1 zisCV6Xa2&OFgSTz@gAj-qbK1?vtaYYV;kI;Xd6icqpLIud{WQmp`V7|Z?XP?{om>S LGwb6g(ihq?OFd`O literal 0 HcmV?d00001 diff --git a/test/data/testrepo.git/objects/11/19926b06311143cab273f0af84eae77f5b3462 b/test/data/testrepo.git/objects/11/19926b06311143cab273f0af84eae77f5b3462 new file mode 100644 index 0000000000000000000000000000000000000000..8ac529e9ac5413e8aa6e578eb38d6061a627552f GIT binary patch literal 136 zcmV;30C)d*0V^p=O;s>7HD)k0FfcPQQAlJc?_iw&`pFlj$Ynl3vu3>wlS{d~7bdBf zoL^9hPel^L^lPFI_o&-Aob)Qn5P81S{&tkKnTY`qC?qo^`rlm4>HSUO)#U#F2l#5G qLVnD;05v)%zbG}AK|r}=@5UGJPsOF3eJk;|-ymEpWGMjU?LoUvmqAAW literal 0 HcmV?d00001 diff --git a/test/data/testrepo.git/objects/19/bf31524643d743751b09cf719456914bbd8bd5 b/test/data/testrepo.git/objects/19/bf31524643d743751b09cf719456914bbd8bd5 new file mode 100644 index 0000000000000000000000000000000000000000..af11713b32bf8b0e22f687aa1a74440110a4c8a3 GIT binary patch literal 136 zcmV;30C)d*0V^p=O;s>7HD)k0FfcPQQAlJc?_iw&`pFlj$Ynl3vu3>wlS{d~7bdBf zoL^9hPel^L^lPFI_o&-Aob)Qn5P81S{&tkKnTY`qC?qo^`rlm4>HSUO)#U#F2l#5G qLVnD;05v+Zptv-bK|r}=@5UGJPsOF3eJk;|-ymEpWGMjWKtalDDM67v}71LW? z%IJMtU|f1yQv@(^v*?W@XKQdacx0VQ3z(dzgpRpJu3nwADM|Th4*q=qDBu2&{@%H!g3fIjv zqe*q!mQL!1r``Y(k!eytv$t1tHpN*-TR4{B5HXHrn62|_zGK7Qi3bm_9)rwMk^m|} z12gP2A%`4X3WbB3_}jl~qt7K(`l~yg@c&ck_2!2=zaP&pA86~cA2QxZtXC)DdO^=D a%qHDWJ05A6m@_i1wPB;OtS}dDrb~bhy;CCq literal 0 HcmV?d00001 diff --git a/test/data/testrepo.git/refs/heads/master b/test/data/testrepo.git/refs/heads/master index b8e984855..436950fb2 100644 --- a/test/data/testrepo.git/refs/heads/master +++ b/test/data/testrepo.git/refs/heads/master @@ -1 +1 @@ -056e626e51b1fc1ee2182800e399ed8d84c8f082 +784855caf26449a1914d2cf62d12b9374d76ae78 diff --git a/test/test_diff.py b/test/test_diff.py index 9b3b3d25c..e67f32102 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -41,6 +41,9 @@ COMMIT_SHA1_3 = '2cdae28389c059815e951d0bb9eed6533f61a46b' COMMIT_SHA1_4 = 'ccca47fbb26183e71a7a46d165299b84e2e6c0b3' COMMIT_SHA1_5 = '056e626e51b1fc1ee2182800e399ed8d84c8f082' +COMMIT_SHA1_6 = 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87' +COMMIT_SHA1_7 = '784855caf26449a1914d2cf62d12b9374d76ae78' + PATCH = """diff --git a/a b/a index 7f129fd..af431f2 100644 @@ -210,15 +213,15 @@ def test_diff_oids(self): 'af431f20fc541ed6d5afede3e2dc7160f6f01f16') def test_find_similar(self): - commit_a = self.repo[COMMIT_SHA1_4] - commit_b = self.repo[COMMIT_SHA1_5] + commit_a = self.repo[COMMIT_SHA1_6] + commit_b = self.repo[COMMIT_SHA1_7] #~ Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate #~ --find-copies-harder during rename transformion... diff = commit_a.tree.diff(commit_b.tree, GIT_DIFF_INCLUDE_UNMODIFIED) - self.assertFalse(('a', 'a.copy', 5, 100) in diff[0].files) - diff.find_similar(pygit2.GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED) - self.assertAny(lambda x:('a', 'a.copy', 5, 100) in x.files, diff) + self.assertFalse(('lorem', 'ipsum', 4, 100) in diff[0].files) + diff.find_similar() + self.assertAny(lambda x: ('lorem', 'ipsum', 4, 100) in x.files, diff) if __name__ == '__main__': unittest.main() diff --git a/test/test_remote.py b/test/test_remote.py index aacdc10bf..9b2c8c8b0 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -35,8 +35,8 @@ REMOTE_URL = 'git://github.com/libgit2/pygit2.git' REMOTE_FETCHSPEC_SRC = 'refs/heads/*' REMOTE_FETCHSPEC_DST = 'refs/remotes/origin/*' -REMOTE_REPO_OBJECTS = 19 -REMOTE_REPO_BYTES = 1586 +REMOTE_REPO_OBJECTS = 24 +REMOTE_REPO_BYTES = 2253 class RepositoryTest(utils.RepoTestCase): def test_remote_create(self): diff --git a/test/test_repository.py b/test/test_repository.py index 105d1c5c9..c3457c483 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -42,8 +42,8 @@ from . import utils -HEAD_SHA = '056e626e51b1fc1ee2182800e399ed8d84c8f082' -PARENT_SHA = 'ccca47fbb26183e71a7a46d165299b84e2e6c0b3' # HEAD^ +HEAD_SHA = '784855caf26449a1914d2cf62d12b9374d76ae78' +PARENT_SHA = 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87' # HEAD^ A_HEX_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' A_BIN_SHA = binascii.unhexlify(A_HEX_SHA.encode('ascii')) From 86b063fbd02967065418b683b5b243c6825d47b5 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 4 Mar 2013 19:32:12 +0100 Subject: [PATCH 0408/2237] fixed assert for diff.find_similiar test --- test/test_diff.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/test_diff.py b/test/test_diff.py index e67f32102..8cb7b2c4a 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -219,9 +219,10 @@ def test_find_similar(self): #~ Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate #~ --find-copies-harder during rename transformion... diff = commit_a.tree.diff(commit_b.tree, GIT_DIFF_INCLUDE_UNMODIFIED) - self.assertFalse(('lorem', 'ipsum', 4, 100) in diff[0].files) + entry = ('lorem', 'ipsum', pygit2.GIT_DELTA_RENAMED, 100) + self.assertAll(lambda x: entry not in x.files, diff) diff.find_similar() - self.assertAny(lambda x: ('lorem', 'ipsum', 4, 100) in x.files, diff) + self.assertAny(lambda x: entry in x.files, diff) if __name__ == '__main__': unittest.main() From 8d8416e083120885f9233d9f2d378226b9976bc4 Mon Sep 17 00:00:00 2001 From: Valentin Haenel Date: Fri, 8 Mar 2013 22:02:26 +0100 Subject: [PATCH 0409/2237] pep8 fixes for setup.py as reported by flake8 --- setup.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index d52e93551..e38ebec7a 100644 --- a/setup.py +++ b/setup.py @@ -64,8 +64,9 @@ libgit2_bin = os.path.join(libgit2_path, 'bin') libgit2_include = os.path.join(libgit2_path, 'include') libgit2_lib = os.getenv('LIBGIT2_LIB', os.path.join(libgit2_path, 'lib')) -pygit2_exts = [ os.path.join('src', name) for name in os.listdir('src') - if name.endswith('.c') ] +pygit2_exts = [os.path.join('src', name) for name in os.listdir('src') + if name.endswith('.c')] + class TestCommand(Command): """Command for running unittests without install.""" @@ -155,7 +156,6 @@ def get_file_list(self): self.write_manifest() - cmdclass = { 'test': TestCommand, 'sdist': sdist_files_from_git} @@ -183,11 +183,11 @@ def get_file_list(self): maintainer=u('J. David Ibáñez'), maintainer_email='jdavid.ibp@gmail.com', long_description=long_description, - packages = ['pygit2'], + packages=['pygit2'], ext_modules=[ Extension('_pygit2', pygit2_exts, include_dirs=[libgit2_include, 'include'], library_dirs=[libgit2_lib], libraries=['git2']), - ], + ], cmdclass=cmdclass) From 2ffb39397d932bc238a90a07e2434447ca1cea26 Mon Sep 17 00:00:00 2001 From: Valentin Haenel Date: Fri, 8 Mar 2013 22:08:37 +0100 Subject: [PATCH 0410/2237] syntax highlighting for shell commands in sphinx --- docs/install.rst | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index aacad2f58..6cde5b587 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -14,7 +14,9 @@ platform-specific instructions to build the library in the libgit2 website: Also, make sure you have Python 2.6+ installed together with the Python development headers. -When those are installed, you can install pygit2:: +When those are installed, you can install pygit2: + +.. code-block:: sh $ git clone git://github.com/libgit2/pygit2.git $ cd pygit2 @@ -36,7 +38,9 @@ using the default installation procedure (e.g. without specifying ``CMAKE_INSTALL_PREFIX``), you probably installed it under ``/usr/local/lib``. On some distributions (e.g. Ubuntu), ``/usr/local/lib`` is not in the linker's default search path (see the -`ld man page`_ for details), and you will get errors like:: +`ld man page`_ for details), and you will get errors like: + +.. code-block:: sh $ python -c 'import pygit2' Traceback (most recent call last): @@ -47,7 +51,9 @@ using the default installation procedure (e.g. without specifying The following recipe shows how to install libgit2 and pygit2 on these systems. First, download and install libgit2 (following the -instructions in the libgit2 ``README.md``):: +instructions in the libgit2 ``README.md``): + +.. code-block:: sh $ git clone git://github.com/libgit2/libgit2.git $ mkdir libgit2/build @@ -59,7 +65,9 @@ instructions in the libgit2 ``README.md``):: Now, download and install pygit2. You will probably have to set the ``LIBGIT2`` environment variable so the compiler can find the libgit2 -headers and libraries:: +headers and libraries: + +.. code-block:: sh $ git clone git://github.com/libgit2/pygit2.git $ cd pygit2 @@ -72,7 +80,9 @@ This compiles the pygit2 libraries with a ``RUNPATH``, which bakes extra library search paths directly into the binaries (see the `ld man page`_ for details). With ``RUNPATH`` compiled in, you won't have to use ``LD_LIBRARY_PATH``. You can check to ensure ``RUNPATH`` was set -with readelf_:: +with readelf_: + +.. code-block:: sh $ readelf --dynamic build/lib.linux-x86_64-3.2/_pygit2.cpython-32.so | grep PATH 0x000000000000000f (RPATH) Library rpath: [/usr/local/lib] @@ -90,7 +100,9 @@ in the ``LIBGIT2`` environment variable. In addition, make sure that libgit2 is build in "__cdecl" mode. The following recipe shows you how to do it, assuming you're working -from a bash shell:: +from a bash shell: + +.. code-block:: sh $ export LIBGIT2=C:/Dev/libgit2 $ git clone git://github.com/libgit2/libgit2.git From 27aba1f3c8746a81c6fc1beb82f8d913f7959707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 9 Mar 2013 12:36:58 +0100 Subject: [PATCH 0411/2237] Update to changes in libgit2 concerning refs Two methods have been drop: - Repository.packall_references - Reference.reload The unit tests have been commented until we wrap the new reference database from libgit2. --- src/pygit2.c | 2 +- src/reference.c | 44 +++++++++++--------------------------------- src/repository.c | 19 ------------------- test/test_refs.py | 22 +++++++++++----------- 4 files changed, 23 insertions(+), 64 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index 1c3f42b26..cd56a6e88 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -290,9 +290,9 @@ moduleinit(PyObject* m) PyModule_AddIntConstant(m, "GIT_SORT_TOPOLOGICAL", GIT_SORT_TOPOLOGICAL); PyModule_AddIntConstant(m, "GIT_SORT_TIME", GIT_SORT_TIME); PyModule_AddIntConstant(m, "GIT_SORT_REVERSE", GIT_SORT_REVERSE); + PyModule_AddIntConstant(m, "GIT_REF_INVALID", GIT_REF_INVALID); PyModule_AddIntConstant(m, "GIT_REF_OID", GIT_REF_OID); PyModule_AddIntConstant(m, "GIT_REF_SYMBOLIC", GIT_REF_SYMBOLIC); - PyModule_AddIntConstant(m, "GIT_REF_PACKED", GIT_REF_PACKED); PyModule_AddIntConstant(m, "GIT_REF_LISTALL", GIT_REF_LISTALL); /* Git status flags */ diff --git a/src/reference.c b/src/reference.c index f13ed3111..129ed44ec 100644 --- a/src/reference.c +++ b/src/reference.c @@ -161,6 +161,7 @@ Reference_rename(Reference *self, PyObject *py_name) { char *c_name; int err; + git_reference *new_reference; CHECK_REFERENCE(self); @@ -170,33 +171,12 @@ Reference_rename(Reference *self, PyObject *py_name) return NULL; /* Rename */ - err = git_reference_rename(self->reference, c_name, 0); + err = git_reference_rename(&new_reference, self->reference, c_name, 0); free(c_name); if (err < 0) return Error_set(err); - Py_RETURN_NONE; -} - - -PyDoc_STRVAR(Reference_reload__doc__, - "reload()\n" - "\n" - "Reload the reference from the file-system."); - -PyObject * -Reference_reload(Reference *self) -{ - int err; - - CHECK_REFERENCE(self); - - err = git_reference_reload(self->reference); - if (err < 0) { - self->reference = NULL; - return Error_set(err); - } - + self->reference = new_reference; Py_RETURN_NONE; } @@ -214,13 +194,8 @@ Reference_resolve(Reference *self, PyObject *args) CHECK_REFERENCE(self); - /* Direct: reload */ + /* Direct: return myself */ if (git_reference_type(self->reference) == GIT_REF_OID) { - err = git_reference_reload(self->reference); - if (err < 0) { - self->reference = NULL; - return Error_set(err); - } Py_INCREF(self); return (PyObject *)self; } @@ -263,6 +238,7 @@ Reference_target__set__(Reference *self, PyObject *py_name) { char *c_name; int err; + git_reference *new_ref; CHECK_REFERENCE_INT(self); @@ -272,13 +248,14 @@ Reference_target__set__(Reference *self, PyObject *py_name) return -1; /* Set the new target */ - err = git_reference_symbolic_set_target(self->reference, c_name); + err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name); free(c_name); if (err < 0) { Error_set(err); return -1; } + self->reference = new_ref; return 0; } @@ -320,6 +297,7 @@ Reference_oid__set__(Reference *self, PyObject *py_hex) { git_oid oid; int err; + git_reference *new_ref; CHECK_REFERENCE_INT(self); @@ -332,12 +310,13 @@ Reference_oid__set__(Reference *self, PyObject *py_hex) } /* Set the oid */ - err = git_reference_set_target(self->reference, &oid); + err = git_reference_set_target(&new_ref, self->reference, &oid); if (err < 0) { Error_set(err); return -1; } + self->reference = new_ref; return 0; } @@ -366,7 +345,7 @@ Reference_hex__get__(Reference *self) PyDoc_STRVAR(Reference_type__doc__, - "Type (GIT_REF_OID, GIT_REF_SYMBOLIC or GIT_REF_PACKED)."); + "Type (GIT_REF_OID or GIT_REF_SYMBOLIC)."); PyObject * Reference_type__get__(Reference *self) @@ -481,7 +460,6 @@ PyTypeObject RefLogEntryType = { PyMethodDef Reference_methods[] = { METHOD(Reference, delete, METH_NOARGS), METHOD(Reference, rename, METH_O), - METHOD(Reference, reload, METH_NOARGS), METHOD(Reference, resolve, METH_NOARGS), METHOD(Reference, log, METH_NOARGS), {NULL} diff --git a/src/repository.c b/src/repository.c index 1016948c0..497e28dbd 100644 --- a/src/repository.c +++ b/src/repository.c @@ -902,24 +902,6 @@ Repository_create_symbolic_reference(Repository *self, PyObject *args, } -PyDoc_STRVAR(Repository_packall_references__doc__, - "packall_references()\n" - "\n" - "Pack all the loose references in the repository."); - -PyObject * -Repository_packall_references(Repository *self, PyObject *args) -{ - int err; - - err = git_reference_packall(self->repo); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; -} - - PyDoc_STRVAR(Repository_status__doc__, "status() -> {str: int}\n" "\n" @@ -1145,7 +1127,6 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, create_symbolic_reference, METH_VARARGS), METHOD(Repository, listall_references, METH_VARARGS), METHOD(Repository, lookup_reference, METH_O), - METHOD(Repository, packall_references, METH_NOARGS), METHOD(Repository, revparse_single, METH_O), METHOD(Repository, status, METH_NOARGS), METHOD(Repository, status_file, METH_O), diff --git a/test/test_refs.py b/test/test_refs.py index 0b2f61cfb..fb001822d 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -142,16 +142,16 @@ def test_rename(self): self.assertEqual(reference.name, 'refs/tags/version2') - def test_reload(self): - name = 'refs/tags/version1' +# def test_reload(self): +# name = 'refs/tags/version1' - repo = self.repo - ref = repo.create_reference(name, "refs/heads/master", symbolic=True) - ref2 = repo.lookup_reference(name) - ref.delete() - self.assertEqual(ref2.name, name) - self.assertRaises(KeyError, ref2.reload) - self.assertRaises(GitError, getattr, ref2, 'name') +# repo = self.repo +# ref = repo.create_reference(name, "refs/heads/master", symbolic=True) +# ref2 = repo.lookup_reference(name) +# ref.delete() +# self.assertEqual(ref2.name, name) +# self.assertRaises(KeyError, ref2.reload) +# self.assertRaises(GitError, getattr, ref2, 'name') def test_reference_resolve(self): @@ -209,8 +209,8 @@ def test_create_symbolic_reference(self): self.assertEqual(reference.target, 'refs/heads/master') - def test_packall_references(self): - self.repo.packall_references() +# def test_packall_references(self): +# self.repo.packall_references() if __name__ == '__main__': From 33638b66e22c43cc9d983e43bf6029a054a810c3 Mon Sep 17 00:00:00 2001 From: Valentin Haenel Date: Sat, 9 Mar 2013 13:02:48 +0100 Subject: [PATCH 0412/2237] the extension compiles to _pygit2.so, so ignore that too --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 972131dfd..d8022420b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ MANIFEST build dist pygit2.so +_pygit2.so test/*.pyc test/__pycache__ pygit2/*.pyc From ad4b4cc4c51acbb6200ebb6c0c230a1d57d5bcd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 9 Mar 2013 23:10:20 +0100 Subject: [PATCH 0413/2237] Implement the Repository mapping interface in Python Add 'Repository.get' and low level 'Repository.git_object_lookup_prefix' --- include/pygit2/types.h | 4 -- pygit2/repository.py | 22 ++++++++++ src/repository.c | 95 +++++++++++------------------------------- 3 files changed, 47 insertions(+), 74 deletions(-) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index dfd08c03d..497d2506a 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -143,10 +143,6 @@ typedef struct { } Signature; -PyObject* -lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, - git_otype type); - PyObject* lookup_object(Repository *repo, const git_oid *oid, git_otype type); #endif diff --git a/pygit2/repository.py b/pygit2/repository.py index f73de73d6..4513d0919 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -31,6 +31,28 @@ class Repository(_Repository): + # + # Mapping interface + # + def get(self, key, default=None): + value = self.git_object_lookup_prefix(key) + return value if (value is not None) else default + + + def __getitem__(self, key): + value = self.git_object_lookup_prefix(key) + if value is None: + raise KeyError(key) + return value + + + def __contains__(self, key): + return self.git_object_lookup_prefix(key) is not None + + + # + # References + # def create_reference(self, name, target, force=False, symbolic=False): """ Create a new reference "name" which points to a object or another diff --git a/src/repository.c b/src/repository.c index c0541b931..7afab3445 100644 --- a/src/repository.c +++ b/src/repository.c @@ -60,26 +60,19 @@ int_to_loose_object_type(int type_id) } PyObject * -lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, - git_otype type) +lookup_object(Repository *repo, const git_oid *oid, git_otype type) { int err; git_object *obj; - err = git_object_lookup_prefix(&obj, repo->repo, oid, - (unsigned int)len, type); + err = git_object_lookup_prefix(&obj, repo->repo, oid, GIT_OID_HEXSZ, + type); if (err < 0) - return Error_set_oid(err, oid, len); + return Error_set_oid(err, oid, GIT_OID_HEXSZ); return wrap_object(obj, repo); } -PyObject * -lookup_object(Repository *repo, const git_oid *oid, git_otype type) -{ - return lookup_object_prefix(repo, oid, GIT_OID_HEXSZ, type); -} - int Repository_init(Repository *self, PyObject *args, PyObject *kwds) { @@ -127,42 +120,6 @@ Repository_clear(Repository *self) return 0; } -int -Repository_contains(Repository *self, PyObject *value) -{ - git_oid oid; - git_odb *odb; - int err, len, exists; - - len = py_str_to_git_oid(value, &oid); - if (len < 0) - return -1; - - err = git_repository_odb(&odb, self->repo); - if (err < 0) { - Error_set(err); - return -1; - } - - if (len < GIT_OID_HEXSZ) { - git_odb_object *obj = NULL; - err = git_odb_read_prefix(&obj, odb, &oid, len); - if (err < 0 && err != GIT_ENOTFOUND) { - Error_set(err); - exists = -1; - } else { - exists = (err == 0); - if (obj) - git_odb_object_free(obj); - } - } else { - exists = git_odb_exists(odb, &oid); - } - - git_odb_free(odb); - return exists; -} - static int Repository_build_as_iter(const git_oid *oid, void *accum) { @@ -279,17 +236,31 @@ Repository_is_bare__get__(Repository *self) } +PyDoc_STRVAR(Repository_git_object_lookup_prefix__doc__, + "git_object_lookup_prefix(oid) -> Object\n" + "\n" + "Returns the Git object with the given oid."); + PyObject * -Repository_getitem(Repository *self, PyObject *value) +Repository_git_object_lookup_prefix(Repository *self, PyObject *key) { + int err, len; git_oid oid; - int len; + git_object *obj; - len = py_str_to_git_oid(value, &oid); + len = py_str_to_git_oid(key, &oid); if (len < 0) return NULL; - return lookup_object_prefix(self, &oid, len, GIT_OBJ_ANY); + err = git_object_lookup_prefix(&obj, self->repo, &oid, + (unsigned int)len, GIT_OBJ_ANY); + if (err == 0) + return wrap_object(obj, self); + + if (err == GIT_ENOTFOUND) + Py_RETURN_NONE; + + return Error_set_oid(err, &oid, len); } @@ -1132,6 +1103,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, status_file, METH_O), METHOD(Repository, create_remote, METH_VARARGS), METHOD(Repository, checkout, METH_VARARGS|METH_KEYWORDS), + METHOD(Repository, git_object_lookup_prefix, METH_O), {NULL} }; @@ -1149,23 +1121,6 @@ PyGetSetDef Repository_getseters[] = { {NULL} }; -PySequenceMethods Repository_as_sequence = { - 0, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - 0, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - (objobjproc)Repository_contains, /* sq_contains */ -}; - -PyMappingMethods Repository_as_mapping = { - 0, /* mp_length */ - (binaryfunc)Repository_getitem, /* mp_subscript */ - 0, /* mp_ass_subscript */ -}; - PyDoc_STRVAR(Repository__doc__, "Repository(path) -> Repository\n" @@ -1184,8 +1139,8 @@ PyTypeObject RepositoryType = { 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ - &Repository_as_sequence, /* tp_as_sequence */ - &Repository_as_mapping, /* tp_as_mapping */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ From 98013eb44dc575851d30ff680b16e2b442f6d69f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 10 Mar 2013 12:26:32 +0100 Subject: [PATCH 0414/2237] Support short (raw) oids --- src/oid.c | 8 ++++---- test/test_repository.py | 8 +++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/oid.c b/src/oid.c index 102424aa3..36bacf448 100644 --- a/src/oid.c +++ b/src/oid.c @@ -42,11 +42,11 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) /* Case 1: raw sha */ if (PyString_Check(py_str)) { - hex_or_bin = PyString_AsString(py_str); - if (hex_or_bin == NULL) + err = PyString_AsStringAndSize(py_str, &hex_or_bin, &len); + if (err) return -1; - git_oid_fromraw(oid, (const unsigned char*)hex_or_bin); - return GIT_OID_HEXSZ; + memcpy(oid->id, (const unsigned char*)hex_or_bin, len); + return len; } /* Case 2: hex sha */ diff --git a/test/test_repository.py b/test/test_repository.py index c3457c483..c26712abb 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -91,16 +91,14 @@ def test_write(self): def test_contains(self): self.assertRaises(TypeError, lambda: 123 in self.repo) self.assertTrue(A_BIN_SHA in self.repo) + self.assertTrue(A_BIN_SHA[:10] in self.repo) self.assertTrue(A_HEX_SHA in self.repo) - self.assertTrue(A_HEX_SHA[0:10] in self.repo) + self.assertTrue(A_HEX_SHA[:10] in self.repo) self.assertFalse('a' * 40 in self.repo) self.assertFalse('a' * 20 in self.repo) def test_iterable(self): - l = [] - for obj in self.repo: - l.append(obj) - + l = [ obj for obj in self.repo ] self.assertTrue(A_HEX_SHA in l) def test_lookup_blob(self): From 5ed77671b2bc1ad67ff11079a4edaaa816f40cbf Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 7 Mar 2013 11:39:55 +0100 Subject: [PATCH 0415/2237] fixed compiler warnings --- src/pygit2.c | 2 +- src/repository.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index cd56a6e88..e713c0c40 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -69,7 +69,7 @@ PyDoc_STRVAR(init_repository__doc__, "Creates a new Git repository in the given path."); PyObject * -init_repository(PyObject *self, PyObject *args, PyObject *kw) { +init_repository(PyObject *self, PyObject *args) { git_repository *repo; const char *path; unsigned int bare; diff --git a/src/repository.c b/src/repository.c index 497e28dbd..4fb53099d 100644 --- a/src/repository.c +++ b/src/repository.c @@ -839,7 +839,7 @@ Repository_create_direct_reference(Repository *self, PyObject *args, { PyObject *py_obj; git_reference *c_reference; - char *c_name, *c_target; + char *c_name; git_oid oid; int err, force; @@ -879,7 +879,6 @@ Repository_create_symbolic_reference(Repository *self, PyObject *args, PyObject *py_obj; git_reference *c_reference; char *c_name, *c_target; - git_oid oid; int err, force; if (!PyArg_ParseTuple(args, "sOi", &c_name, &py_obj, &force)) From 9d0015f096ab3a3fcb7ccfba4116901b75e762a7 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 10 Mar 2013 12:59:19 +0100 Subject: [PATCH 0416/2237] added valgrind python suppression file To start a valgrind memory check use the following command: valgrind -v --leak-check=full --suppressions=misc/valgrind-python.supp python -E -tt ./setup.py test If your python installation is not compiled with valgrind option, you get as well tons of false-positive leaks in valgrind. Just check for git_* function the the output. Have in my that circular memory dependencies can not easily be found with this approach. --- misc/valgrind-python.supp | 391 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) create mode 100644 misc/valgrind-python.supp diff --git a/misc/valgrind-python.supp b/misc/valgrind-python.supp new file mode 100644 index 000000000..15982cc3e --- /dev/null +++ b/misc/valgrind-python.supp @@ -0,0 +1,391 @@ +# +# This is a valgrind suppression file that should be used when using valgrind. +# +# Here's an example of running valgrind: +# +# cd python/dist/src +# valgrind --tool=memcheck --suppressions=Misc/valgrind-python.supp \ +# ./python -E -tt ./Lib/test/regrtest.py -u bsddb,network +# +# You must edit Objects/obmalloc.c and uncomment Py_USING_MEMORY_DEBUGGER +# to use the preferred suppressions with Py_ADDRESS_IN_RANGE. +# +# If you do not want to recompile Python, you can uncomment +# suppressions for PyObject_Free and PyObject_Realloc. +# +# See Misc/README.valgrind for more information. + +# all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Addr4 + fun:Py_ADDRESS_IN_RANGE +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Value4 + fun:Py_ADDRESS_IN_RANGE +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64 aka amd64) + Memcheck:Value8 + fun:Py_ADDRESS_IN_RANGE +} + +{ + ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value + Memcheck:Cond + fun:Py_ADDRESS_IN_RANGE +} + +# +# Leaks (including possible leaks) +# Hmmm, I wonder if this masks some real leaks. I think it does. +# Will need to fix that. +# + +{ + Suppress leaking the GIL. Happens once per process, see comment in ceval.c. + Memcheck:Leak + fun:malloc + fun:PyThread_allocate_lock + fun:PyEval_InitThreads +} + +{ + Suppress leaking the GIL after a fork. + Memcheck:Leak + fun:malloc + fun:PyThread_allocate_lock + fun:PyEval_ReInitThreads +} + +{ + Suppress leaking the autoTLSkey. This looks like it shouldn't leak though. + Memcheck:Leak + fun:malloc + fun:PyThread_create_key + fun:_PyGILState_Init + fun:Py_InitializeEx + fun:Py_Main +} + +{ + Hmmm, is this a real leak or like the GIL? + Memcheck:Leak + fun:malloc + fun:PyThread_ReInitTLS +} + +{ + Handle PyMalloc confusing valgrind (possibly leaked) + Memcheck:Leak + fun:realloc + fun:_PyObject_GC_Resize + fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING +} + +{ + Handle PyMalloc confusing valgrind (possibly leaked) + Memcheck:Leak + fun:malloc + fun:_PyObject_GC_New + fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING +} + +{ + Handle PyMalloc confusing valgrind (possibly leaked) + Memcheck:Leak + fun:malloc + fun:_PyObject_GC_NewVar + fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING +} + +# +# Non-python specific leaks +# + +{ + Handle pthread issue (possibly leaked) + Memcheck:Leak + fun:calloc + fun:allocate_dtv + fun:_dl_allocate_tls_storage + fun:_dl_allocate_tls +} + +{ + Handle pthread issue (possibly leaked) + Memcheck:Leak + fun:memalign + fun:_dl_allocate_tls_storage + fun:_dl_allocate_tls +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Addr4 + fun:PyObject_Free +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Value4 + fun:PyObject_Free +} + +{ + ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value + Memcheck:Cond + fun:PyObject_Free +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Addr4 + fun:PyObject_Realloc +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Value4 + fun:PyObject_Realloc +} + +{ + ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value + Memcheck:Cond + fun:PyObject_Realloc +} + +### +### All the suppressions below are for errors that occur within libraries +### that Python uses. The problems to not appear to be related to Python's +### use of the libraries. +### + +{ + Generic ubuntu ld problems + Memcheck:Addr8 + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so +} + +{ + Generic gentoo ld problems + Memcheck:Cond + obj:/lib/ld-2.3.4.so + obj:/lib/ld-2.3.4.so + obj:/lib/ld-2.3.4.so + obj:/lib/ld-2.3.4.so +} + +{ + DBM problems, see test_dbm + Memcheck:Param + write(buf) + fun:write + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + fun:dbm_close +} + +{ + DBM problems, see test_dbm + Memcheck:Value8 + fun:memmove + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + fun:dbm_store + fun:dbm_ass_sub +} + +{ + DBM problems, see test_dbm + Memcheck:Cond + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + fun:dbm_store + fun:dbm_ass_sub +} + +{ + DBM problems, see test_dbm + Memcheck:Cond + fun:memmove + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + fun:dbm_store + fun:dbm_ass_sub +} + +{ + GDBM problems, see test_gdbm + Memcheck:Param + write(buf) + fun:write + fun:gdbm_open + +} + +{ + ZLIB problems, see test_gzip + Memcheck:Cond + obj:/lib/libz.so.1.2.3 + obj:/lib/libz.so.1.2.3 + fun:deflate +} + +{ + Avoid problems w/readline doing a putenv and leaking on exit + Memcheck:Leak + fun:malloc + fun:xmalloc + fun:sh_set_lines_and_columns + fun:_rl_get_screen_size + fun:_rl_init_terminal_io + obj:/lib/libreadline.so.4.3 + fun:rl_initialize +} + +### +### These occur from somewhere within the SSL, when running +### test_socket_sll. They are too general to leave on by default. +### +###{ +### somewhere in SSL stuff +### Memcheck:Cond +### fun:memset +###} +###{ +### somewhere in SSL stuff +### Memcheck:Value4 +### fun:memset +###} +### +###{ +### somewhere in SSL stuff +### Memcheck:Cond +### fun:MD5_Update +###} +### +###{ +### somewhere in SSL stuff +### Memcheck:Value4 +### fun:MD5_Update +###} + +# +# All of these problems come from using test_socket_ssl +# +{ + from test_socket_ssl + Memcheck:Cond + fun:BN_bin2bn +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:BN_num_bits_word +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:BN_num_bits_word +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:BN_mod_exp_mont_word +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:BN_mod_exp_mont +} + +{ + from test_socket_ssl + Memcheck:Param + write(buf) + fun:write + obj:/usr/lib/libcrypto.so.0.9.7 +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:RSA_verify +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:RSA_verify +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:DES_set_key_unchecked +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:DES_encrypt2 +} + +{ + from test_socket_ssl + Memcheck:Cond + obj:/usr/lib/libssl.so.0.9.7 +} + +{ + from test_socket_ssl + Memcheck:Value4 + obj:/usr/lib/libssl.so.0.9.7 +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:BUF_MEM_grow_clean +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:memcpy + fun:ssl3_read_bytes +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:SHA1_Update +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:SHA1_Update +} + + From 4e2fe5b84d82b6d561f59977516bd3c3b54041f1 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 10 Mar 2013 14:21:26 +0100 Subject: [PATCH 0417/2237] added missing remote header file and fixed instance creation --- include/pygit2/remote.h | 38 ++++++++++++++++++++++++++++++++++++++ src/remote.c | 7 ++++--- src/repository.c | 12 ++++++++---- 3 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 include/pygit2/remote.h diff --git a/include/pygit2/remote.h b/include/pygit2/remote.h new file mode 100644 index 000000000..726a52aa8 --- /dev/null +++ b/include/pygit2/remote.h @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2013 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. + */ + +#ifndef INCLUDE_pygit2_remote_h +#define INCLUDE_pygit2_remote_h + +#define PY_SSIZE_T_CLEAN +#include +#include + +PyObject* Remote_init(Remote *self, PyObject *args, PyObject *kwds); +PyObject* Remote_fetch(Remote *self, PyObject *args); + +#endif diff --git a/src/remote.c b/src/remote.c index 42288e7a9..ccf7e559d 100644 --- a/src/remote.c +++ b/src/remote.c @@ -31,12 +31,13 @@ #include #include #include +#include extern PyObject *GitError; extern PyTypeObject RepositoryType; PyObject * -Remote_call(Remote *self, PyObject *args, PyObject *kwds) +Remote_init(Remote *self, PyObject *args, PyObject *kwds) { Repository* py_repo = NULL; char *name = NULL; @@ -239,7 +240,7 @@ PyTypeObject RemoteType = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc) Remote_call, /* tp_call */ + 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ @@ -260,7 +261,7 @@ PyTypeObject RemoteType = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - 0, /* tp_init */ + (initproc)Remote_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; diff --git a/src/repository.c b/src/repository.c index 4fb53099d..bba47e325 100644 --- a/src/repository.c +++ b/src/repository.c @@ -34,6 +34,7 @@ #include #include #include +#include extern PyObject *GitError; @@ -1049,20 +1050,23 @@ PyObject * Repository_remotes__get__(Repository *self) { git_strarray remotes; - PyObject* py_list = NULL, *py_tmp; + PyObject* py_list = NULL, *py_args = NULL; + Remote *py_remote; 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); + py_remote = PyObject_New(Remote, &RemoteType); + py_args = Py_BuildValue("Os", self, remotes.strings[i]); + Remote_init(py_remote, py_args, NULL); + PyList_SetItem(py_list, i, (PyObject*) py_remote); } git_strarray_free(&remotes); - return py_list; + return (PyObject*) py_list; } From 00d0f09f43c9ffa5da2324110446e48ac00aa8ce Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 7 Mar 2013 11:39:55 +0100 Subject: [PATCH 0418/2237] fixed compiler warnings --- src/pygit2.c | 2 +- src/repository.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index cd56a6e88..e713c0c40 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -69,7 +69,7 @@ PyDoc_STRVAR(init_repository__doc__, "Creates a new Git repository in the given path."); PyObject * -init_repository(PyObject *self, PyObject *args, PyObject *kw) { +init_repository(PyObject *self, PyObject *args) { git_repository *repo; const char *path; unsigned int bare; diff --git a/src/repository.c b/src/repository.c index 497e28dbd..4fb53099d 100644 --- a/src/repository.c +++ b/src/repository.c @@ -839,7 +839,7 @@ Repository_create_direct_reference(Repository *self, PyObject *args, { PyObject *py_obj; git_reference *c_reference; - char *c_name, *c_target; + char *c_name; git_oid oid; int err, force; @@ -879,7 +879,6 @@ Repository_create_symbolic_reference(Repository *self, PyObject *args, PyObject *py_obj; git_reference *c_reference; char *c_name, *c_target; - git_oid oid; int err, force; if (!PyArg_ParseTuple(args, "sOi", &c_name, &py_obj, &force)) From 59680a2b02487b22f9682188bceb154231bb7c25 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 10 Mar 2013 12:59:19 +0100 Subject: [PATCH 0419/2237] added valgrind python suppression file To start a valgrind memory check use the following command: valgrind -v --leak-check=full --suppressions=misc/valgrind-python.supp python -E -tt ./setup.py test If your python installation is not compiled with valgrind option, you get as well tons of false-positive leaks in valgrind. Just check for git_* function the the output. Have in my that circular memory dependencies can not easily be found with this approach. --- misc/valgrind-python.supp | 391 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) create mode 100644 misc/valgrind-python.supp diff --git a/misc/valgrind-python.supp b/misc/valgrind-python.supp new file mode 100644 index 000000000..15982cc3e --- /dev/null +++ b/misc/valgrind-python.supp @@ -0,0 +1,391 @@ +# +# This is a valgrind suppression file that should be used when using valgrind. +# +# Here's an example of running valgrind: +# +# cd python/dist/src +# valgrind --tool=memcheck --suppressions=Misc/valgrind-python.supp \ +# ./python -E -tt ./Lib/test/regrtest.py -u bsddb,network +# +# You must edit Objects/obmalloc.c and uncomment Py_USING_MEMORY_DEBUGGER +# to use the preferred suppressions with Py_ADDRESS_IN_RANGE. +# +# If you do not want to recompile Python, you can uncomment +# suppressions for PyObject_Free and PyObject_Realloc. +# +# See Misc/README.valgrind for more information. + +# all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Addr4 + fun:Py_ADDRESS_IN_RANGE +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Value4 + fun:Py_ADDRESS_IN_RANGE +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64 aka amd64) + Memcheck:Value8 + fun:Py_ADDRESS_IN_RANGE +} + +{ + ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value + Memcheck:Cond + fun:Py_ADDRESS_IN_RANGE +} + +# +# Leaks (including possible leaks) +# Hmmm, I wonder if this masks some real leaks. I think it does. +# Will need to fix that. +# + +{ + Suppress leaking the GIL. Happens once per process, see comment in ceval.c. + Memcheck:Leak + fun:malloc + fun:PyThread_allocate_lock + fun:PyEval_InitThreads +} + +{ + Suppress leaking the GIL after a fork. + Memcheck:Leak + fun:malloc + fun:PyThread_allocate_lock + fun:PyEval_ReInitThreads +} + +{ + Suppress leaking the autoTLSkey. This looks like it shouldn't leak though. + Memcheck:Leak + fun:malloc + fun:PyThread_create_key + fun:_PyGILState_Init + fun:Py_InitializeEx + fun:Py_Main +} + +{ + Hmmm, is this a real leak or like the GIL? + Memcheck:Leak + fun:malloc + fun:PyThread_ReInitTLS +} + +{ + Handle PyMalloc confusing valgrind (possibly leaked) + Memcheck:Leak + fun:realloc + fun:_PyObject_GC_Resize + fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING +} + +{ + Handle PyMalloc confusing valgrind (possibly leaked) + Memcheck:Leak + fun:malloc + fun:_PyObject_GC_New + fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING +} + +{ + Handle PyMalloc confusing valgrind (possibly leaked) + Memcheck:Leak + fun:malloc + fun:_PyObject_GC_NewVar + fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING +} + +# +# Non-python specific leaks +# + +{ + Handle pthread issue (possibly leaked) + Memcheck:Leak + fun:calloc + fun:allocate_dtv + fun:_dl_allocate_tls_storage + fun:_dl_allocate_tls +} + +{ + Handle pthread issue (possibly leaked) + Memcheck:Leak + fun:memalign + fun:_dl_allocate_tls_storage + fun:_dl_allocate_tls +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Addr4 + fun:PyObject_Free +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Value4 + fun:PyObject_Free +} + +{ + ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value + Memcheck:Cond + fun:PyObject_Free +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Addr4 + fun:PyObject_Realloc +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Value4 + fun:PyObject_Realloc +} + +{ + ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value + Memcheck:Cond + fun:PyObject_Realloc +} + +### +### All the suppressions below are for errors that occur within libraries +### that Python uses. The problems to not appear to be related to Python's +### use of the libraries. +### + +{ + Generic ubuntu ld problems + Memcheck:Addr8 + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so +} + +{ + Generic gentoo ld problems + Memcheck:Cond + obj:/lib/ld-2.3.4.so + obj:/lib/ld-2.3.4.so + obj:/lib/ld-2.3.4.so + obj:/lib/ld-2.3.4.so +} + +{ + DBM problems, see test_dbm + Memcheck:Param + write(buf) + fun:write + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + fun:dbm_close +} + +{ + DBM problems, see test_dbm + Memcheck:Value8 + fun:memmove + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + fun:dbm_store + fun:dbm_ass_sub +} + +{ + DBM problems, see test_dbm + Memcheck:Cond + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + fun:dbm_store + fun:dbm_ass_sub +} + +{ + DBM problems, see test_dbm + Memcheck:Cond + fun:memmove + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + fun:dbm_store + fun:dbm_ass_sub +} + +{ + GDBM problems, see test_gdbm + Memcheck:Param + write(buf) + fun:write + fun:gdbm_open + +} + +{ + ZLIB problems, see test_gzip + Memcheck:Cond + obj:/lib/libz.so.1.2.3 + obj:/lib/libz.so.1.2.3 + fun:deflate +} + +{ + Avoid problems w/readline doing a putenv and leaking on exit + Memcheck:Leak + fun:malloc + fun:xmalloc + fun:sh_set_lines_and_columns + fun:_rl_get_screen_size + fun:_rl_init_terminal_io + obj:/lib/libreadline.so.4.3 + fun:rl_initialize +} + +### +### These occur from somewhere within the SSL, when running +### test_socket_sll. They are too general to leave on by default. +### +###{ +### somewhere in SSL stuff +### Memcheck:Cond +### fun:memset +###} +###{ +### somewhere in SSL stuff +### Memcheck:Value4 +### fun:memset +###} +### +###{ +### somewhere in SSL stuff +### Memcheck:Cond +### fun:MD5_Update +###} +### +###{ +### somewhere in SSL stuff +### Memcheck:Value4 +### fun:MD5_Update +###} + +# +# All of these problems come from using test_socket_ssl +# +{ + from test_socket_ssl + Memcheck:Cond + fun:BN_bin2bn +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:BN_num_bits_word +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:BN_num_bits_word +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:BN_mod_exp_mont_word +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:BN_mod_exp_mont +} + +{ + from test_socket_ssl + Memcheck:Param + write(buf) + fun:write + obj:/usr/lib/libcrypto.so.0.9.7 +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:RSA_verify +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:RSA_verify +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:DES_set_key_unchecked +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:DES_encrypt2 +} + +{ + from test_socket_ssl + Memcheck:Cond + obj:/usr/lib/libssl.so.0.9.7 +} + +{ + from test_socket_ssl + Memcheck:Value4 + obj:/usr/lib/libssl.so.0.9.7 +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:BUF_MEM_grow_clean +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:memcpy + fun:ssl3_read_bytes +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:SHA1_Update +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:SHA1_Update +} + + From 8b2abc1ca731bfaff4290a386f86e7f49b1c1b39 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 10 Mar 2013 14:21:26 +0100 Subject: [PATCH 0420/2237] added missing remote header file and fixed instance creation --- include/pygit2/remote.h | 38 ++++++++++++++++++++++++++++++++++++++ src/remote.c | 7 ++++--- src/repository.c | 12 ++++++++---- 3 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 include/pygit2/remote.h diff --git a/include/pygit2/remote.h b/include/pygit2/remote.h new file mode 100644 index 000000000..726a52aa8 --- /dev/null +++ b/include/pygit2/remote.h @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2013 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. + */ + +#ifndef INCLUDE_pygit2_remote_h +#define INCLUDE_pygit2_remote_h + +#define PY_SSIZE_T_CLEAN +#include +#include + +PyObject* Remote_init(Remote *self, PyObject *args, PyObject *kwds); +PyObject* Remote_fetch(Remote *self, PyObject *args); + +#endif diff --git a/src/remote.c b/src/remote.c index 42288e7a9..ccf7e559d 100644 --- a/src/remote.c +++ b/src/remote.c @@ -31,12 +31,13 @@ #include #include #include +#include extern PyObject *GitError; extern PyTypeObject RepositoryType; PyObject * -Remote_call(Remote *self, PyObject *args, PyObject *kwds) +Remote_init(Remote *self, PyObject *args, PyObject *kwds) { Repository* py_repo = NULL; char *name = NULL; @@ -239,7 +240,7 @@ PyTypeObject RemoteType = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc) Remote_call, /* tp_call */ + 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ @@ -260,7 +261,7 @@ PyTypeObject RemoteType = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - 0, /* tp_init */ + (initproc)Remote_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; diff --git a/src/repository.c b/src/repository.c index 4fb53099d..bba47e325 100644 --- a/src/repository.c +++ b/src/repository.c @@ -34,6 +34,7 @@ #include #include #include +#include extern PyObject *GitError; @@ -1049,20 +1050,23 @@ PyObject * Repository_remotes__get__(Repository *self) { git_strarray remotes; - PyObject* py_list = NULL, *py_tmp; + PyObject* py_list = NULL, *py_args = NULL; + Remote *py_remote; 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); + py_remote = PyObject_New(Remote, &RemoteType); + py_args = Py_BuildValue("Os", self, remotes.strings[i]); + Remote_init(py_remote, py_args, NULL); + PyList_SetItem(py_list, i, (PyObject*) py_remote); } git_strarray_free(&remotes); - return py_list; + return (PyObject*) py_list; } From 8cb053985857ae04cf9ddc963034254c0e5f55d0 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 10 Mar 2013 14:10:22 +0100 Subject: [PATCH 0421/2237] fixing several memory leaks in pygit2 * fixed several ref counter issues * us Py_CLEAR through the whole lib * refactorization * Config does not need Repository pointer -> No need for cyclic garbage collecter support --- include/pygit2/config.h | 3 +- include/pygit2/types.h | 7 +- include/pygit2/utils.h | 22 +++--- src/config.c | 158 +++++++++++++++++++++------------------- src/object.c | 2 +- src/reference.c | 20 ++--- src/remote.c | 6 +- src/repository.c | 35 ++++++--- src/signature.c | 6 +- src/tree.c | 4 +- src/treebuilder.c | 2 +- src/utils.c | 2 +- src/walker.c | 2 +- 13 files changed, 151 insertions(+), 118 deletions(-) diff --git a/include/pygit2/config.h b/include/pygit2/config.h index 976ee3d6f..b0eba9353 100644 --- a/include/pygit2/config.h +++ b/include/pygit2/config.h @@ -32,6 +32,7 @@ #include #include +PyObject* wrap_config(char *c_path); PyObject* Config_get_global_config(void); PyObject* Config_get_system_config(void); PyObject* Config_add_file(Config *self, PyObject *args, PyObject *kwds); @@ -39,6 +40,6 @@ PyObject* Config_getitem(Config *self, PyObject *key); PyObject* Config_foreach(Config *self, PyObject *args); PyObject* Config_get_multivar(Config *self, PyObject *args); PyObject* Config_set_multivar(Config *self, PyObject *args); +int Config_init(Config *self, PyObject *args, PyObject *kwds); int Config_setitem(Config *self, PyObject *key, PyObject *value); - #endif diff --git a/include/pygit2/types.h b/include/pygit2/types.h index dfd08c03d..feff58e54 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -57,10 +57,14 @@ OBJECT_STRUCT(Blob, git_blob, blob) 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) OBJECT_STRUCT(Diff, git_diff_list, list) +typedef struct { + PyObject_HEAD + git_config* config; +} Config; + typedef struct { PyObject_HEAD git_diff_list* list; @@ -128,7 +132,6 @@ typedef struct { typedef struct { PyObject_HEAD - Reference *reference; git_reflog *reflog; int i; int size; diff --git a/include/pygit2/utils.h b/include/pygit2/utils.h index 161b1946f..d1ecc42b8 100644 --- a/include/pygit2/utils.h +++ b/include/pygit2/utils.h @@ -95,9 +95,6 @@ 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)\ {#name, (PyCFunction) type ## _ ## name, args, type ## _ ## name ## __doc__} @@ -119,21 +116,22 @@ char * py_str_to_c_str(PyObject *value, const char *encoding); #define MEMBER(type, attr, attr_type, docstr)\ {#attr, attr_type, offsetof(type, attr), 0, PyDoc_STR(docstr)} -/* Helpers for memory allocation */ +/* Helpers for memory allocation */ +#define CALLOC(ptr, num, size, label) \ + ptr = calloc((num), size);\ + if (ptr == NULL) {\ + err = GIT_ERROR;\ + giterr_set_oom();\ + goto label;\ + } #define MALLOC(ptr, size, label) \ - ptr = realloc(ptr, size * sizeof(char));\ + ptr = malloc(size);\ if (ptr == NULL) {\ err = GIT_ERROR;\ giterr_set_oom();\ goto label;\ - }\ - -#define FREE(to_free)\ - if (to_free != NULL) { free(to_free); to_free = NULL; } -#define FREE_FUNC(to_free, fnct)\ - if (to_free != NULL) { fnct(to_free); to_free = NULL; } - + } #endif diff --git a/src/config.c b/src/config.c index b2167b6c5..9cf0b553f 100644 --- a/src/config.c +++ b/src/config.c @@ -34,10 +34,28 @@ extern PyTypeObject ConfigType; + +PyObject * +wrap_config(char *c_path) { + int err; + PyObject *py_path; + Config *py_config; + + py_path = Py_BuildValue("(s)", c_path); + py_config = PyObject_New(Config, &ConfigType); + + err = Config_init(py_config, py_path, NULL); + if (err < 0) + return NULL; + + return (PyObject*) py_config; +} + + int Config_init(Config *self, PyObject *args, PyObject *kwds) { - char *path; + char *path = NULL; int err; if (kwds) { @@ -46,18 +64,17 @@ Config_init(Config *self, PyObject *args, PyObject *kwds) return -1; } - if (PySequence_Length(args) > 0) { - if (!PyArg_ParseTuple(args, "s", &path)) { - return -1; - } - - err = git_config_open_ondisk(&self->config, path); + if (!PyArg_ParseTuple(args, "|s", &path)) + return -1; - } else { + if (path == NULL) err = git_config_new(&self->config); - } + else + err = git_config_open_ondisk(&self->config, path); if (err < 0) { + git_config_free(self->config); + if (err == GIT_ENOTFOUND) { Error_set_exc(PyExc_IOError); } else { @@ -70,35 +87,14 @@ Config_init(Config *self, PyObject *args, PyObject *kwds) return 0; } + void Config_dealloc(Config *self) { - PyObject_GC_UnTrack(self); - Py_XDECREF(self->repo); git_config_free(self->config); - PyObject_GC_Del(self); + PyObject_Del(self); } -int -Config_traverse(Config *self, visitproc visit, void *arg) -{ - Py_VISIT(self->repo); - return 0; -} - -PyObject * -Config_open(char *c_path) { - PyObject *py_path = Py_BuildValue("(s)", c_path); - Config *config = PyObject_GC_New(Config, &ConfigType); - - Config_init(config, py_path, NULL); - - Py_INCREF(config); - - return (PyObject *)config; -} - - PyDoc_STRVAR(Config_get_global_config__doc__, "get_global_config() -> Config\n" "\n" @@ -116,10 +112,11 @@ Config_get_global_config(void) PyErr_SetString(PyExc_IOError, "Global config file not found."); return NULL; } + return Error_set(err); } - return Config_open(path); + return wrap_config(path); } @@ -143,9 +140,10 @@ Config_get_system_config(void) return Error_set(err); } - return Config_open(path); + return wrap_config(path); } + int Config_contains(Config *self, PyObject *py_key) { int err; @@ -158,9 +156,11 @@ Config_contains(Config *self, PyObject *py_key) { err = git_config_get_string(&c_value, self->config, c_key); free(c_key); - if (err == GIT_ENOTFOUND) - return 0; + if (err < 0) { + if (err == GIT_ENOTFOUND) + return 0; + Error_set(err); return -1; } @@ -168,69 +168,71 @@ Config_contains(Config *self, PyObject *py_key) { return 1; } + PyObject * Config_getitem(Config *self, PyObject *py_key) { - int err; - int64_t c_intvalue; - int c_boolvalue; - const char *c_charvalue; - char *c_key; - - if (!(c_key = py_str_to_c_str(py_key, NULL))) + long value_int; + int err, value_bool; + const char *value_str; + char *key; + PyObject* py_value; + + key = py_str_to_c_str(py_key, NULL); + if (key == NULL) return NULL; - err = git_config_get_int64(&c_intvalue, self->config, c_key); - if (err == GIT_OK) { - free(c_key); - return PyInt_FromLong((long)c_intvalue); - } + err = git_config_get_string(&value_str, self->config, key); + if (err < 0) + goto cleanup; - err = git_config_get_bool(&c_boolvalue, self->config, c_key); - if (err == GIT_OK) { - free(c_key); - return PyBool_FromLong((long)c_boolvalue); - } + if (git_config_parse_int64(&value_int, value_str) == 0) + py_value = PyLong_FromLong(value_int); + else if(git_config_parse_bool(&value_bool, value_str) == 0) + py_value = PyBool_FromLong(value_bool); + else + py_value = PyUnicode_FromString(value_str); + +cleanup: + free(key); - err = git_config_get_string(&c_charvalue, self->config, c_key); - free(c_key); if (err < 0) { if (err == GIT_ENOTFOUND) { PyErr_SetObject(PyExc_KeyError, py_key); return NULL; } + return Error_set(err); } - return PyUnicode_FromString(c_charvalue); + return py_value; } int Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) { int err; - char *c_key; - char *py_str; + char *key, *value; - if (!(c_key = py_str_to_c_str(py_key, NULL))) + key = py_str_to_c_str(py_key, NULL); + if (key == NULL) return -1; - if (!py_value) { - err = git_config_delete_entry(self->config, c_key); - } else if (PyBool_Check(py_value)) { - err = git_config_set_bool(self->config, c_key, + if (py_value == NULL) + err = git_config_delete_entry(self->config, key); + else if (PyBool_Check(py_value)) { + err = git_config_set_bool(self->config, key, (int)PyObject_IsTrue(py_value)); } else if (PyInt_Check(py_value)) { - err = git_config_set_int64(self->config, c_key, + err = git_config_set_int64(self->config, key, (int64_t)PyInt_AsLong(py_value)); } else { - py_value = PyObject_Str(py_value); - py_str = py_str_to_c_str(py_value, NULL); - err = git_config_set_string(self->config, c_key, py_str); - free(py_str); + value = py_str_to_c_str(py_value, NULL); + err = git_config_set_string(self->config, key, value); + free(value); } - free(c_key); + free(key); if (err < 0) { Error_set(err); return -1; @@ -263,6 +265,8 @@ Config_foreach_callback_wrapper(const git_config_entry *entry, void *c_payload) if ((c_result = PyLong_AsLong(py_result) == -1)) return -1; + Py_CLEAR(args); + return c_result; } @@ -282,7 +286,7 @@ Config_foreach(Config *self, PyObject *args) { int ret; PyObject *py_callback; - PyObject *py_payload; + PyObject *py_payload = NULL; if (!PyArg_ParseTuple(args, "O|O", &py_callback, &py_payload)) return NULL; @@ -338,13 +342,14 @@ PyDoc_STRVAR(Config_get_multivar__doc__, int Config_get_multivar_fn_wrapper(const git_config_entry *value, void *data) { - PyObject *list = (PyObject *)data; PyObject *item = NULL; if (!(item = PyUnicode_FromString(value->value))) return -2; - PyList_Append(list, item); + PyList_Append((PyObject *)data, item); + + Py_CLEAR(item); return 0; } @@ -353,17 +358,20 @@ PyObject * Config_get_multivar(Config *self, PyObject *args) { int err; - PyObject *list = PyList_New(0); + PyObject *list; const char *name = NULL; const char *regex = NULL; if (!PyArg_ParseTuple(args, "s|s", &name, ®ex)) return NULL; + list = PyList_New(0); err = git_config_get_multivar(self->config, name, regex, Config_get_multivar_fn_wrapper, (void *)list); if (err < 0) { + Py_CLEAR(list); + if (err == GIT_ENOTFOUND) Error_set(err); else @@ -454,9 +462,9 @@ PyTypeObject ConfigType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ Config__doc__, /* tp_doc */ - (traverseproc)Config_traverse, /* tp_traverse */ + 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ diff --git a/src/object.c b/src/object.c index 40e30e4c6..314bac21d 100644 --- a/src/object.c +++ b/src/object.c @@ -43,8 +43,8 @@ extern PyTypeObject TagType; void Object_dealloc(Object* self) { + Py_CLEAR(self->repo); git_object_free(self->obj); - Py_XDECREF(self->repo); PyObject_Del(self); } diff --git a/src/reference.c b/src/reference.c index 129ed44ec..b508567d0 100644 --- a/src/reference.c +++ b/src/reference.c @@ -43,9 +43,8 @@ extern PyTypeObject RefLogEntryType; void RefLogIter_dealloc(RefLogIter *self) { - Py_XDECREF(self->reference); git_reflog_free(self->reflog); - PyObject_GC_Del(self); + PyObject_Del(self); } PyObject* RefLogIter_iternext(PyObject *self) @@ -146,7 +145,9 @@ Reference_delete(Reference *self, PyObject *args) if (err < 0) return Error_set(err); + git_reference_free(self->reference); self->reference = NULL; /* Invalidate the pointer */ + Py_RETURN_NONE; } @@ -172,6 +173,7 @@ Reference_rename(Reference *self, PyObject *py_name) /* Rename */ err = git_reference_rename(&new_reference, self->reference, c_name, 0); + git_reference_free(self->reference); free(c_name); if (err < 0) return Error_set(err); @@ -255,6 +257,7 @@ Reference_target__set__(Reference *self, PyObject *py_name) return -1; } + git_reference_free(self->reference); self->reference = new_ref; return 0; } @@ -316,6 +319,7 @@ Reference_oid__set__(Reference *self, PyObject *py_hex) return -1; } + git_reference_free(self->reference); self->reference = new_ref; return 0; } @@ -371,14 +375,10 @@ Reference_log(Reference *self) CHECK_REFERENCE(self); iter = PyObject_New(RefLogIter, &RefLogIterType); - if (iter) { - iter->reference = self; + if (iter != NULL) { git_reflog_read(&iter->reflog, self->reference); iter->size = git_reflog_entrycount(iter->reflog); iter->i = 0; - - Py_INCREF(self); - Py_INCREF(iter); } return (PyObject*)iter; } @@ -398,9 +398,9 @@ RefLogEntry_init(RefLogEntry *self, PyObject *args, PyObject *kwds) static void RefLogEntry_dealloc(RefLogEntry *self) { - Py_XDECREF(self->oid_old); - Py_XDECREF(self->oid_new); - Py_XDECREF(self->committer); + Py_CLEAR(self->oid_old); + Py_CLEAR(self->oid_new); + Py_CLEAR(self->committer); free(self->message); PyObject_Del(self); } diff --git a/src/remote.c b/src/remote.c index ccf7e559d..0d55a54cb 100644 --- a/src/remote.c +++ b/src/remote.c @@ -47,6 +47,7 @@ Remote_init(Remote *self, PyObject *args, PyObject *kwds) return NULL; self->repo = py_repo; + Py_INCREF(self->repo); err = git_remote_load(&self->remote, py_repo->repo, name); if (err < 0) @@ -59,6 +60,7 @@ Remote_init(Remote *self, PyObject *args, PyObject *kwds) static void Remote_dealloc(Remote *self) { + Py_CLEAR(self->repo); git_remote_free(self->remote); PyObject_Del(self); } @@ -81,6 +83,7 @@ Remote_name__set__(Remote *self, PyObject* py_name) name = py_str_to_c_str(py_name, NULL); if (name != NULL) { err = git_remote_rename(self->remote, name, NULL, NULL); + free(name); if (err == GIT_OK) return 0; @@ -105,11 +108,12 @@ int Remote_url__set__(Remote *self, PyObject* py_url) { int err; - char* url; + char* url = NULL; url = py_str_to_c_str(py_url, NULL); if (url != NULL) { err = git_remote_set_url(self->remote, url); + free(url); if (err == GIT_OK) return 0; diff --git a/src/repository.c b/src/repository.c index bba47e325..87ff39562 100644 --- a/src/repository.c +++ b/src/repository.c @@ -102,6 +102,9 @@ Repository_init(Repository *self, PyObject *args, PyObject *kwds) return -1; } + self->config = NULL; + self->index = NULL; + return 0; } @@ -109,7 +112,8 @@ void Repository_dealloc(Repository *self) { PyObject_GC_UnTrack(self); - Py_XDECREF(self->index); + Py_CLEAR(self->index); + Py_CLEAR(self->config); git_repository_free(self->repo); PyObject_GC_Del(self); } @@ -494,7 +498,7 @@ PyDoc_STRVAR(Repository_config__doc__, "(if they are available)."); PyObject * -Repository_config__get__(Repository *self, void *closure) +Repository_config__get__(Repository *self) { int err; git_config *config; @@ -507,20 +511,18 @@ Repository_config__get__(Repository *self, void *closure) if (err < 0) return Error_set(err); - py_config = PyObject_GC_New(Config, &ConfigType); - if (!py_config) { + py_config = PyObject_New(Config, &ConfigType); + if (py_config == NULL) { git_config_free(config); return NULL; } - Py_INCREF(self); - py_config->repo = self; py_config->config = config; - PyObject_GC_Track(py_config); self->config = (PyObject*)py_config; + } else { + Py_INCREF(self->config); } - Py_INCREF(self->config); return self->config; } @@ -815,6 +817,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) return err_obj; } free(c_name); + /* 3- Make an instance of Reference and return it */ return wrap_reference(c_reference); } @@ -888,13 +891,19 @@ Repository_create_symbolic_reference(Repository *self, PyObject *args, #if PY_MAJOR_VERSION == 2 c_target = PyString_AsString(py_obj); #else - c_target = PyString_AsString(PyUnicode_AsASCIIString(py_obj)); + // increases ref counter, so we have to release it afterwards + PyObject* py_str = PyUnicode_AsASCIIString(py_obj); + c_target = PyString_AsString(py_str); #endif if (c_target == NULL) return NULL; err = git_reference_symbolic_create(&c_reference, self->repo, c_name, c_target, force); + #if PY_MAJOR_VERSION > 2 + Py_CLEAR(py_str); + #endif + if (err < 0) return Error_set(err); @@ -914,9 +923,14 @@ read_status_cb(const char *path, unsigned int status_flags, void *payload) /* This is the callback that will be called in git_status_foreach. It * will be called for every path.*/ PyObject *flags; + int err; flags = PyInt_FromLong((long) status_flags); - PyDict_SetItemString(payload, path, flags); + err = PyDict_SetItemString(payload, path, flags); + Py_CLEAR(flags); + + if (err < 0) + return GIT_ERROR; return GIT_OK; } @@ -1103,6 +1117,7 @@ Repository_checkout(Repository *self, PyObject *args, PyObject *kw) err = git_repository_set_head(self->repo, git_reference_name(ref->reference)); } + git_object_free(object); } } else { /* checkout from head / index */ opts.checkout_strategy = strategy; diff --git a/src/signature.c b/src/signature.c index b0e85663b..25dae0ba7 100644 --- a/src/signature.c +++ b/src/signature.c @@ -82,12 +82,13 @@ void Signature_dealloc(Signature *self) { if (self->obj) - Py_DECREF(self->obj); + Py_CLEAR(self->obj); else { git_signature_free((git_signature*)self->signature); free((char*)self->encoding); } - Py_TYPE(self)->tp_free((PyObject*)self); + + PyObject_Del(self); } @@ -221,6 +222,7 @@ build_signature(Object *obj, const git_signature *signature, Signature *py_signature; py_signature = PyObject_New(Signature, &SignatureType); + if (py_signature) { Py_INCREF(obj); py_signature->obj = obj; diff --git a/src/tree.c b/src/tree.c index 749329929..6121c3fd8 100644 --- a/src/tree.c +++ b/src/tree.c @@ -42,7 +42,7 @@ extern PyTypeObject IndexType; void TreeEntry_dealloc(TreeEntry *self) { - Py_XDECREF(self->owner); + Py_CLEAR(self->owner); git_tree_entry_free((git_tree_entry*)self->entry); PyObject_Del(self); } @@ -339,6 +339,7 @@ Tree_diff(Tree *self, PyObject *args) PyErr_SetObject(PyExc_TypeError, py_obj); return NULL; } + if (err < 0) return Error_set(err); @@ -352,6 +353,7 @@ Tree_diff(Tree *self, PyObject *args) return (PyObject*)py_diff; } + PySequenceMethods Tree_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ diff --git a/src/treebuilder.c b/src/treebuilder.c index 1b06b318b..d7205bc01 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -37,7 +37,7 @@ void TreeBuilder_dealloc(TreeBuilder *self) { - Py_XDECREF(self->repo); + Py_CLEAR(self->repo); git_treebuilder_free(self->bld); PyObject_Del(self); } diff --git a/src/utils.c b/src/utils.c index cfa9cae19..efd7887c3 100644 --- a/src/utils.c +++ b/src/utils.c @@ -36,13 +36,13 @@ extern PyTypeObject ReferenceType; * the string contained in the value argument. */ char * py_str_to_c_str(PyObject *value, const char *encoding) { + char *c_str = NULL; /* Case 1: byte string */ if (PyString_Check(value)) return strdup(PyString_AsString(value)); /* Case 2: text string */ if (PyUnicode_Check(value)) { - char *c_str = NULL; if (encoding == NULL) value = PyUnicode_AsUTF8String(value); diff --git a/src/walker.c b/src/walker.c index d2481a174..7e48ab366 100644 --- a/src/walker.c +++ b/src/walker.c @@ -38,8 +38,8 @@ extern PyTypeObject CommitType; void Walker_dealloc(Walker *self) { + Py_CLEAR(self->repo); git_revwalk_free(self->walk); - Py_DECREF(self->repo); PyObject_Del(self); } From d8bb184a3181a0325a8a3bcbf44f5a9059e54231 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 10 Mar 2013 12:56:27 +0100 Subject: [PATCH 0422/2237] diff refactorization --- include/pygit2/types.h | 20 ++-- src/diff.c | 247 ++++++++++++++++++----------------------- src/pygit2.c | 4 +- test/test_diff.py | 71 ++++++------ 4 files changed, 152 insertions(+), 190 deletions(-) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index feff58e54..27bccda97 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -67,16 +67,21 @@ typedef struct { typedef struct { PyObject_HEAD - git_diff_list* list; + Diff* diff; size_t i; size_t n; } DiffIter; typedef struct { PyObject_HEAD - PyObject* files; PyObject* hunks; -} DiffEntry; + const char * old_file_path; + const char * new_file_path; + char* old_oid; + char* new_oid; + unsigned status; + unsigned similarity; +} Patch; typedef struct { PyObject_HEAD @@ -86,18 +91,11 @@ typedef struct { typedef struct { PyObject_HEAD - const char *header; + PyObject* lines; int old_start; int old_lines; - char* old_oid; - int old_mode; - const char* old_file; int new_start; int new_lines; - char* new_oid; - int new_mode; - const char* new_file; - PyObject *data; } Hunk; typedef struct { diff --git a/src/diff.c b/src/diff.c index c9b49834a..b8289d50a 100644 --- a/src/diff.c +++ b/src/diff.c @@ -40,123 +40,106 @@ extern PyTypeObject IndexType; extern PyTypeObject DiffType; extern PyTypeObject HunkType; -PyTypeObject DiffEntryType; +PyTypeObject PatchType; PyObject* -diff_get_patch_byindex(git_diff_list* list, size_t i) +diff_get_patch_byindex(git_diff_list* list, size_t idx) { const git_diff_delta* delta; const git_diff_range* range; git_diff_patch* patch = NULL; - - char buffer[41]; - const char* hunk_content; - size_t hunk_amounts, j, hunk_header_len, hunk_lines; + size_t i, j, hunk_amounts, lines_in_hunk, line_len, header_len; + const char* line, *header; int err; + Hunk *py_hunk = NULL; + Patch *py_patch = NULL; - PyObject *file; - Hunk *py_hunk; - DiffEntry *py_entry = NULL; - - err = git_diff_get_patch(&patch, &delta, list, i); - - if (err == GIT_OK) { - py_entry = (DiffEntry*) INSTANCIATE_CLASS(DiffEntryType, NULL); - if (py_entry != NULL) { - if (err == GIT_OK) { - file = Py_BuildValue("(s,s,i,i)", - delta->old_file.path, - delta->new_file.path, - delta->status, - delta->similarity - ); - - PyList_Append((PyObject*) py_entry->files, file); - } - - hunk_amounts = git_diff_patch_num_hunks(patch); - - for (j=0; j < hunk_amounts; ++j) { - err = git_diff_patch_get_hunk(&range, &hunk_content, - &hunk_header_len, &hunk_lines, patch, j); - - if (err == GIT_OK) { - py_hunk = (Hunk*)PyType_GenericNew(&HunkType, NULL, NULL); - if (py_hunk != NULL) { - py_hunk->old_file = delta->old_file.path; - py_hunk->new_file = delta->new_file.path; - py_hunk->header = hunk_content; - py_hunk->old_start = range->old_start; - py_hunk->old_lines = range->old_lines; - py_hunk->new_start = range->new_start; - py_hunk->new_lines = range->new_lines; - - git_oid_fmt(buffer, &delta->old_file.oid); - py_hunk->old_oid = calloc(41, sizeof(char)); - memcpy(py_hunk->old_oid, buffer, 40); - - git_oid_fmt(buffer, &delta->new_file.oid); - py_hunk->new_oid = calloc(41, sizeof(char)); - memcpy(py_hunk->new_oid, buffer, 40); - - py_hunk->data = Py_BuildValue("(s#,i)", - hunk_content, hunk_header_len, - hunk_lines); - - PyList_Append((PyObject*) py_entry->hunks, - (PyObject*) py_hunk); - } - } - } - } - } - + err = git_diff_get_patch(&patch, &delta, list, idx); if (err < 0) return Error_set(err); - return (PyObject*) py_entry; -} + py_patch = (Patch*) PyType_GenericNew(&PatchType, NULL, NULL); + if (py_patch != NULL) { + py_patch->old_file_path = delta->old_file.path; + py_patch->new_file_path = delta->new_file.path; + py_patch->status = delta->status; + py_patch->similarity = delta->similarity; + py_patch->old_oid = git_oid_allocfmt(&delta->old_file.oid); + py_patch->new_oid = git_oid_allocfmt(&delta->new_file.oid); + + + hunk_amounts = git_diff_patch_num_hunks(patch); + py_patch->hunks = PyList_New(hunk_amounts); + for (i=0; i < hunk_amounts; ++i) { + err = git_diff_patch_get_hunk(&range, &header, &header_len, + &lines_in_hunk, patch, i); + + if (err < 0) + goto cleanup; + + py_hunk = (Hunk*)PyType_GenericNew(&HunkType, NULL, NULL); + if (py_hunk != NULL) { + py_hunk->old_start = range->old_start; + py_hunk->old_lines = range->old_lines; + py_hunk->new_start = range->new_start; + py_hunk->new_lines = range->new_lines; + + py_hunk->lines = PyList_New(lines_in_hunk + 1); + PyList_SetItem(py_hunk->lines, 0, + PyUnicode_FromStringAndSize(header, header_len)); + for (j=1; j < lines_in_hunk + 1; ++j) { + err = git_diff_patch_get_line_in_hunk(NULL, &line, + &line_len, NULL, NULL, patch, i, j - 1); + + if (err < 0) + goto cleanup; + + PyList_SetItem(py_hunk->lines, j, + PyUnicode_FromStringAndSize(line, line_len)); + } -PyObject * -DiffEntry_call(DiffEntry *self, PyObject *args, PyObject *kwds) -{ - self->files = PyList_New(0); - if (self->files == NULL) { - Py_XDECREF(self); - return NULL; + PyList_SetItem((PyObject*) py_patch->hunks, i, + (PyObject*) py_hunk); + } + } } - self->hunks = PyList_New(0); - if (self->hunks == NULL) { - Py_XDECREF(self); - return NULL; - } +cleanup: + git_diff_patch_free(patch); - return (PyObject*) self; + return (err < 0) ? Error_set(err) : (PyObject*) py_patch; } static void -DiffEntry_dealloc(DiffEntry *self) +Patch_dealloc(Patch *self) { - Py_DECREF((PyObject*) self->files); - Py_DECREF((PyObject*) self->hunks); + Py_CLEAR(self->hunks); + free(self->old_oid); + free(self->new_oid); + // we do not have to free old_file_path and new_file_path, they will + // be freed by git_diff_list_free in Diff_dealloc PyObject_Del(self); } -PyMemberDef DiffEntry_members[] = { - MEMBER(DiffEntry, files, T_OBJECT, "files"), - MEMBER(DiffEntry, hunks, T_OBJECT, "hunks"), +PyMemberDef Patch_members[] = { + MEMBER(Patch, old_file_path, T_STRING, "old file path"), + MEMBER(Patch, new_file_path, T_STRING, "new file path"), + MEMBER(Patch, old_oid, T_STRING, "old oid"), + MEMBER(Patch, new_oid, T_STRING, "new oid"), + MEMBER(Patch, status, T_INT, "status"), + MEMBER(Patch, similarity, T_INT, "similarity"), + MEMBER(Patch, hunks, T_OBJECT, "hunks"), {NULL} }; -PyDoc_STRVAR(DiffEntry__doc__, "Diff entry object."); +PyDoc_STRVAR(Patch__doc__, "Diff patch object."); -PyTypeObject DiffEntryType = { +PyTypeObject PatchType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.DiffEntry", /* tp_name */ - sizeof(DiffEntry), /* tp_basicsize */ + "_pygit2.Patch", /* tp_name */ + sizeof(Patch), /* tp_basicsize */ 0, /* tp_itemsize */ - (destructor)DiffEntry_dealloc, /* tp_dealloc */ + (destructor)Patch_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -166,13 +149,13 @@ PyTypeObject DiffEntryType = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc) DiffEntry_call, /* tp_call */ + 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - DiffEntry__doc__, /* tp_doc */ + Patch__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -180,7 +163,7 @@ PyTypeObject DiffEntryType = { 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ - DiffEntry_members, /* tp_members */ + Patch_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ @@ -197,7 +180,7 @@ PyObject * DiffIter_iternext(DiffIter *self) { if (self->i < self->n) - return diff_get_patch_byindex(self->list, self->i++); + return diff_get_patch_byindex(self->diff->list, self->i++); PyErr_SetNone(PyExc_StopIteration); return NULL; @@ -206,7 +189,7 @@ DiffIter_iternext(DiffIter *self) void DiffIter_dealloc(DiffIter *self) { - Py_CLEAR(self->list); + Py_CLEAR(self->diff); PyObject_Del(self); } @@ -251,37 +234,39 @@ Diff_patch__get__(Diff *self) { const git_diff_delta* delta; git_diff_patch* patch; - char* str = NULL, *buffer = NULL; - int err = 0; - size_t i, len, num, size; + char **strings = NULL; + char *buffer = NULL; + int err = GIT_ERROR; + size_t i, len, num; PyObject *py_patch = NULL; num = git_diff_num_deltas(self->list); - for (i = 0; i < num ; ++i) { - err = git_diff_get_patch(&patch, &delta, self->list, i); - - if (err < 0 || (err = git_diff_patch_to_str(&str, patch)) < 0) - goto error; + MALLOC(strings, num * sizeof(char*), cleanup); - len = strlen(str) + 1; - size = (buffer == NULL) ? len : strlen(buffer) + len; - MALLOC(buffer, size, error); - - if (len == size) - strcpy(buffer, str); - else - strcat(buffer, str); + for (i = 0, len = 1; i < num ; ++i) { + err = git_diff_get_patch(&patch, &delta, self->list, i); + if (err < 0) + goto cleanup; + + err = git_diff_patch_to_str(&(strings[i]), patch); + if (err < 0) + goto cleanup; + + len += strlen(strings[i]); + git_diff_patch_free(patch); + } - FREE(str); + CALLOC(buffer, (len + 1), sizeof(char), cleanup); + for (i = 0; i < num; ++i) { + strcat(buffer, strings[i]); + free(strings[i]); } + free(strings); py_patch = PyUnicode_FromString(buffer); + free(buffer); -error: - FREE(str); - FREE(buffer); - FREE_FUNC(patch, git_diff_patch_free); - +cleanup: return (err < 0) ? Error_set(err) : py_patch; } @@ -289,34 +274,16 @@ Diff_patch__get__(Diff *self) static void Hunk_dealloc(Hunk *self) { - if (self->header != NULL) { - free((void*) self->header); - } - if (self->new_file != NULL) { - free((void*) self->new_file); - } - if (self->old_file != NULL) { - free((void*) self->old_file); - } - Py_XDECREF(self->old_oid); - Py_XDECREF(self->new_oid); - Py_XDECREF(self->data); + Py_CLEAR(self->lines); PyObject_Del(self); } PyMemberDef Hunk_members[] = { - MEMBER(Hunk, header, T_STRING, "Header."), MEMBER(Hunk, old_start, T_INT, "Old start."), MEMBER(Hunk, old_lines, T_INT, "Old lines."), - MEMBER(Hunk, old_mode, T_INT, "Old mode."), - MEMBER(Hunk, old_file, T_STRING, "Old file."), - MEMBER(Hunk, old_oid, T_STRING, "Old oid."), MEMBER(Hunk, new_start, T_INT, "New start."), MEMBER(Hunk, new_lines, T_INT, "New lines."), - MEMBER(Hunk, new_mode, T_INT, "New mode."), - MEMBER(Hunk, new_file, T_STRING, "New file."), - MEMBER(Hunk, new_oid, T_STRING, "New oid."), - MEMBER(Hunk, data, T_OBJECT, "Data."), + MEMBER(Hunk, lines, T_OBJECT, "Lines."), {NULL} }; @@ -393,7 +360,7 @@ Diff_merge(Diff *self, PyObject *args) PyDoc_STRVAR(Diff_find_similar__doc__, "find_similar([flags])\n" "\n" - "Find renamed files in diff."); + "Find renamed files in diff and updates them in-place in the diff itself."); PyObject * Diff_find_similar(Diff *self, PyObject *args) @@ -417,9 +384,9 @@ Diff_iter(Diff *self) DiffIter *iter; iter = PyObject_New(DiffIter, &DiffIterType); - if (iter) { + if (iter != NULL) { Py_INCREF(self); - iter->list = self->list; + iter->diff = self; iter->i = 0; iter->n = git_diff_num_deltas(self->list); } @@ -444,7 +411,7 @@ static void Diff_dealloc(Diff *self) { git_diff_list_free(self->list); - Py_XDECREF(self->repo); + Py_CLEAR(self->repo); PyObject_Del(self); } diff --git a/src/pygit2.c b/src/pygit2.c index e713c0c40..09573ef2b 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -42,7 +42,7 @@ extern PyTypeObject ObjectType; extern PyTypeObject CommitType; extern PyTypeObject DiffType; extern PyTypeObject DiffIterType; -extern PyTypeObject DiffEntryType; +extern PyTypeObject PatchType; extern PyTypeObject HunkType; extern PyTypeObject TreeType; extern PyTypeObject TreeBuilderType; @@ -201,7 +201,7 @@ moduleinit(PyObject* m) return NULL; if (PyType_Ready(&DiffIterType) < 0) return NULL; - if (PyType_Ready(&DiffEntryType) < 0) + if (PyType_Ready(&PatchType) < 0) return NULL; if (PyType_Ready(&HunkType) < 0) return NULL; diff --git a/test/test_diff.py b/test/test_diff.py index 8cb7b2c4a..54290fc34 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -31,7 +31,6 @@ from __future__ import unicode_literals import unittest import pygit2 -import itertools from pygit2 import GIT_DIFF_INCLUDE_UNMODIFIED from . import utils @@ -84,22 +83,27 @@ 'subdir/modified_file' ] +HUNK_EXPECTED = """@@ -1 +1 @@ +a contents 2 +a contents +""" + class DiffDirtyTest(utils.DirtyRepoTestCase): def test_diff_empty_index(self): repo = self.repo head = repo[repo.lookup_reference('HEAD').resolve().oid] diff = head.tree.diff(repo.index) - files = [[x[0] for x in entry.files] for entry in diff] - self.assertEqual(DIFF_INDEX_EXPECTED, list(itertools.chain(*files))) + files = [patch.new_file_path for patch in diff] + self.assertEqual(DIFF_INDEX_EXPECTED, files) def test_workdir_to_tree(self): repo = self.repo head = repo[repo.lookup_reference('HEAD').resolve().oid] diff = head.tree.diff() - files = [[x[0] for x in entry.files] for entry in diff] - self.assertEqual(DIFF_WORKDIR_EXPECTED, list(itertools.chain(*files))) + files = [patch.new_file_path for patch in diff] + self.assertEqual(DIFF_WORKDIR_EXPECTED, files) class DiffTest(utils.BareRepoTestCase): @@ -113,8 +117,8 @@ def test_diff_empty_index(self): head = repo[repo.lookup_reference('HEAD').resolve().oid] diff = head.tree.diff(repo.index) - files = [[x[0].split('/')[0] for x in entry.files] for entry in diff] - self.assertEqual([x.name for x in head.tree], list(itertools.chain(*files))) + files = [patch.new_file_path.split('/')[0] for patch in diff] + self.assertEqual([x.name for x in head.tree], files) def test_diff_tree(self): commit_a = self.repo[COMMIT_SHA1_1] @@ -125,20 +129,17 @@ def test_diff_tree(self): # self.assertIsNotNone is 2.7 only self.assertTrue(diff is not None) # self.assertIn is 2.7 only - self.assertAny(lambda x: ('a', 'a', 3, 0) in x.files, diff) self.assertEqual(2, sum(map(lambda x: len(x.hunks), diff))) - hunk = diff[0].hunks[0] + patch = diff[0] + hunk = patch.hunks[0] self.assertEqual(hunk.old_start, 1) self.assertEqual(hunk.old_lines, 1) self.assertEqual(hunk.new_start, 1) self.assertEqual(hunk.new_lines, 1) - self.assertEqual(hunk.old_file, 'a') - self.assertEqual(hunk.new_file, 'a') - - #self.assertEqual(hunk.data[0][0], b'a contents 2\n') - #self.assertEqual(hunk.data[1][0], b'a contents\n') + self.assertEqual(patch.old_file_path, 'a') + self.assertEqual(patch.new_file_path, 'a') def test_diff_tree_opts(self): commit_c = self.repo[COMMIT_SHA1_3] @@ -168,25 +169,23 @@ def test_diff_merge(self): self.assertTrue(diff_c is not None) # assertIn / assertNotIn are 2.7 only - self.assertAll(lambda x:('b', 'b', 3, 0) not in x.files, diff_b) - self.assertAny(lambda x:('b', 'b', 3, 0) in x.files, diff_c) + self.assertFalse('b' in [patch.new_file_path for patch in diff_b]) + self.assertTrue('b' in [patch.new_file_path for patch in diff_c]) diff_b.merge(diff_c) # assertIn is 2.7 only - self.assertAny(lambda x:('b', 'b', 3, 0) in x.files, diff_b) + self.assertTrue('b' in [patch.new_file_path for patch in diff_b]) - hunk = diff_b[1].hunks[0] + patch = diff_b[0] + hunk = patch.hunks[0] self.assertEqual(hunk.old_start, 1) self.assertEqual(hunk.old_lines, 1) self.assertEqual(hunk.new_start, 1) self.assertEqual(hunk.new_lines, 1) - self.assertEqual(hunk.old_file, 'b') - self.assertEqual(hunk.new_file, 'b') - - #self.assertEqual(hunk.data[0][0], b'b contents\n') - #self.assertEqual(hunk.data[1][0], b'b contents 2\n') + self.assertEqual(patch.old_file_path, 'a') + self.assertEqual(patch.new_file_path, 'a') def test_diff_patch(self): commit_a = self.repo[COMMIT_SHA1_1] @@ -195,23 +194,22 @@ def test_diff_patch(self): diff = commit_a.tree.diff(commit_b.tree) self.assertEqual(diff.patch, PATCH) - def test_diff_header(self): - commit_a = self.repo[COMMIT_SHA1_1] - commit_b = self.repo[COMMIT_SHA1_2] - diff = commit_a.tree.diff(commit_b.tree) - - self.assertEqual(diff[0].hunks[0].header, "@@ -1 +1 @@\n") - def test_diff_oids(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] - diff = commit_a.tree.diff(commit_b.tree) - hunk = diff[0].hunks[0] - self.assertEqual(hunk.old_oid, + patch = commit_a.tree.diff(commit_b.tree)[0] + self.assertEqual(patch.old_oid, '7f129fd57e31e935c6d60a0c794efe4e6927664b') - self.assertEqual(hunk.new_oid, + self.assertEqual(patch.new_oid, 'af431f20fc541ed6d5afede3e2dc7160f6f01f16') + def test_hunk_content(self): + commit_a = self.repo[COMMIT_SHA1_1] + commit_b = self.repo[COMMIT_SHA1_2] + patch = commit_a.tree.diff(commit_b.tree)[0] + hunk = patch.hunks[0] + self.assertEqual(HUNK_EXPECTED, ''.join(hunk.lines)) + def test_find_similar(self): commit_a = self.repo[COMMIT_SHA1_6] commit_b = self.repo[COMMIT_SHA1_7] @@ -219,10 +217,9 @@ def test_find_similar(self): #~ Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate #~ --find-copies-harder during rename transformion... diff = commit_a.tree.diff(commit_b.tree, GIT_DIFF_INCLUDE_UNMODIFIED) - entry = ('lorem', 'ipsum', pygit2.GIT_DELTA_RENAMED, 100) - self.assertAll(lambda x: entry not in x.files, diff) + self.assertAll(lambda x: x.status is not pygit2.GIT_DELTA_RENAMED, diff) diff.find_similar() - self.assertAny(lambda x: entry in x.files, diff) + self.assertAny(lambda x: x.status is pygit2.GIT_DELTA_RENAMED, diff) if __name__ == '__main__': unittest.main() From 2328cdef692f6b61368c6a5b9d4aa5875bc325e9 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 10 Mar 2013 12:42:22 +0100 Subject: [PATCH 0423/2237] added note support * Repository.create_note() * Repository.lookup_note() * Repository.notes() - generator --- include/pygit2/note.h | 37 +++++++ include/pygit2/types.h | 15 +++ src/note.c | 236 +++++++++++++++++++++++++++++++++++++++++ src/pygit2.c | 10 ++ src/repository.c | 93 ++++++++++++++++ 5 files changed, 391 insertions(+) create mode 100644 include/pygit2/note.h create mode 100644 src/note.c diff --git a/include/pygit2/note.h b/include/pygit2/note.h new file mode 100644 index 000000000..a4ded369a --- /dev/null +++ b/include/pygit2/note.h @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2013 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. + */ + +#ifndef INCLUDE_pygit2_note_h +#define INCLUDE_pygit2_note_h + +#define PY_SSIZE_T_CLEAN +#include +#include + +PyObject* wrap_note(Repository* repo, git_oid* annotated_id, const char* ref); + +#endif diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 27bccda97..218a8e641 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -67,6 +67,21 @@ typedef struct { typedef struct { PyObject_HEAD + Repository *repo; + git_note *note; + char* annotated_id; +} Note; + +typedef struct { + PyObject_HEAD + Repository *repo; + git_note_iterator* iter; + char* ref; +} NoteIter; + +typedef struct { + PyObject_HEAD + Diff* diff; size_t i; size_t n; diff --git a/src/note.c b/src/note.c new file mode 100644 index 000000000..110ca8b72 --- /dev/null +++ b/src/note.c @@ -0,0 +1,236 @@ +/* + * Copyright 2010-2013 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 +#include +#include + +extern PyTypeObject SignatureType; + +PyDoc_STRVAR(Note_remove__doc__, + "Removes a note for an annotated object"); + +PyObject* +Note_remove(Note *self, PyObject* args) +{ + char *ref = "refs/notes/commits"; + int err = GIT_ERROR; + git_oid annotated_id; + Signature *py_author, *py_committer; + + if (!PyArg_ParseTuple(args, "O!O!|s", + &SignatureType, &py_author, + &SignatureType, &py_committer, + &ref)) + return NULL; + + err = git_oid_fromstr(&annotated_id, self->annotated_id); + if (err < 0) + return Error_set(err); + + err = git_note_remove(self->repo->repo, ref, py_author->signature, + py_committer->signature, &annotated_id); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + + +PyDoc_STRVAR(Note_oid__doc__, + "Gets the id of the blob containing the note message\n"); + +PyObject * +Note_oid__get__(Note *self) +{ + return git_oid_to_py_str(git_note_oid(self->note)); +} + + +PyDoc_STRVAR(Note_message__doc__, + "Gets message of the note\n"); + +PyObject * +Note_message__get__(Note *self) +{ + return PyUnicode_FromString(git_note_message(self->note)); +} + + +static void +Note_dealloc(Note *self) +{ + Py_CLEAR(self->repo); + free(self->annotated_id); + git_note_free(self->note); + PyObject_Del(self); +} + + +PyMethodDef Note_methods[] = { + METHOD(Note, remove, METH_VARARGS), + {NULL} +}; + +PyMemberDef Note_members[] = { + MEMBER(Note, annotated_id, T_STRING, "id of the annotated object."), + {NULL} +}; + +PyGetSetDef Note_getseters[] = { + GETTER(Note, message), + GETTER(Note, oid), + {NULL} +}; + +PyDoc_STRVAR(Note__doc__, "Note object."); + +PyTypeObject NoteType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.Note", /* tp_name */ + sizeof(Note), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Note_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Note__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Note_methods, /* tp_methods */ + Note_members, /* tp_members */ + Note_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 */ +}; + + +PyObject * +NoteIter_iternext(NoteIter *self) +{ + int err; + git_oid note_id, annotated_id; + + err = git_note_next(¬e_id, &annotated_id, self->iter); + if (err < 0) + return Error_set(err); + + return (PyObject*) wrap_note(self->repo, &annotated_id, self->ref); +} + +void +NoteIter_dealloc(NoteIter *self) +{ + git_note_iterator_free(self->iter); + PyObject_Del(self); +} + + +PyDoc_STRVAR(NoteIter__doc__, "Note iterator object."); + +PyTypeObject NoteIterType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.NoteIter", /* tp_name */ + sizeof(NoteIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)NoteIter_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + NoteIter__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc) NoteIter_iternext, /* tp_iternext */ +}; + + +PyObject* +wrap_note(Repository* repo, git_oid* annotated_id, const char* ref) +{ + Note* py_note = NULL; + int err = GIT_ERROR; + + py_note = (Note*) PyType_GenericNew(&NoteType, NULL, NULL); + if (py_note == NULL) { + PyErr_NoMemory(); + return NULL; + } + + err = git_note_read(&py_note->note, repo->repo, ref, annotated_id); + if (err < 0) + return Error_set(err); + + py_note->repo = repo; + Py_INCREF(repo); + py_note->annotated_id = git_oid_allocfmt(annotated_id); + + return (PyObject*) py_note; +} + + diff --git a/src/pygit2.c b/src/pygit2.c index 09573ef2b..e730bf7a0 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -60,6 +60,8 @@ extern PyTypeObject RefLogIterType; extern PyTypeObject RefLogEntryType; extern PyTypeObject SignatureType; extern PyTypeObject RemoteType; +extern PyTypeObject NoteType; +extern PyTypeObject NoteIterType; @@ -236,6 +238,11 @@ moduleinit(PyObject* m) if (PyType_Ready(&RemoteType) < 0) return NULL; + if (PyType_Ready(&NoteType) < 0) + return NULL; + if (PyType_Ready(&NoteIterType) < 0) + return NULL; + Py_INCREF(GitError); PyModule_AddObject(m, "GitError", GitError); @@ -281,6 +288,9 @@ moduleinit(PyObject* m) Py_INCREF(&RemoteType); PyModule_AddObject(m, "Remote", (PyObject *)&RemoteType); + Py_INCREF(&NoteType); + PyModule_AddObject(m, "Note", (PyObject *)&NoteType); + 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/repository.c b/src/repository.c index 87ff39562..e487fc9bd 100644 --- a/src/repository.c +++ b/src/repository.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,8 @@ extern PyTypeObject ConfigType; extern PyTypeObject DiffType; extern PyTypeObject RemoteType; extern PyTypeObject ReferenceType; +extern PyTypeObject NoteType; +extern PyTypeObject NoteIterType; git_otype int_to_loose_object_type(int type_id) @@ -1132,6 +1135,93 @@ Repository_checkout(Repository *self, PyObject *args, PyObject *kw) } +PyDoc_STRVAR(Repository_notes__doc__, ""); + +PyObject * +Repository_notes(Repository *self, PyObject* args) +{ + NoteIter *iter = NULL; + char *ref = "refs/notes/commits"; + int err = GIT_ERROR; + + if (!PyArg_ParseTuple(args, "|s", &ref)) + return NULL; + + iter = PyObject_New(NoteIter, &NoteIterType); + if (iter != NULL) { + iter->repo = self; + iter->ref = ref; + + err = git_note_iterator_new(&iter->iter, self->repo, iter->ref); + if (err == GIT_OK) { + Py_INCREF(self); + return (PyObject*)iter; + } + } + + return Error_set(err); + +} + + +PyDoc_STRVAR(Repository_create_note__doc__, + "create_note(message, author, committer, annotated_id [,ref, force]) -> ID\n" + "\n" + "Create a new note for an object, return its SHA-ID." + "If no ref is given 'refs/notes/commits' will be used."); + +PyObject * +Repository_create_note(Repository *self, PyObject* args) +{ + git_oid note_id, annotated_id; + char *annotated = NULL, *message = NULL, *ref = "refs/notes/commits"; + int err = GIT_ERROR; + unsigned int force = 0; + Signature *py_author, *py_committer; + + if (!PyArg_ParseTuple(args, "sO!O!s|si", + &message, + &SignatureType, &py_author, + &SignatureType, &py_committer, + &annotated, &ref, &force)) + return NULL; + + err = git_oid_fromstr(&annotated_id, annotated); + if (err < 0) + return Error_set(err); + + err = git_note_create(¬e_id, self->repo, py_author->signature, + py_committer->signature, ref, + &annotated_id, message, force); + if (err < 0) + return Error_set(err); + + return git_oid_to_python(note_id.id); +} + + +PyDoc_STRVAR(Repository_lookup_note__doc__, + "lookup_note(annotated_id [, ref]) -> Note\n" + "\n" + "Lookup a note for an annotated object in a repository."); + +PyObject * +Repository_lookup_note(Repository *self, PyObject* args) +{ + git_oid annotated_id; + char* annotated = NULL, *ref = "refs/notes/commits"; + int err; + + if (!PyArg_ParseTuple(args, "s|s", &annotated, &ref)) + return NULL; + + err = git_oid_fromstr(&annotated_id, annotated); + if (err < 0) + return Error_set(err); + + return (PyObject*) wrap_note(self, &annotated_id, ref); +} + PyMethodDef Repository_methods[] = { METHOD(Repository, create_blob, METH_VARARGS), METHOD(Repository, create_blob_fromfile, METH_VARARGS), @@ -1150,6 +1240,9 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, status_file, METH_O), METHOD(Repository, create_remote, METH_VARARGS), METHOD(Repository, checkout, METH_VARARGS|METH_KEYWORDS), + METHOD(Repository, notes, METH_VARARGS), + METHOD(Repository, create_note, METH_VARARGS), + METHOD(Repository, lookup_note, METH_VARARGS), {NULL} }; From a89d55a0f3c0bf796105232fb32a2f598c648d06 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 10 Mar 2013 12:44:22 +0100 Subject: [PATCH 0424/2237] added notes in testreop --- .../62/cc88a53cfb046fcf603b3aaeb73b8e18215442 | Bin 0 -> 84 bytes .../63/59f8019b0954201a807b766547526173f3cc67 | Bin 0 -> 150 bytes .../a0/75f5a7394b0838a9f54dfc511e1a3fbbb3b973 | Bin 0 -> 184 bytes .../ab/533997b80705767be3dae8cbb06a0740809f79 | Bin 0 -> 34 bytes .../c2/c2f6b06efdb6c1e4b1337811f0629fc0cadbd1 | Bin 0 -> 149 bytes .../d8/79714d880671ed84f8aaed8b27fca23ba01f27 | Bin 0 -> 37 bytes test/data/testrepo.git/refs/notes/commits | 1 + 7 files changed, 1 insertion(+) create mode 100644 test/data/testrepo.git/objects/62/cc88a53cfb046fcf603b3aaeb73b8e18215442 create mode 100644 test/data/testrepo.git/objects/63/59f8019b0954201a807b766547526173f3cc67 create mode 100644 test/data/testrepo.git/objects/a0/75f5a7394b0838a9f54dfc511e1a3fbbb3b973 create mode 100644 test/data/testrepo.git/objects/ab/533997b80705767be3dae8cbb06a0740809f79 create mode 100644 test/data/testrepo.git/objects/c2/c2f6b06efdb6c1e4b1337811f0629fc0cadbd1 create mode 100644 test/data/testrepo.git/objects/d8/79714d880671ed84f8aaed8b27fca23ba01f27 create mode 100644 test/data/testrepo.git/refs/notes/commits diff --git a/test/data/testrepo.git/objects/62/cc88a53cfb046fcf603b3aaeb73b8e18215442 b/test/data/testrepo.git/objects/62/cc88a53cfb046fcf603b3aaeb73b8e18215442 new file mode 100644 index 0000000000000000000000000000000000000000..8c78013ea4e51b174707a25dd99625777dc8be92 GIT binary patch literal 84 zcmV-a0IUCa0V^p=O;s?nU@$Z=Ff%bxFt;$VFf~n1Ofv%VEE5ea4NX#vlGDtLQVflf qERD@gQq0W~Q_U?HRtH;7-@(pWR{i+ai_;sj*c}??R{{VIo)_=gEFkLu literal 0 HcmV?d00001 diff --git a/test/data/testrepo.git/objects/63/59f8019b0954201a807b766547526173f3cc67 b/test/data/testrepo.git/objects/63/59f8019b0954201a807b766547526173f3cc67 new file mode 100644 index 0000000000000000000000000000000000000000..970393606a4bb3f501bdeba66aec634284ee881e GIT binary patch literal 150 zcmV;H0BQet0i};S4#F@HM5%p>?NT5j|KkE7L;+no4q&g>V2Q+1VvursAQC5_n|Z}N z4VGcZ6{HZGIuU`Dh|YPVFa@q{!eqG$%6kf(3XX)6!e}j-f7W}MpwC#~RYthbG8fo? zd%Nc2`MNvN^vGiy=>S4mX-#K33tKKYXSkXx{j84ttGRN4w;+_BT+@Ru?k8mxsdrzi_RU``B+D!m@)dn zBBc>S4$OWC-ZBMOod^e$e)?xy;4n#p-!{Wuua^cdx4ygQw~yoYU6-$E-W7cXa%|ZL m9}zZ4h(_+FU7y)7ht~B1sT3`cudqJ-f1e+0ZT7He)a}FfcPQQ82eKu`o4FPE0cb@+=b#Ee%aljFQvLj8Y7Z zk}QqQO;XIw5>w4B7*+>cPT#@KT2}q|){D~{ve+FO=2t?^NHa|}O-wXNH8x93GDu4` zGfGW0OioTVOHHy!Gq+5#Ff+3-GfuKFXSh*W=-a_o__pQ8s<++he->FUkXHu)KsGP0 D_^Ue8 literal 0 HcmV?d00001 diff --git a/test/data/testrepo.git/objects/d8/79714d880671ed84f8aaed8b27fca23ba01f27 b/test/data/testrepo.git/objects/d8/79714d880671ed84f8aaed8b27fca23ba01f27 new file mode 100644 index 0000000000000000000000000000000000000000..6b4186cf6448018dbcec6752f62e690a36e974a7 GIT binary patch literal 37 tcmb Date: Sun, 10 Mar 2013 12:44:48 +0100 Subject: [PATCH 0425/2237] added tests for notes --- test/__init__.py | 2 +- test/test_note.py | 78 +++++++++++++++++++++++++++++++++++++++++++++ test/test_remote.py | 4 +-- 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 test/test_note.py diff --git a/test/__init__.py b/test/__init__.py index 44bc24ba5..e81179b99 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -37,7 +37,7 @@ names = ['blob', 'commit', 'config', 'diff', 'index', 'refs', 'remote', 'repository', 'revwalk', 'signature', 'status', 'tag', 'tree', - 'treebuilder'] + 'treebuilder', 'note'] def test_suite(): # Sometimes importing pygit2 fails, we try this first to get an diff --git a/test/test_note.py b/test/test_note.py new file mode 100644 index 000000000..b0439e651 --- /dev/null +++ b/test/test_note.py @@ -0,0 +1,78 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2010-2013 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. + +"""Tests for note objects.""" + +from __future__ import absolute_import +from __future__ import unicode_literals +import unittest + +from pygit2 import Signature +from . import utils + +NOTE = ('6c8980ba963cad8b25a9bcaf68d4023ee57370d8', 'note message') + +NOTES = [ + ('ab533997b80705767be3dae8cbb06a0740809f79', 'First Note - HEAD\n', + '784855caf26449a1914d2cf62d12b9374d76ae78'), + ('d879714d880671ed84f8aaed8b27fca23ba01f27', 'Second Note - HEAD~1\n', + 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87') +] + +class NotesTest(utils.BareRepoTestCase): + + def test_create_note(self): + annotated_id = self.repo.revparse_single('HEAD~3').hex + author = committer = Signature('Foo bar', 'foo@bar.com', 12346, 0) + note_id = self.repo.create_note(NOTE[1], author, committer, annotated_id) + self.assertEqual(NOTE[0], utils.oid_to_hex(note_id)) + + # check the note blob + self.assertEqual(NOTE[1].encode(), self.repo[note_id].data) + + def test_lookup_note(self): + annotated_id = self.repo.head.hex + note = self.repo.lookup_note(annotated_id) + self.assertEqual(NOTES[0][0], note.oid) + self.assertEqual(NOTES[0][1], note.message) + + def test_remove_note(self): + note = self.repo.lookup_note(self.repo.head.hex) + author = committer = Signature('Foo bar', 'foo@bar.com', 12346, 0) + note.remove(author, committer) + self.assertRaises(KeyError, lambda: self.repo.lookup_note(self.repo.head.hex)) + + def test_iterate_notes(self): + for i, note in enumerate(self.repo.notes()): + entry = (note.oid, note.message, note.annotated_id) + self.assertEqual(NOTES[i],entry) + + def test_iterate_non_existing_ref(self): + self.assertRaises(KeyError, lambda: self.repo.notes("refs/notes/bad_ref")) + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_remote.py b/test/test_remote.py index 9b2c8c8b0..be00b711e 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -35,8 +35,8 @@ REMOTE_URL = 'git://github.com/libgit2/pygit2.git' REMOTE_FETCHSPEC_SRC = 'refs/heads/*' REMOTE_FETCHSPEC_DST = 'refs/remotes/origin/*' -REMOTE_REPO_OBJECTS = 24 -REMOTE_REPO_BYTES = 2253 +REMOTE_REPO_OBJECTS = 30 +REMOTE_REPO_BYTES = 2758 class RepositoryTest(utils.RepoTestCase): def test_remote_create(self): From cd07b9edac76272ea3b58088c9513912696ad98c Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 12 Mar 2013 15:29:22 +0100 Subject: [PATCH 0426/2237] remove needless tp_getattro entries --- src/index.c | 2 +- src/reference.c | 2 +- src/tree.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.c b/src/index.c index 79554fc83..81b20cb96 100644 --- a/src/index.c +++ b/src/index.c @@ -542,7 +542,7 @@ PyTypeObject IndexIterType = { 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ diff --git a/src/reference.c b/src/reference.c index b508567d0..6d9a77357 100644 --- a/src/reference.c +++ b/src/reference.c @@ -446,7 +446,7 @@ PyTypeObject RefLogEntryType = { 0, /* tp_iternext */ 0, /* tp_methods */ RefLogEntry_members, /* tp_members */ - 0, /* tp_getset */ + RefLogEntry_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ diff --git a/src/tree.c b/src/tree.c index 6121c3fd8..c092e5e3c 100644 --- a/src/tree.c +++ b/src/tree.c @@ -462,7 +462,7 @@ PyTypeObject TreeIterType = { 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ From f97f58ec56339c8e41a51da8f01dc5000061b5f2 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 12 Mar 2013 15:31:04 +0100 Subject: [PATCH 0427/2237] use PyObject_New() for object instanziation --- src/diff.c | 4 ++-- src/note.c | 3 ++- src/repository.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/diff.c b/src/diff.c index b8289d50a..e6d53b02e 100644 --- a/src/diff.c +++ b/src/diff.c @@ -58,7 +58,7 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) if (err < 0) return Error_set(err); - py_patch = (Patch*) PyType_GenericNew(&PatchType, NULL, NULL); + py_patch = PyObject_New(Patch, &PatchType); if (py_patch != NULL) { py_patch->old_file_path = delta->old_file.path; py_patch->new_file_path = delta->new_file.path; @@ -77,7 +77,7 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) if (err < 0) goto cleanup; - py_hunk = (Hunk*)PyType_GenericNew(&HunkType, NULL, NULL); + py_hunk = PyObject_New(Hunk, &HunkType); if (py_hunk != NULL) { py_hunk->old_start = range->old_start; py_hunk->old_lines = range->old_lines; diff --git a/src/note.c b/src/note.c index 110ca8b72..2dffbae04 100644 --- a/src/note.c +++ b/src/note.c @@ -172,6 +172,7 @@ NoteIter_iternext(NoteIter *self) void NoteIter_dealloc(NoteIter *self) { + Py_CLEAR(self->repo); git_note_iterator_free(self->iter); PyObject_Del(self); } @@ -216,7 +217,7 @@ wrap_note(Repository* repo, git_oid* annotated_id, const char* ref) Note* py_note = NULL; int err = GIT_ERROR; - py_note = (Note*) PyType_GenericNew(&NoteType, NULL, NULL); + py_note = PyObject_New(Note, &NoteType); if (py_note == NULL) { PyErr_NoMemory(); return NULL; diff --git a/src/repository.c b/src/repository.c index e487fc9bd..5c83cc7f9 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1053,7 +1053,7 @@ Repository_create_remote(Repository *self, PyObject *args) if (err < 0) return Error_set(err); - py_remote = (Remote*) PyType_GenericNew(&RemoteType, NULL, NULL); + py_remote = PyObject_New(Remote, &RemoteType); py_remote->repo = self; py_remote->remote = remote; From 78cddb6c91215486d8de07cc712b45e7afb33b6c Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 12 Mar 2013 15:31:26 +0100 Subject: [PATCH 0428/2237] Use basic types for objects and grouped types into groups --- include/pygit2/types.h | 80 ++++++++++++++++++++++++++++-------------- src/reference.c | 70 ++++++++++++++++++------------------ 2 files changed, 88 insertions(+), 62 deletions(-) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 218a8e641..1b6f53181 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -32,39 +32,47 @@ #include #include -/* Python objects */ +/* + * Python objects + * + **/ + +/* git_repository */ typedef struct { PyObject_HEAD git_repository *repo; - PyObject *index; /* It will be None for a bare repository */ - PyObject *config; + PyObject *index; /* It will be None for a bare repository */ + PyObject *config; /* It will be None for a bare repository */ } Repository; -/* The structs for some of the object subtypes are identical except for - * the type of their object pointers. */ -#define OBJECT_STRUCT(_name, _ptr_type, _ptr_name) \ + +#define SIMPLE_TYPE(_name, _ptr_type, _ptr_name) \ typedef struct {\ PyObject_HEAD\ Repository *repo;\ _ptr_type *_ptr_name;\ } _name; -OBJECT_STRUCT(Object, git_object, obj) -OBJECT_STRUCT(Commit, git_commit, commit) -OBJECT_STRUCT(Tree, git_tree, tree) -OBJECT_STRUCT(TreeBuilder, git_treebuilder, bld) -OBJECT_STRUCT(Blob, git_blob, blob) -OBJECT_STRUCT(Tag, git_tag, tag) -OBJECT_STRUCT(Index, git_index, index) -OBJECT_STRUCT(Walker, git_revwalk, walk) -OBJECT_STRUCT(Remote, git_remote, remote) -OBJECT_STRUCT(Diff, git_diff_list, list) +/* git object types + * + * The structs for some of the object subtypes are identical except for + * the type of their object pointers. */ +SIMPLE_TYPE(Object, git_object, obj) +SIMPLE_TYPE(Commit, git_commit, commit) +SIMPLE_TYPE(Tree, git_tree, tree) +SIMPLE_TYPE(Blob, git_blob, blob) +SIMPLE_TYPE(Tag, git_tag, tag) + + +/* git_config */ typedef struct { PyObject_HEAD git_config* config; } Config; + +/* git_note */ typedef struct { PyObject_HEAD Repository *repo; @@ -79,9 +87,12 @@ typedef struct { char* ref; } NoteIter; + +/* git _diff */ +SIMPLE_TYPE(Diff, git_diff_list, list) + typedef struct { PyObject_HEAD - Diff* diff; size_t i; size_t n; @@ -98,12 +109,6 @@ typedef struct { unsigned similarity; } Patch; -typedef struct { - PyObject_HEAD - PyObject *owner; /* Tree or TreeBuilder */ - const git_tree_entry *entry; -} TreeEntry; - typedef struct { PyObject_HEAD PyObject* lines; @@ -113,12 +118,26 @@ typedef struct { int new_lines; } Hunk; + +/* git_tree_walk , git_treebuilder*/ +SIMPLE_TYPE(TreeBuilder, git_treebuilder, bld) + +typedef struct { + PyObject_HEAD + PyObject *owner; /* Tree or TreeBuilder */ + const git_tree_entry *entry; +} TreeEntry; + typedef struct { PyObject_HEAD Tree *owner; int i; } TreeIter; + +/* git_index */ +SIMPLE_TYPE(Index, git_index, index) + typedef struct { PyObject_HEAD const git_index_entry *entry; @@ -130,6 +149,10 @@ typedef struct { int i; } IndexIter; + +/* git_reference, git_reflog */ +SIMPLE_TYPE(Walker, git_revwalk, walk) + typedef struct { PyObject_HEAD git_reference *reference; @@ -137,9 +160,9 @@ typedef struct { typedef struct { PyObject_HEAD - PyObject *oid_old; - PyObject *oid_new; - PyObject *committer; + git_signature *signature; + char *oid_old; + char *oid_new; char *message; } RefLogEntry; @@ -151,6 +174,7 @@ typedef struct { } RefLogIter; +/* git_signature */ typedef struct { PyObject_HEAD Object *obj; @@ -159,6 +183,10 @@ typedef struct { } Signature; +/* git_remote */ +SIMPLE_TYPE(Remote, git_remote, remote) + + PyObject* lookup_object_prefix(Repository *repo, const git_oid *oid, size_t len, git_otype type); diff --git a/src/reference.c b/src/reference.c index 6d9a77357..41b2d2792 100644 --- a/src/reference.c +++ b/src/reference.c @@ -47,40 +47,24 @@ void RefLogIter_dealloc(RefLogIter *self) PyObject_Del(self); } -PyObject* RefLogIter_iternext(PyObject *self) +PyObject* RefLogIter_iternext(RefLogIter *self) { - RefLogIter *p = (RefLogIter *) self; const git_reflog_entry *entry; - char oid_old[40], oid_new[40]; + RefLogEntry *py_entry; - if (p->i < p->size) { - RefLogEntry *py_entry; - git_signature *signature; - - entry = git_reflog_entry_byindex(p->reflog, p->i); - py_entry = (RefLogEntry*) PyType_GenericNew(&RefLogEntryType, NULL, - NULL); - - git_oid_fmt(oid_old, git_reflog_entry_id_old(entry)); - git_oid_fmt(oid_new, git_reflog_entry_id_new(entry)); - - py_entry->oid_new = PyUnicode_FromStringAndSize(oid_new, 40); - py_entry->oid_old = PyUnicode_FromStringAndSize(oid_old, 40); + if (self->i < self->size) { + entry = git_reflog_entry_byindex(self->reflog, self->i); + py_entry = PyObject_New(RefLogEntry, &RefLogEntryType); + py_entry->oid_old = git_oid_allocfmt(git_reflog_entry_id_old(entry)); + py_entry->oid_new = git_oid_allocfmt(git_reflog_entry_id_new(entry)); py_entry->message = strdup(git_reflog_entry_message(entry)); + py_entry->signature = git_signature_dup( + git_reflog_entry_committer(entry)); - signature = git_signature_dup( - git_reflog_entry_committer(entry) - ); - - if (signature != NULL) - py_entry->committer = build_signature( - (Object*)py_entry, signature, "utf-8"); - - ++(p->i); + ++(self->i); return (PyObject*) py_entry; - } PyErr_SetNone(PyExc_StopIteration); @@ -383,13 +367,23 @@ Reference_log(Reference *self) return (PyObject*)iter; } + +PyDoc_STRVAR(RefLogEntry_committer__doc__, "Committer."); + +PyObject * +RefLogEntry_committer__get__(RefLogEntry *self) +{ + return build_signature((Object*) self, self->signature, "utf-8"); +} + + static int RefLogEntry_init(RefLogEntry *self, PyObject *args, PyObject *kwds) { - self->oid_old = Py_None; - self->oid_new = Py_None; - self->message = ""; - self->committer = Py_None; + self->oid_old = NULL; + self->oid_new = NULL; + self->message = NULL; + self->signature = NULL; return 0; } @@ -398,18 +392,22 @@ RefLogEntry_init(RefLogEntry *self, PyObject *args, PyObject *kwds) static void RefLogEntry_dealloc(RefLogEntry *self) { - Py_CLEAR(self->oid_old); - Py_CLEAR(self->oid_new); - Py_CLEAR(self->committer); + free(self->oid_old); + free(self->oid_new); free(self->message); + git_signature_free(self->signature); PyObject_Del(self); } PyMemberDef RefLogEntry_members[] = { - MEMBER(RefLogEntry, oid_new, T_OBJECT, "New oid."), - MEMBER(RefLogEntry, oid_old, T_OBJECT, "Old oid."), + MEMBER(RefLogEntry, oid_new, T_STRING, "New oid."), + MEMBER(RefLogEntry, oid_old, T_STRING, "Old oid."), MEMBER(RefLogEntry, message, T_STRING, "Message."), - MEMBER(RefLogEntry, committer, T_OBJECT, "Committer."), + {NULL} +}; + +PyGetSetDef RefLogEntry_getseters[] = { + GETTER(RefLogEntry, committer), {NULL} }; From b60f24c12784251ba26fae15511e5f82fff1fe4e Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 12 Mar 2013 16:21:17 +0100 Subject: [PATCH 0429/2237] use utils helper macros/functions through the whole lib * use to_unicode()/to_unicode_n() for creation of unicode strings * main support switch to python3 instead of python2 (provide defines for python2) --- include/pygit2/oid.h | 2 +- include/pygit2/utils.h | 39 ++++++++++++++++++++------------------- src/blob.c | 2 +- src/commit.c | 2 +- src/config.c | 10 +++++----- src/diff.c | 10 +++++----- src/index.c | 8 ++++---- src/note.c | 2 +- src/object.c | 4 ++-- src/oid.c | 8 ++++---- src/reference.c | 2 +- src/remote.c | 4 ++-- src/repository.c | 8 ++++---- src/signature.c | 4 ++-- src/tag.c | 2 +- src/tree.c | 6 +++--- src/utils.c | 6 +++--- src/walker.c | 2 +- 18 files changed, 61 insertions(+), 60 deletions(-) diff --git a/include/pygit2/oid.h b/include/pygit2/oid.h index 4f22337fe..7dce30300 100644 --- a/include/pygit2/oid.h +++ b/include/pygit2/oid.h @@ -38,6 +38,6 @@ int py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, PyObject* git_oid_to_py_str(const git_oid *oid); #define git_oid_to_python(id) \ - PyString_FromStringAndSize((const char*)id, GIT_OID_RAWSZ) + PyBytes_FromStringAndSize((const char*)id, GIT_OID_RAWSZ) #endif diff --git a/include/pygit2/utils.h b/include/pygit2/utils.h index d1ecc42b8..a47c18a0c 100644 --- a/include/pygit2/utils.h +++ b/include/pygit2/utils.h @@ -33,25 +33,22 @@ #include #include - -/* Python 3 support */ -#if PY_MAJOR_VERSION >= 3 - #define PyInt_AsLong PyLong_AsLong - #define PyInt_Check PyLong_Check - #define PyInt_FromLong PyLong_FromLong - #define PyString_AS_STRING PyBytes_AS_STRING - #define PyString_AsString PyBytes_AsString - #define PyString_AsStringAndSize PyBytes_AsStringAndSize - #define PyString_Check PyBytes_Check - #define PyString_FromString PyBytes_FromString - #define PyString_FromStringAndSize PyBytes_FromStringAndSize - #define PyString_Size PyBytes_Size -#endif - +/* Python 2 support */ #if PY_MAJOR_VERSION == 2 + #define PyLong_FromSize_t PyInt_FromSize_t + #define PyLong_AsLong PyInt_AsLong + #undef PyLong_Check + #define PyLong_Check PyInt_Check + #define PyLong_FromLong PyInt_FromLong + #define PyBytes_AS_STRING PyString_AS_STRING + #define PyBytes_AsString PyString_AsString + #define PyBytes_AsStringAndSize PyString_AsStringAndSize + #define PyBytes_Check PyString_Check + #define PyBytes_FromString PyString_FromString + #define PyBytes_FromStringAndSize PyString_FromStringAndSize + #define PyBytes_Size PyString_Size #define to_path(x) to_bytes(x) #define to_encoding(x) to_bytes(x) - #define PyLong_FromSize_t PyInt_FromSize_t #else #define to_path(x) to_unicode(x, Py_FileSystemDefaultEncoding, "strict") #define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict") @@ -69,9 +66,12 @@ return -1;\ } + /* Utilities */ +#define to_unicode(x, encoding, errors) to_unicode_n(x, strlen(x), encoding, errors) + Py_LOCAL_INLINE(PyObject*) -to_unicode(const char *value, const char *encoding, const char *errors) +to_unicode_n(const char *value, size_t len, const char *encoding, const char *errors) { if (encoding == NULL) { /* If the encoding is not explicit, it may not be UTF-8, so it @@ -81,13 +81,14 @@ to_unicode(const char *value, const char *encoding, const char *errors) encoding = "utf-8"; errors = "replace"; } - return PyUnicode_Decode(value, strlen(value), encoding, errors); + + return PyUnicode_Decode(value, len, encoding, errors); } Py_LOCAL_INLINE(PyObject*) to_bytes(const char * value) { - return PyString_FromString(value); + return PyBytes_FromString(value); } char * py_str_to_c_str(PyObject *value, const char *encoding); diff --git a/src/blob.c b/src/blob.c index 446101b60..c21ce0707 100644 --- a/src/blob.c +++ b/src/blob.c @@ -37,7 +37,7 @@ PyDoc_STRVAR(Blob_size__doc__, "Size."); PyObject * Blob_size__get__(Blob *self) { - return PyInt_FromLong(git_blob_rawsize(self->blob)); + return PyLong_FromLong(git_blob_rawsize(self->blob)); } diff --git a/src/commit.c b/src/commit.c index 2f6dbc424..7d2e2fc7f 100644 --- a/src/commit.c +++ b/src/commit.c @@ -68,7 +68,7 @@ PyDoc_STRVAR(Commit__message__doc__, "Message (bytes)."); PyObject * Commit__message__get__(Commit *commit) { - return PyString_FromString(git_commit_message(commit->commit)); + return PyBytes_FromString(git_commit_message(commit->commit)); } diff --git a/src/config.c b/src/config.c index 9cf0b553f..b06c557e3 100644 --- a/src/config.c +++ b/src/config.c @@ -191,7 +191,7 @@ Config_getitem(Config *self, PyObject *py_key) else if(git_config_parse_bool(&value_bool, value_str) == 0) py_value = PyBool_FromLong(value_bool); else - py_value = PyUnicode_FromString(value_str); + py_value = to_unicode(value_str, NULL, NULL); cleanup: free(key); @@ -223,9 +223,9 @@ Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) else if (PyBool_Check(py_value)) { err = git_config_set_bool(self->config, key, (int)PyObject_IsTrue(py_value)); - } else if (PyInt_Check(py_value)) { + } else if (PyLong_Check(py_value)) { err = git_config_set_int64(self->config, key, - (int64_t)PyInt_AsLong(py_value)); + (int64_t)PyLong_AsLong(py_value)); } else { value = py_str_to_c_str(py_value, NULL); err = git_config_set_string(self->config, key, value); @@ -300,7 +300,7 @@ Config_foreach(Config *self, PyObject *args) ret = git_config_foreach(self->config, Config_foreach_callback_wrapper, (void *)args); - return PyInt_FromLong((long)ret); + return PyLong_FromLong((long)ret); } @@ -344,7 +344,7 @@ Config_get_multivar_fn_wrapper(const git_config_entry *value, void *data) { PyObject *item = NULL; - if (!(item = PyUnicode_FromString(value->value))) + if (!(item = to_unicode(value->value, NULL, NULL))) return -2; PyList_Append((PyObject *)data, item); diff --git a/src/diff.c b/src/diff.c index e6d53b02e..619d14240 100644 --- a/src/diff.c +++ b/src/diff.c @@ -85,8 +85,8 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) py_hunk->new_lines = range->new_lines; py_hunk->lines = PyList_New(lines_in_hunk + 1); - PyList_SetItem(py_hunk->lines, 0, - PyUnicode_FromStringAndSize(header, header_len)); + PyList_SetItem(py_hunk->lines, 0, + to_unicode_n(header, header_len, NULL, NULL)); for (j=1; j < lines_in_hunk + 1; ++j) { err = git_diff_patch_get_line_in_hunk(NULL, &line, &line_len, NULL, NULL, patch, i, j - 1); @@ -95,7 +95,7 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) goto cleanup; PyList_SetItem(py_hunk->lines, j, - PyUnicode_FromStringAndSize(line, line_len)); + to_unicode_n(line, line_len, NULL, NULL)); } PyList_SetItem((PyObject*) py_patch->hunks, i, @@ -247,7 +247,7 @@ Diff_patch__get__(Diff *self) err = git_diff_get_patch(&patch, &delta, self->list, i); if (err < 0) goto cleanup; - + err = git_diff_patch_to_str(&(strings[i]), patch); if (err < 0) goto cleanup; @@ -263,7 +263,7 @@ Diff_patch__get__(Diff *self) } free(strings); - py_patch = PyUnicode_FromString(buffer); + py_patch = to_unicode(buffer, NULL, NULL); free(buffer); cleanup: diff --git a/src/index.c b/src/index.c index 81b20cb96..7eb3b432c 100644 --- a/src/index.c +++ b/src/index.c @@ -178,7 +178,7 @@ Index__find(Index *self, PyObject *py_path) size_t idx; int err; - path = PyString_AsString(py_path); + path = PyBytes_AsString(py_path); if (!path) return NULL; @@ -236,8 +236,8 @@ Index_get_position(Index *self, PyObject *value) int err; /* Case 1: integer */ - if (PyInt_Check(value)) { - err = (int)PyInt_AsLong(value); + if (PyLong_Check(value)) { + err = (int)PyLong_AsLong(value); if (err == -1 && PyErr_Occurred()) return -1; if (err < 0) { @@ -567,7 +567,7 @@ PyDoc_STRVAR(IndexEntry_mode__doc__, "Mode."); PyObject * IndexEntry_mode__get__(IndexEntry *self) { - return PyInt_FromLong(self->entry->mode); + return PyLong_FromLong(self->entry->mode); } diff --git a/src/note.c b/src/note.c index 2dffbae04..c887036af 100644 --- a/src/note.c +++ b/src/note.c @@ -82,7 +82,7 @@ PyDoc_STRVAR(Note_message__doc__, PyObject * Note_message__get__(Note *self) { - return PyUnicode_FromString(git_note_message(self->note)); + return to_unicode(git_note_message(self->note), NULL, NULL); } diff --git a/src/object.c b/src/object.c index 314bac21d..c3a33fa6e 100644 --- a/src/object.c +++ b/src/object.c @@ -86,7 +86,7 @@ PyDoc_STRVAR(Object_type__doc__, PyObject * Object_type__get__(Object *self) { - return PyInt_FromLong(git_object_type(self->obj)); + return PyLong_FromLong(git_object_type(self->obj)); } @@ -107,7 +107,7 @@ Object_read_raw(Object *self) if (obj == NULL) return NULL; - aux = PyString_FromStringAndSize( + aux = PyBytes_FromStringAndSize( git_odb_object_data(obj), git_odb_object_size(obj)); diff --git a/src/oid.c b/src/oid.c index 102424aa3..773d71ff2 100644 --- a/src/oid.c +++ b/src/oid.c @@ -41,8 +41,8 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) Py_ssize_t len; /* Case 1: raw sha */ - if (PyString_Check(py_str)) { - hex_or_bin = PyString_AsString(py_str); + if (PyBytes_Check(py_str)) { + hex_or_bin = PyBytes_AsString(py_str); if (hex_or_bin == NULL) return -1; git_oid_fromraw(oid, (const unsigned char*)hex_or_bin); @@ -54,7 +54,7 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) py_hex = PyUnicode_AsASCIIString(py_str); if (py_hex == NULL) return -1; - err = PyString_AsStringAndSize(py_hex, &hex_or_bin, &len); + err = PyBytes_AsStringAndSize(py_hex, &hex_or_bin, &len); if (err) { Py_DECREF(py_hex); return -1; @@ -118,6 +118,6 @@ git_oid_to_py_str(const git_oid *oid) char hex[GIT_OID_HEXSZ]; git_oid_fmt(hex, oid); - return PyUnicode_DecodeASCII(hex, GIT_OID_HEXSZ, "strict"); + return to_unicode_n(hex, GIT_OID_HEXSZ, "utf-8", "strict"); } diff --git a/src/reference.c b/src/reference.c index 41b2d2792..774166b45 100644 --- a/src/reference.c +++ b/src/reference.c @@ -342,7 +342,7 @@ Reference_type__get__(Reference *self) CHECK_REFERENCE(self); c_type = git_reference_type(self->reference); - return PyInt_FromLong(c_type); + return PyLong_FromLong(c_type); } diff --git a/src/remote.c b/src/remote.c index 0d55a54cb..a01f7985e 100644 --- a/src/remote.c +++ b/src/remote.c @@ -71,7 +71,7 @@ PyDoc_STRVAR(Remote_name__doc__, "Name of the remote refspec"); PyObject * Remote_name__get__(Remote *self) { - return PyUnicode_FromString(git_remote_name(self->remote)); + return to_unicode(git_remote_name(self->remote), NULL, NULL); } int @@ -100,7 +100,7 @@ PyDoc_STRVAR(Remote_url__doc__, "Url of the remote"); PyObject * Remote_url__get__(Remote *self) { - return PyUnicode_FromString(git_remote_url(self->remote)); + return to_unicode(git_remote_url(self->remote), NULL, NULL); } diff --git a/src/repository.c b/src/repository.c index 5c83cc7f9..b07b8192e 100644 --- a/src/repository.c +++ b/src/repository.c @@ -892,11 +892,11 @@ Repository_create_symbolic_reference(Repository *self, PyObject *args, return NULL; #if PY_MAJOR_VERSION == 2 - c_target = PyString_AsString(py_obj); + c_target = PyBytes_AsString(py_obj); #else // increases ref counter, so we have to release it afterwards PyObject* py_str = PyUnicode_AsASCIIString(py_obj); - c_target = PyString_AsString(py_str); + c_target = PyBytes_AsString(py_str); #endif if (c_target == NULL) return NULL; @@ -928,7 +928,7 @@ read_status_cb(const char *path, unsigned int status_flags, void *payload) PyObject *flags; int err; - flags = PyInt_FromLong((long) status_flags); + flags = PyLong_FromLong((long) status_flags); err = PyDict_SetItemString(payload, path, flags); Py_CLEAR(flags); @@ -972,7 +972,7 @@ Repository_status_file(Repository *self, PyObject *value) free(path); return err_obj; } - return PyInt_FromLong(status); + return PyLong_FromLong(status); } diff --git a/src/signature.c b/src/signature.c index 25dae0ba7..982713e57 100644 --- a/src/signature.c +++ b/src/signature.c @@ -148,7 +148,7 @@ PyDoc_STRVAR(Signature_time__doc__, "Unix time."); PyObject * Signature_time__get__(Signature *self) { - return PyInt_FromLong(self->signature->when.time); + return PyLong_FromLong(self->signature->when.time); } @@ -157,7 +157,7 @@ PyDoc_STRVAR(Signature_offset__doc__, "Offset from UTC in minutes."); PyObject * Signature_offset__get__(Signature *self) { - return PyInt_FromLong(self->signature->when.offset); + return PyLong_FromLong(self->signature->when.offset); } PyGetSetDef Signature_getseters[] = { diff --git a/src/tag.c b/src/tag.c index ee2b329ae..2a9ba82fa 100644 --- a/src/tag.c +++ b/src/tag.c @@ -91,7 +91,7 @@ PyDoc_STRVAR(Tag__message__doc__, "Tag message (bytes)."); PyObject * Tag__message__get__(Tag *self) { - return PyString_FromString(git_tag_message(self->tag)); + return PyBytes_FromString(git_tag_message(self->tag)); } PyGetSetDef Tag_getseters[] = { diff --git a/src/tree.c b/src/tree.c index c092e5e3c..d2e5debf7 100644 --- a/src/tree.c +++ b/src/tree.c @@ -53,7 +53,7 @@ PyDoc_STRVAR(TreeEntry_filemode__doc__, "Filemode."); PyObject * TreeEntry_filemode__get__(TreeEntry *self) { - return PyInt_FromLong(git_tree_entry_filemode(self->entry)); + return PyLong_FromLong(git_tree_entry_filemode(self->entry)); } @@ -201,7 +201,7 @@ Tree_fix_index(Tree *self, PyObject *py_index) size_t len; long slen; - index = PyInt_AsLong(py_index); + index = PyLong_AsLong(py_index); if (PyErr_Occurred()) return -1; @@ -263,7 +263,7 @@ Tree_getitem(Tree *self, PyObject *value) int err; /* Case 1: integer */ - if (PyInt_Check(value)) + if (PyLong_Check(value)) return Tree_getitem_by_index(self, value); /* Case 2: byte or text string */ diff --git a/src/utils.c b/src/utils.c index efd7887c3..488ffa17b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -38,8 +38,8 @@ char * py_str_to_c_str(PyObject *value, const char *encoding) { char *c_str = NULL; /* Case 1: byte string */ - if (PyString_Check(value)) - return strdup(PyString_AsString(value)); + if (PyBytes_Check(value)) + return strdup(PyBytes_AsString(value)); /* Case 2: text string */ if (PyUnicode_Check(value)) { @@ -50,7 +50,7 @@ char * py_str_to_c_str(PyObject *value, const char *encoding) value = PyUnicode_AsEncodedString(value, encoding, "strict"); if (value == NULL) return NULL; - c_str = strdup(PyString_AsString(value)); + c_str = strdup(PyBytes_AsString(value)); Py_DECREF(value); return c_str; } diff --git a/src/walker.c b/src/walker.c index 7e48ab366..9e483f785 100644 --- a/src/walker.c +++ b/src/walker.c @@ -100,7 +100,7 @@ Walker_sort(Walker *self, PyObject *py_sort_mode) { int sort_mode; - sort_mode = (int)PyInt_AsLong(py_sort_mode); + sort_mode = (int)PyLong_AsLong(py_sort_mode); if (sort_mode == -1 && PyErr_Occurred()) return NULL; From fa8ef0d6d4b2266a4a029bd931caeaa4ceacb7d8 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 12 Mar 2013 23:40:24 +0100 Subject: [PATCH 0430/2237] Repository_read().data should be binary data --- src/repository.c | 4 ++++ test/test_repository.py | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/repository.c b/src/repository.c index b07b8192e..df0d9b306 100644 --- a/src/repository.c +++ b/src/repository.c @@ -380,7 +380,11 @@ Repository_read(Repository *self, PyObject *py_hex) return NULL; tuple = Py_BuildValue( + #if PY_MAJOR_VERSION == 2 "(ns#)", + #else + "(ny#)", + #endif git_odb_object_type(obj), git_odb_object_data(obj), git_odb_object_size(obj)); diff --git a/test/test_repository.py b/test/test_repository.py index c3457c483..a92e8a7e2 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -70,14 +70,14 @@ def test_read(self): ab = self.repo.read(A_BIN_SHA) a = self.repo.read(A_HEX_SHA) self.assertEqual(ab, a) - self.assertEqual((GIT_OBJ_BLOB, 'a contents\n'), a) + self.assertEqual((GIT_OBJ_BLOB, b'a contents\n'), a) a2 = self.repo.read('7f129fd57e31e935c6d60a0c794efe4e6927664b') - self.assertEqual((GIT_OBJ_BLOB, 'a contents 2\n'), a2) + self.assertEqual((GIT_OBJ_BLOB, b'a contents 2\n'), a2) a_hex_prefix = A_HEX_SHA[:4] a3 = self.repo.read(a_hex_prefix) - self.assertEqual((GIT_OBJ_BLOB, 'a contents\n'), a3) + self.assertEqual((GIT_OBJ_BLOB, b'a contents\n'), a3) def test_write(self): data = b"hello world" From e99573dfb840f95875db995923526d15fa66f800 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 12 Mar 2013 23:52:42 +0100 Subject: [PATCH 0431/2237] reactivated TreeBuilder test --- test/test_tree.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/test/test_tree.py b/test/test_tree.py index f3bc6e829..ecc07b3b1 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -32,7 +32,6 @@ import operator import unittest -import pygit2 from . import utils @@ -82,21 +81,26 @@ def test_read_subtree(self): sha = '297efb891a47de80be0cfe9c639e4b8c9b450989' self.assertTreeEntryEqual(subtree[0], sha, 'd', 0o0100644) - # TODO This test worked with libgit2 v0.10.0, update to use the - # tree-builder - def xtest_new_tree(self): - b = self.repo.TreeBuilder() - b.insert('1' * 40, 'x', 0o0100644) - b.insert('2' * 40, 'y', 0o0100755) - tree = self.repo[b.write()] + def test_new_tree(self): + b0 = self.repo.create_blob('1') + b1 = self.repo.create_blob('2') + t = self.repo.TreeBuilder() + t.insert('x', b0, 0o0100644) + t.insert('y', b1, 0o0100755) + tree = self.repo[t.write()] self.assertTrue('x' in tree) self.assertTrue('y' in tree) - self.assertRaisesWithArg(KeyError, '1' * 40, tree['x'].to_object) - contents = '100644 x\0%s100755 y\0%s' % ('\x11' * 20, '\x22' * 20) - self.assertEqual((pygit2.GIT_OBJ_TREE, contents), - self.repo.read(tree.hex)) + x = tree['x'] + y = tree['y'] + self.assertEqual(x.filemode, 0o0100644) + self.assertEqual(y.filemode, 0o0100755) + + self.assertEqual(x.to_object().oid, b0) + self.assertEqual(y.to_object().oid, b1) + + def test_modify_tree(self): tree = self.repo[TREE_SHA] From 4c782f451d765f111726b3225964213e356942f2 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 13 Mar 2013 12:48:26 +0100 Subject: [PATCH 0432/2237] fixed non working test for config.set_multivar() --- test/test_config.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/test_config.py b/test/test_config.py index 662107045..9c7e13d3d 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -55,13 +55,13 @@ def test_config(self): def test_global_config(self): try: self.assertNotEqual(None, pygit2.Config.get_global_config()) - except IOError: + except IOError: # there is no user config pass def test_system_config(self): try: self.assertNotEqual(None, pygit2.Config.get_system_config()) - except IOError: + except IOError: # there is no system config pass def test_new(self): @@ -155,14 +155,19 @@ def test_write(self): config.add_file(config_filename, 5) self.assertTrue('this.that' in config) + l = config.get_multivar('this.that', 'foo.*') + self.assertEqual(len(l), 2) + config.set_multivar('this.that', '^.*beer', 'fool') l = config.get_multivar('this.that', 'fool') self.assertEqual(len(l), 1) self.assertEqual(l[0], 'fool') - config.set_multivar('this.that', 'foo.*', '123456') + + config.set_multivar('this.that', 'foo.*', 'foo-123456') l = config.get_multivar('this.that', 'foo.*') + self.assertEqual(len(l), 2) for i in l: - self.assertEqual(i, '123456') + self.assertEqual(i, 'foo-123456') def test_foreach(self): config = self.repo.config From 119d0e03449d25b18732e59c70ca1571f3f38643 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 13 Mar 2013 12:48:58 +0100 Subject: [PATCH 0433/2237] added missing unittest.main() for tests_remote.py --- test/test_remote.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/test_remote.py b/test/test_remote.py index be00b711e..6e5a150af 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -28,6 +28,7 @@ """Tests for Remote objects.""" +import unittest import pygit2 from . import utils @@ -104,3 +105,7 @@ def test_fetch(self): self.assertEqual(stats['received_bytes'], REMOTE_REPO_BYTES) self.assertEqual(stats['indexed_objects'], REMOTE_REPO_OBJECTS) self.assertEqual(stats['received_objects'], REMOTE_REPO_OBJECTS) + + +if __name__ == '__main__': + unittest.main() From 4abd370ea8dc6aee34f0eeddef8a09c598b4b3ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 13 Mar 2013 14:36:40 +0100 Subject: [PATCH 0434/2237] Make bug #196 to show up with Python 2 --- pygit2/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 09aa8588f..056f8b275 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -25,13 +25,15 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -from .version import __version__ +# Import from the future +from __future__ import absolute_import # Low level API import _pygit2 from _pygit2 import * # High level API +from .version import __version__ from repository import Repository import pygit2.utils From 8588c209439c5265cf64b220d1347bb1a7767413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 13 Mar 2013 14:43:44 +0100 Subject: [PATCH 0435/2237] Fix import (#196) using relative imports --- pygit2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 056f8b275..12634f1f7 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -33,8 +33,8 @@ from _pygit2 import * # High level API +from .repository import Repository from .version import __version__ -from repository import Repository import pygit2.utils From ffc6069a34217bf55dc7d8a7fcc29834bc993ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 24 Mar 2013 14:04:53 +0100 Subject: [PATCH 0436/2237] Fix for Python 3 --- src/oid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oid.c b/src/oid.c index 86a0ce5d9..78335d72f 100644 --- a/src/oid.c +++ b/src/oid.c @@ -42,7 +42,7 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) /* Case 1: raw sha */ if (PyBytes_Check(py_str)) { - err = PyString_AsStringAndSize(py_str, &hex_or_bin, &len); + err = PyBytes_AsStringAndSize(py_str, &hex_or_bin, &len); if (err) return -1; memcpy(oid->id, (const unsigned char*)hex_or_bin, len); From 734db55f5806e1260739f13f007d8607614411eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 26 Mar 2013 10:58:02 +0100 Subject: [PATCH 0437/2237] Fix iterators (issue #198) --- src/diff.c | 10 +++++----- src/index.c | 4 ++-- src/note.c | 2 +- src/reference.c | 6 +++--- src/tree.c | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/diff.c b/src/diff.c index 619d14240..a113739e5 100644 --- a/src/diff.c +++ b/src/diff.c @@ -197,11 +197,11 @@ DiffIter_dealloc(DiffIter *self) PyDoc_STRVAR(DiffIter__doc__, "Diff iterator object."); PyTypeObject DiffIterType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.DiffIter", /* tp_name */ - sizeof(DiffIter), /* tp_basicsize */ + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "_pygit2.DiffIter", /* tp_name */ + sizeof(DiffIter), /* tp_basicsize */ 0, /* tp_itemsize */ - (destructor)DiffIter_dealloc, /* tp_dealloc */ + (destructor)DiffIter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -223,7 +223,7 @@ PyTypeObject DiffIterType = { 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc) DiffIter_iternext, /* tp_iternext */ + (iternextfunc) DiffIter_iternext, /* tp_iternext */ }; diff --git a/src/index.c b/src/index.c index 7eb3b432c..30abfbb82 100644 --- a/src/index.c +++ b/src/index.c @@ -460,7 +460,7 @@ PyDoc_STRVAR(Index__doc__, "Index file."); PyTypeObject IndexType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Index", /* tp_name */ + "_pygit2.Index", /* tp_name */ sizeof(Index), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Index_dealloc, /* tp_dealloc */ @@ -526,7 +526,7 @@ IndexIter_iternext(IndexIter *self) PyDoc_STRVAR(IndexIter__doc__, "Index iterator."); PyTypeObject IndexIterType = { - PyVarObject_HEAD_INIT(NULL, 0) + PyVarObject_HEAD_INIT(&PyType_Type, 0) "_pygit2.IndexIter", /* tp_name */ sizeof(IndexIter), /* tp_basicsize */ 0, /* tp_itemsize */ diff --git a/src/note.c b/src/note.c index c887036af..07df5743e 100644 --- a/src/note.c +++ b/src/note.c @@ -181,7 +181,7 @@ NoteIter_dealloc(NoteIter *self) PyDoc_STRVAR(NoteIter__doc__, "Note iterator object."); PyTypeObject NoteIterType = { - PyVarObject_HEAD_INIT(NULL, 0) + PyVarObject_HEAD_INIT(&PyType_Type, 0) "_pygit2.NoteIter", /* tp_name */ sizeof(NoteIter), /* tp_basicsize */ 0, /* tp_itemsize */ diff --git a/src/reference.c b/src/reference.c index 774166b45..1191dcfca 100644 --- a/src/reference.c +++ b/src/reference.c @@ -75,8 +75,8 @@ PyObject* RefLogIter_iternext(RefLogIter *self) PyDoc_STRVAR(RefLogIterType__doc__, "Internal reflog iterator object."); PyTypeObject RefLogIterType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_libgit2.RefLogIter", /* tp_name */ + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "_pygit2.RefLogIter", /* tp_name */ sizeof(RefLogIter), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)RefLogIter_dealloc, /* tp_dealloc */ @@ -101,7 +101,7 @@ PyTypeObject RefLogIterType = { 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc)RefLogIter_iternext /* tp_iternext */ + (iternextfunc)RefLogIter_iternext, /* tp_iternext */ }; void diff --git a/src/tree.c b/src/tree.c index d2e5debf7..e79c53268 100644 --- a/src/tree.c +++ b/src/tree.c @@ -446,7 +446,7 @@ TreeIter_iternext(TreeIter *self) PyDoc_STRVAR(TreeIter__doc__, "Tree iterator."); PyTypeObject TreeIterType = { - PyVarObject_HEAD_INIT(NULL, 0) + PyVarObject_HEAD_INIT(&PyType_Type, 0) "_pygit2.TreeIter", /* tp_name */ sizeof(TreeIter), /* tp_basicsize */ 0, /* tp_itemsize */ From ad56ac6353120681e3ef6752b87e617aeaa3abc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 27 Mar 2013 11:20:53 +0100 Subject: [PATCH 0438/2237] Fix compile with MSVC (issue #199) And use a macro to make type initialization shorter. --- include/pygit2/utils.h | 7 +++ src/diff.c | 2 +- src/index.c | 2 +- src/note.c | 2 +- src/pygit2.c | 98 +++++++++++++++--------------------------- src/reference.c | 2 +- src/tree.c | 2 +- 7 files changed, 46 insertions(+), 69 deletions(-) diff --git a/include/pygit2/utils.h b/include/pygit2/utils.h index a47c18a0c..3045144de 100644 --- a/include/pygit2/utils.h +++ b/include/pygit2/utils.h @@ -135,4 +135,11 @@ char * py_str_to_c_str(PyObject *value, const char *encoding); goto label;\ } +/* Helpers to make type init shorter. */ +#define INIT_TYPE(type, base, new) \ + if (base != NULL) type.tp_base = base; \ + if (new != NULL) type.tp_new = new; \ + if (PyType_Ready(&type) < 0) return NULL; + + #endif diff --git a/src/diff.c b/src/diff.c index a113739e5..f2cb21ef0 100644 --- a/src/diff.c +++ b/src/diff.c @@ -197,7 +197,7 @@ DiffIter_dealloc(DiffIter *self) PyDoc_STRVAR(DiffIter__doc__, "Diff iterator object."); PyTypeObject DiffIterType = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) + PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.DiffIter", /* tp_name */ sizeof(DiffIter), /* tp_basicsize */ 0, /* tp_itemsize */ diff --git a/src/index.c b/src/index.c index 30abfbb82..fed0c73ba 100644 --- a/src/index.c +++ b/src/index.c @@ -526,7 +526,7 @@ IndexIter_iternext(IndexIter *self) PyDoc_STRVAR(IndexIter__doc__, "Index iterator."); PyTypeObject IndexIterType = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) + PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.IndexIter", /* tp_name */ sizeof(IndexIter), /* tp_basicsize */ 0, /* tp_itemsize */ diff --git a/src/note.c b/src/note.c index 07df5743e..c887036af 100644 --- a/src/note.c +++ b/src/note.c @@ -181,7 +181,7 @@ NoteIter_dealloc(NoteIter *self) PyDoc_STRVAR(NoteIter__doc__, "Note iterator object."); PyTypeObject NoteIterType = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) + PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.NoteIter", /* tp_name */ sizeof(NoteIter), /* tp_basicsize */ 0, /* tp_itemsize */ diff --git a/src/pygit2.c b/src/pygit2.c index e730bf7a0..bbe3e4f05 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -178,70 +178,40 @@ moduleinit(PyObject* m) GitError = PyErr_NewException("_pygit2.GitError", NULL, NULL); - RepositoryType.tp_new = PyType_GenericNew; - if (PyType_Ready(&RepositoryType) < 0) - return NULL; - - /* Do not set 'tp_new' for Git objects. To create Git objects use the - * Repository.create_XXX methods */ - if (PyType_Ready(&ObjectType) < 0) - return NULL; - CommitType.tp_base = &ObjectType; - if (PyType_Ready(&CommitType) < 0) - return NULL; - TreeType.tp_base = &ObjectType; - if (PyType_Ready(&TreeType) < 0) - return NULL; - BlobType.tp_base = &ObjectType; - if (PyType_Ready(&BlobType) < 0) - return NULL; - TagType.tp_base = &ObjectType; - if (PyType_Ready(&TagType) < 0) - return NULL; - - if (PyType_Ready(&DiffType) < 0) - return NULL; - if (PyType_Ready(&DiffIterType) < 0) - return NULL; - if (PyType_Ready(&PatchType) < 0) - return NULL; - if (PyType_Ready(&HunkType) < 0) - return NULL; - - TreeEntryType.tp_new = PyType_GenericNew; - if (PyType_Ready(&TreeEntryType) < 0) - return NULL; - IndexType.tp_new = PyType_GenericNew; - if (PyType_Ready(&IndexType) < 0) - return NULL; - IndexEntryType.tp_new = PyType_GenericNew; - if (PyType_Ready(&IndexEntryType) < 0) - return NULL; - TreeBuilderType.tp_new = PyType_GenericNew; - if (PyType_Ready(&TreeBuilderType) < 0) - return NULL; - ConfigType.tp_new = PyType_GenericNew; - if (PyType_Ready(&ConfigType) < 0) - return NULL; - WalkerType.tp_new = PyType_GenericNew; - if (PyType_Ready(&WalkerType) < 0) - return NULL; - ReferenceType.tp_new = PyType_GenericNew; - if (PyType_Ready(&ReferenceType) < 0) - return NULL; - if (PyType_Ready(&RefLogEntryType) < 0) - return NULL; - SignatureType.tp_new = PyType_GenericNew; - if (PyType_Ready(&SignatureType) < 0) - return NULL; - - if (PyType_Ready(&RemoteType) < 0) - return NULL; - - if (PyType_Ready(&NoteType) < 0) - return NULL; - if (PyType_Ready(&NoteIterType) < 0) - return NULL; + /* Repository */ + INIT_TYPE(RepositoryType, NULL, PyType_GenericNew) + /* Objects (make them with the Repository.create_XXX methods). */ + INIT_TYPE(ObjectType, NULL, NULL) + INIT_TYPE(CommitType, &ObjectType, NULL) + INIT_TYPE(SignatureType, NULL, PyType_GenericNew) + INIT_TYPE(TreeType, &ObjectType, NULL) + INIT_TYPE(TreeEntryType, NULL, PyType_GenericNew) + INIT_TYPE(TreeIterType, NULL, NULL) + INIT_TYPE(TreeBuilderType, NULL, PyType_GenericNew) + INIT_TYPE(BlobType, &ObjectType, NULL) + INIT_TYPE(TagType, &ObjectType, NULL) + /* References */ + INIT_TYPE(ReferenceType, NULL, PyType_GenericNew) + INIT_TYPE(RefLogEntryType, NULL, NULL) + INIT_TYPE(RefLogIterType, NULL, NULL) + /* Index */ + INIT_TYPE(IndexType, NULL, PyType_GenericNew) + INIT_TYPE(IndexEntryType, NULL, PyType_GenericNew) + INIT_TYPE(IndexIterType, NULL, NULL) + /* Diff */ + INIT_TYPE(DiffType, NULL, NULL) + INIT_TYPE(DiffIterType, NULL, NULL) + INIT_TYPE(PatchType, NULL, NULL) + INIT_TYPE(HunkType, NULL, NULL) + /* Log */ + INIT_TYPE(WalkerType, NULL, PyType_GenericNew) + /* Config */ + INIT_TYPE(ConfigType, NULL, PyType_GenericNew) + /* Remote */ + INIT_TYPE(RemoteType, NULL, NULL) + /* Notes */ + INIT_TYPE(NoteType, NULL, NULL) + INIT_TYPE(NoteIterType, NULL, NULL) Py_INCREF(GitError); PyModule_AddObject(m, "GitError", GitError); diff --git a/src/reference.c b/src/reference.c index 1191dcfca..35dc30d41 100644 --- a/src/reference.c +++ b/src/reference.c @@ -75,7 +75,7 @@ PyObject* RefLogIter_iternext(RefLogIter *self) PyDoc_STRVAR(RefLogIterType__doc__, "Internal reflog iterator object."); PyTypeObject RefLogIterType = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) + PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.RefLogIter", /* tp_name */ sizeof(RefLogIter), /* tp_basicsize */ 0, /* tp_itemsize */ diff --git a/src/tree.c b/src/tree.c index e79c53268..d2e5debf7 100644 --- a/src/tree.c +++ b/src/tree.c @@ -446,7 +446,7 @@ TreeIter_iternext(TreeIter *self) PyDoc_STRVAR(TreeIter__doc__, "Tree iterator."); PyTypeObject TreeIterType = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) + PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.TreeIter", /* tp_name */ sizeof(TreeIter), /* tp_basicsize */ 0, /* tp_itemsize */ From 83874bd2d105b25816104ad42470b6bfd1e9c230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 27 Mar 2013 19:11:10 +0100 Subject: [PATCH 0439/2237] Fix py_str_to_git_oid for raw oids --- src/oid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oid.c b/src/oid.c index 78335d72f..d391a84dc 100644 --- a/src/oid.c +++ b/src/oid.c @@ -46,7 +46,7 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) if (err) return -1; memcpy(oid->id, (const unsigned char*)hex_or_bin, len); - return len; + return len * 2; } /* Case 2: hex sha */ From 30e4367e916a3fc440f83fd8a45798059e342069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 29 Mar 2013 20:23:28 +0100 Subject: [PATCH 0440/2237] Move header files to src/ This is not a C library, so we don't have a public C API to expose. --- src/blob.c | 6 +++--- {include/pygit2 => src}/blob.h | 2 +- src/commit.c | 8 ++++---- {include/pygit2 => src}/commit.h | 0 src/config.c | 8 ++++---- {include/pygit2 => src}/config.h | 0 src/diff.c | 8 ++++---- {include/pygit2 => src}/diff.h | 2 +- src/error.c | 2 +- {include/pygit2 => src}/error.h | 0 src/index.c | 10 +++++----- {include/pygit2 => src}/index.h | 0 src/note.c | 10 +++++----- {include/pygit2 => src}/note.h | 0 src/object.c | 12 ++++++------ {include/pygit2 => src}/object.h | 2 +- src/oid.c | 6 +++--- {include/pygit2 => src}/oid.h | 0 src/pygit2.c | 10 +++++----- src/reference.c | 12 ++++++------ {include/pygit2 => src}/reference.h | 0 src/remote.c | 8 ++++---- {include/pygit2 => src}/remote.h | 0 src/repository.c | 18 +++++++++--------- {include/pygit2 => src}/repository.h | 2 +- src/signature.c | 10 +++++----- {include/pygit2 => src}/signature.h | 2 +- src/tag.c | 12 ++++++------ {include/pygit2 => src}/tag.h | 2 +- src/tree.c | 10 +++++----- {include/pygit2 => src}/tree.h | 2 +- src/treebuilder.c | 8 ++++---- {include/pygit2 => src}/treebuilder.h | 2 +- {include/pygit2 => src}/types.h | 0 src/utils.c | 4 ++-- {include/pygit2 => src}/utils.h | 2 +- src/walker.c | 10 +++++----- {include/pygit2 => src}/walker.h | 2 +- 38 files changed, 96 insertions(+), 96 deletions(-) rename {include/pygit2 => src}/blob.h (98%) rename {include/pygit2 => src}/commit.h (100%) rename {include/pygit2 => src}/config.h (100%) rename {include/pygit2 => src}/diff.h (98%) rename {include/pygit2 => src}/error.h (100%) rename {include/pygit2 => src}/index.h (100%) rename {include/pygit2 => src}/note.h (100%) rename {include/pygit2 => src}/object.h (98%) rename {include/pygit2 => src}/oid.h (100%) rename {include/pygit2 => src}/reference.h (100%) rename {include/pygit2 => src}/remote.h (100%) rename {include/pygit2 => src}/repository.h (99%) rename {include/pygit2 => src}/signature.h (98%) rename {include/pygit2 => src}/tag.h (98%) rename {include/pygit2 => src}/tree.h (98%) rename {include/pygit2 => src}/treebuilder.h (98%) rename {include/pygit2 => src}/types.h (100%) rename {include/pygit2 => src}/utils.h (99%) rename {include/pygit2 => src}/walker.h (98%) diff --git a/src/blob.c b/src/blob.c index c21ce0707..ec760ce43 100644 --- a/src/blob.c +++ b/src/blob.c @@ -27,9 +27,9 @@ #define PY_SSIZE_T_CLEAN #include -#include -#include -#include +#include "utils.h" +#include "object.h" +#include "blob.h" PyDoc_STRVAR(Blob_size__doc__, "Size."); diff --git a/include/pygit2/blob.h b/src/blob.h similarity index 98% rename from include/pygit2/blob.h rename to src/blob.h index 2edbb682b..5de1dee00 100644 --- a/include/pygit2/blob.h +++ b/src/blob.h @@ -31,7 +31,7 @@ #define PY_SSIZE_T_CLEAN #include #include -#include +#include "types.h" PyObject* Blob_get_size(Blob *self); diff --git a/src/commit.c b/src/commit.c index 7d2e2fc7f..139c8fd99 100644 --- a/src/commit.c +++ b/src/commit.c @@ -27,10 +27,10 @@ #define PY_SSIZE_T_CLEAN #include -#include -#include -#include -#include +#include "error.h" +#include "utils.h" +#include "signature.h" +#include "commit.h" extern PyTypeObject TreeType; diff --git a/include/pygit2/commit.h b/src/commit.h similarity index 100% rename from include/pygit2/commit.h rename to src/commit.h diff --git a/src/config.c b/src/config.c index b06c557e3..52a43c205 100644 --- a/src/config.c +++ b/src/config.c @@ -27,10 +27,10 @@ #define PY_SSIZE_T_CLEAN #include -#include -#include -#include -#include +#include "error.h" +#include "types.h" +#include "utils.h" +#include "config.h" extern PyTypeObject ConfigType; diff --git a/include/pygit2/config.h b/src/config.h similarity index 100% rename from include/pygit2/config.h rename to src/config.h diff --git a/src/diff.c b/src/diff.c index f2cb21ef0..d7c2b6927 100644 --- a/src/diff.c +++ b/src/diff.c @@ -28,10 +28,10 @@ #define PY_SSIZE_T_CLEAN #include #include -#include -#include -#include -#include +#include "error.h" +#include "types.h" +#include "utils.h" +#include "diff.h" extern PyObject *GitError; diff --git a/include/pygit2/diff.h b/src/diff.h similarity index 98% rename from include/pygit2/diff.h rename to src/diff.h index 202bff34c..b04559349 100644 --- a/include/pygit2/diff.h +++ b/src/diff.h @@ -31,7 +31,7 @@ #define PY_SSIZE_T_CLEAN #include #include -#include +#include "types.h" #define DIFF_CHECK_TYPES(_x, _y, _type_x, _type_y) \ PyObject_TypeCheck(_x, _type_x) && \ diff --git a/src/error.c b/src/error.c index a7912fdc8..730c5263b 100644 --- a/src/error.c +++ b/src/error.c @@ -25,7 +25,7 @@ * Boston, MA 02110-1301, USA. */ -#include +#include "error.h" PyObject *GitError; diff --git a/include/pygit2/error.h b/src/error.h similarity index 100% rename from include/pygit2/error.h rename to src/error.h diff --git a/src/index.c b/src/index.c index fed0c73ba..fc101161b 100644 --- a/src/index.c +++ b/src/index.c @@ -27,11 +27,11 @@ #define PY_SSIZE_T_CLEAN #include -#include -#include -#include -#include -#include +#include "error.h" +#include "types.h" +#include "utils.h" +#include "oid.h" +#include "index.h" extern PyTypeObject IndexType; extern PyTypeObject TreeType; diff --git a/include/pygit2/index.h b/src/index.h similarity index 100% rename from include/pygit2/index.h rename to src/index.h diff --git a/src/note.c b/src/note.c index c887036af..73dc90a83 100644 --- a/src/note.c +++ b/src/note.c @@ -28,11 +28,11 @@ #define PY_SSIZE_T_CLEAN #include #include -#include -#include -#include -#include -#include +#include "error.h" +#include "utils.h" +#include "types.h" +#include "oid.h" +#include "note.h" extern PyTypeObject SignatureType; diff --git a/include/pygit2/note.h b/src/note.h similarity index 100% rename from include/pygit2/note.h rename to src/note.h diff --git a/src/object.c b/src/object.c index c3a33fa6e..fc7922f45 100644 --- a/src/object.c +++ b/src/object.c @@ -27,12 +27,12 @@ #define PY_SSIZE_T_CLEAN #include -#include -#include -#include -#include -#include -#include +#include "error.h" +#include "types.h" +#include "utils.h" +#include "oid.h" +#include "repository.h" +#include "object.h" extern PyTypeObject TreeType; extern PyTypeObject CommitType; diff --git a/include/pygit2/object.h b/src/object.h similarity index 98% rename from include/pygit2/object.h rename to src/object.h index 314c55a42..7ab68a4a9 100644 --- a/include/pygit2/object.h +++ b/src/object.h @@ -31,7 +31,7 @@ #define PY_SSIZE_T_CLEAN #include #include -#include +#include "types.h" PyObject* Object_get_oid(Object *self); PyObject* Object_get_hex(Object *self); diff --git a/src/oid.c b/src/oid.c index d391a84dc..c0734031c 100644 --- a/src/oid.c +++ b/src/oid.c @@ -28,9 +28,9 @@ #define PY_SSIZE_T_CLEAN #include #include -#include -#include -#include +#include "utils.h" +#include "error.h" +#include "oid.h" int py_str_to_git_oid(PyObject *py_str, git_oid *oid) diff --git a/include/pygit2/oid.h b/src/oid.h similarity index 100% rename from include/pygit2/oid.h rename to src/oid.h diff --git a/src/pygit2.c b/src/pygit2.c index bbe3e4f05..ef41275a3 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -28,12 +28,12 @@ #define PY_SSIZE_T_CLEAN #include #include -#include -#include -#include -#include -#include #include +#include "error.h" +#include "types.h" +#include "utils.h" +#include "repository.h" +#include "oid.h" extern PyObject *GitError; diff --git a/src/reference.c b/src/reference.c index 35dc30d41..1c3ded896 100644 --- a/src/reference.c +++ b/src/reference.c @@ -29,12 +29,12 @@ #include #include #include -#include -#include -#include -#include -#include -#include +#include "error.h" +#include "types.h" +#include "utils.h" +#include "oid.h" +#include "signature.h" +#include "reference.h" extern PyObject *GitError; diff --git a/include/pygit2/reference.h b/src/reference.h similarity index 100% rename from include/pygit2/reference.h rename to src/reference.h diff --git a/src/remote.c b/src/remote.c index a01f7985e..df238a3c6 100644 --- a/src/remote.c +++ b/src/remote.c @@ -28,10 +28,10 @@ #define PY_SSIZE_T_CLEAN #include #include -#include -#include -#include -#include +#include "error.h" +#include "utils.h" +#include "types.h" +#include "remote.h" extern PyObject *GitError; extern PyTypeObject RepositoryType; diff --git a/include/pygit2/remote.h b/src/remote.h similarity index 100% rename from include/pygit2/remote.h rename to src/remote.h diff --git a/src/repository.c b/src/repository.c index f0b5d677f..62d190621 100644 --- a/src/repository.c +++ b/src/repository.c @@ -27,15 +27,15 @@ #define PY_SSIZE_T_CLEAN #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "error.h" +#include "types.h" +#include "reference.h" +#include "utils.h" +#include "object.h" +#include "oid.h" +#include "note.h" +#include "repository.h" +#include "remote.h" extern PyObject *GitError; diff --git a/include/pygit2/repository.h b/src/repository.h similarity index 99% rename from include/pygit2/repository.h rename to src/repository.h index 6d2d31a1d..4cb7d8a1b 100644 --- a/include/pygit2/repository.h +++ b/src/repository.h @@ -31,7 +31,7 @@ #define PY_SSIZE_T_CLEAN #include #include -#include +#include "types.h" int Repository_init(Repository *self, PyObject *args, PyObject *kwds); int Repository_traverse(Repository *self, visitproc visit, void *arg); diff --git a/src/signature.c b/src/signature.c index 982713e57..31da0bd6c 100644 --- a/src/signature.c +++ b/src/signature.c @@ -27,11 +27,11 @@ #define PY_SSIZE_T_CLEAN #include -#include -#include -#include -#include -#include +#include "error.h" +#include "types.h" +#include "utils.h" +#include "oid.h" +#include "signature.h" int Signature_init(Signature *self, PyObject *args, PyObject *kwds) diff --git a/include/pygit2/signature.h b/src/signature.h similarity index 98% rename from include/pygit2/signature.h rename to src/signature.h index fe69b984a..a0028a9d3 100644 --- a/include/pygit2/signature.h +++ b/src/signature.h @@ -31,7 +31,7 @@ #define PY_SSIZE_T_CLEAN #include #include -#include +#include "types.h" PyObject* Signature_get_encoding(Signature *self); PyObject* Signature_get_raw_name(Signature *self); diff --git a/src/tag.c b/src/tag.c index 2a9ba82fa..4edbde121 100644 --- a/src/tag.c +++ b/src/tag.c @@ -27,12 +27,12 @@ #define PY_SSIZE_T_CLEAN #include -#include -#include -#include -#include -#include -#include +#include "error.h" +#include "types.h" +#include "utils.h" +#include "signature.h" +#include "oid.h" +#include "tag.h" PyDoc_STRVAR(Tag_target__doc__, "Tagged object."); diff --git a/include/pygit2/tag.h b/src/tag.h similarity index 98% rename from include/pygit2/tag.h rename to src/tag.h index 8ac5fd7a0..04d3a1092 100644 --- a/include/pygit2/tag.h +++ b/src/tag.h @@ -31,7 +31,7 @@ #define PY_SSIZE_T_CLEAN #include #include -#include +#include "types.h" PyObject* Tag_get_target(Tag *self); PyObject* Tag_get_name(Tag *self); diff --git a/src/tree.c b/src/tree.c index d2e5debf7..6959a04d9 100644 --- a/src/tree.c +++ b/src/tree.c @@ -28,11 +28,11 @@ #define PY_SSIZE_T_CLEAN #include #include -#include -#include -#include -#include -#include +#include "error.h" +#include "utils.h" +#include "repository.h" +#include "oid.h" +#include "tree.h" extern PyTypeObject TreeType; extern PyTypeObject DiffType; diff --git a/include/pygit2/tree.h b/src/tree.h similarity index 98% rename from include/pygit2/tree.h rename to src/tree.h index 0e503fdfb..fef1facc5 100644 --- a/include/pygit2/tree.h +++ b/src/tree.h @@ -31,7 +31,7 @@ #define PY_SSIZE_T_CLEAN #include #include -#include +#include "types.h" PyObject* TreeEntry_get_filemode(TreeEntry *self); PyObject* TreeEntry_get_name(TreeEntry *self); diff --git a/src/treebuilder.c b/src/treebuilder.c index d7205bc01..30712aa64 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -28,10 +28,10 @@ #define PY_SSIZE_T_CLEAN #include #include -#include -#include -#include -#include +#include "error.h" +#include "utils.h" +#include "oid.h" +#include "treebuilder.h" void diff --git a/include/pygit2/treebuilder.h b/src/treebuilder.h similarity index 98% rename from include/pygit2/treebuilder.h rename to src/treebuilder.h index 322efe6a4..ad000dfff 100644 --- a/include/pygit2/treebuilder.h +++ b/src/treebuilder.h @@ -31,7 +31,7 @@ #define PY_SSIZE_T_CLEAN #include #include -#include +#include "types.h" PyObject* TreeBuilder_insert(TreeBuilder *self, PyObject *args); PyObject* TreeBuilder_write(TreeBuilder *self); diff --git a/include/pygit2/types.h b/src/types.h similarity index 100% rename from include/pygit2/types.h rename to src/types.h diff --git a/src/utils.c b/src/utils.c index 488ffa17b..01a24c3a7 100644 --- a/src/utils.c +++ b/src/utils.c @@ -27,8 +27,8 @@ #define PY_SSIZE_T_CLEAN #include -#include -#include +#include "error.h" +#include "utils.h" extern PyTypeObject ReferenceType; diff --git a/include/pygit2/utils.h b/src/utils.h similarity index 99% rename from include/pygit2/utils.h rename to src/utils.h index 3045144de..0b0913aaa 100644 --- a/include/pygit2/utils.h +++ b/src/utils.h @@ -31,7 +31,7 @@ #define PY_SSIZE_T_CLEAN #include #include -#include +#include "types.h" /* Python 2 support */ #if PY_MAJOR_VERSION == 2 diff --git a/src/walker.c b/src/walker.c index 9e483f785..2799feb4f 100644 --- a/src/walker.c +++ b/src/walker.c @@ -27,11 +27,11 @@ #define PY_SSIZE_T_CLEAN #include -#include -#include -#include -#include -#include +#include "error.h" +#include "utils.h" +#include "oid.h" +#include "tree.h" +#include "walker.h" extern PyTypeObject CommitType; diff --git a/include/pygit2/walker.h b/src/walker.h similarity index 98% rename from include/pygit2/walker.h rename to src/walker.h index 3a86bda90..9742ea97a 100644 --- a/include/pygit2/walker.h +++ b/src/walker.h @@ -31,7 +31,7 @@ #define PY_SSIZE_T_CLEAN #include #include -#include +#include "types.h" void Walker_dealloc(Walker *self); PyObject* Walker_hide(Walker *self, PyObject *py_hex); From 32e460fe164d508ad1922069fd2ddd73c06c92c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 3 Apr 2013 19:09:48 +0200 Subject: [PATCH 0441/2237] docs: complete and improve organization --- docs/config.rst | 20 ++++++--- docs/diff.rst | 33 ++++++++++++--- docs/index-file.rst | 28 ------------ docs/index.rst | 4 +- docs/log.rst | 2 + docs/objects.rst | 88 +++++++++++++++++++++++++------------- docs/references.rst | 46 +++++++++++++++++--- docs/remotes.rst | 16 +++++++ docs/repository.rst | 49 ++++++++++++--------- docs/revparse.rst | 2 + docs/status.rst | 11 ----- docs/utils.rst | 6 +++ docs/working-copy.rst | 66 +++++++++++++++++++++++++++++ pygit2/__init__.py | 8 ++++ src/pygit2.c | 99 +++++++++++++++++++------------------------ src/repository.c | 4 +- src/utils.h | 4 ++ 17 files changed, 322 insertions(+), 164 deletions(-) delete mode 100644 docs/index-file.rst create mode 100644 docs/remotes.rst delete mode 100644 docs/status.rst create mode 100644 docs/working-copy.rst diff --git a/docs/config.rst b/docs/config.rst index 3c1505256..e69fbca83 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -1,8 +1,18 @@ ********************************************************************** -Configuration file +Configuration files ********************************************************************** -.. autoclass:: pygit2.Config - :members: - :show-inheritance: - :undoc-members: +.. autoattribute:: pygit2.Repository.config + + +The Config type +================ + +.. automethod:: pygit2.Config.get_system_config +.. automethod:: pygit2.Config.get_global_config +.. automethod:: pygit2.Config.foreach +.. automethod:: pygit2.Config.add_file +.. automethod:: pygit2.Config.get_multivar +.. automethod:: pygit2.Config.set_multivar + +The :class:`Config` Mapping interface. diff --git a/docs/diff.rst b/docs/diff.rst index dc4029591..53ace3abc 100644 --- a/docs/diff.rst +++ b/docs/diff.rst @@ -2,7 +2,6 @@ Diff ********************************************************************** - A diff shows the changes between trees, an index or the working dir:: # Diff two trees @@ -19,12 +18,32 @@ A diff shows the changes between trees, an index or the working dir:: >>> tree = repo.head.tree >>> diff = tree.diff() -The interface for a diff:: - Diff.changes -- Dict of 'files' and 'hunks' for every change - Diff.patch -- a patch for every changeset - Diff.merge -- Merge two Diffs +The Diff type +==================== + +.. autoattribute:: pygit2.Diff.patch +.. automethod:: pygit2.Diff.merge +.. automethod:: pygit2.Diff.find_similar + + +The Patch type +==================== + +.. autoattribute:: pygit2.Patch.old_file_path +.. autoattribute:: pygit2.Patch.new_file_path +.. autoattribute:: pygit2.Patch.old_oid +.. autoattribute:: pygit2.Patch.new_oid +.. autoattribute:: pygit2.Patch.status +.. autoattribute:: pygit2.Patch.similarity +.. autoattribute:: pygit2.Patch.hunks + +The Hunk type +==================== -.. autoclass:: pygit2.Diff - :members: +.. autoattribute:: pygit2.Hunk.old_start +.. autoattribute:: pygit2.Hunk.old_lines +.. autoattribute:: pygit2.Hunk.new_start +.. autoattribute:: pygit2.Hunk.new_lines +.. autoattribute:: pygit2.Hunk.lines diff --git a/docs/index-file.rst b/docs/index-file.rst deleted file mode 100644 index 94ecf6fef..000000000 --- a/docs/index-file.rst +++ /dev/null @@ -1,28 +0,0 @@ -********************************************************************** -Index file -********************************************************************** - -Index read:: - - >>> index = repo.index - >>> index.read() - >>> oid = index['path/to/file'].oid # from path to object id - >>> blob = repo[oid] # from object id to object - -Iterate over all entries of the index:: - - >>> for entry in index: - ... print entry.path, entry.hex - -Index write:: - - >>> index.add('path/to/file') # git add - >>> del index['path/to/file'] # git rm - >>> index.write() # don't forget to save the changes - - -.. autoclass:: pygit2.Index - :members: add, remove, clear, read, write, read_tree, write_tree, diff - -.. autoclass:: pygit2.IndexEntry - :members: oid, hex, path, mode diff --git a/docs/index.rst b/docs/index.rst index dbd866d5e..daba596a4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -36,10 +36,10 @@ Usage guide: references revparse log + working-copy diff - index-file - status config + remotes errors More: diff --git a/docs/log.rst b/docs/log.rst index ea0378b2c..260b9d0cb 100644 --- a/docs/log.rst +++ b/docs/log.rst @@ -2,6 +2,8 @@ Commit log ********************************************************************** +.. automethod:: pygit2.Repository.walk + You can iterate through the revision history with repo.walk:: >>> from pygit2 import GIT_SORT_TIME diff --git a/docs/objects.rst b/docs/objects.rst index 3ceb31734..c49a0fbfb 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -37,24 +37,30 @@ Objects can not be modified once they have been created. This is the common interface for all Git objects: -.. autoclass:: pygit2.Object - :members: type, oid, hex, read_raw +.. autoattribute:: pygit2.Object.oid +.. autoattribute:: pygit2.Object.hex +.. autoattribute:: pygit2.Object.type +.. automethod:: pygit2.Object.read_raw Commits ------------------ +================= A commit is a snapshot of the working dir with meta informations like author, committer and others. -.. autoclass:: pygit2.Commit - :members: author, committer, message, message_encoding, tree, parents, - commit_time, commit_time_offset - :show-inheritance: +.. autoattribute:: pygit2.Commit.author +.. autoattribute:: pygit2.Commit.committer +.. autoattribute:: pygit2.Commit.message +.. autoattribute:: pygit2.Commit.message_encoding +.. autoattribute:: pygit2.Commit.tree +.. autoattribute:: pygit2.Commit.parents +.. autoattribute:: pygit2.Commit.commit_time +.. autoattribute:: pygit2.Commit.commit_time_offset Signatures -............. +------------- The author and committer attributes of commit objects are ``Signature`` objects:: @@ -62,12 +68,16 @@ objects:: >>> commit.author -.. autoclass:: pygit2.Signature - :members: name, email, time, offset +.. autoattribute:: pygit2.Signature.name +.. autoattribute:: pygit2.Signature.email +.. autoattribute:: pygit2.Signature.time +.. autoattribute:: pygit2.Signature.offset Creating commits -................ +---------------- + +.. automethod:: pygit2.Repository.create_commit Commits can be created by calling the ``create_commit`` method of the repository with the following parameters:: @@ -85,7 +95,7 @@ repository with the following parameters:: Trees ------------------ +================= A tree is a sorted collection of tree entries. It is similar to a folder or directory in a file system. Each entry points to another tree or a blob. A @@ -118,19 +128,28 @@ interfaces:: >>> blob -.. autoclass:: pygit2.Tree - :members: - :show-inheritance: - :undoc-members: +.. automethod:: pygit2.Tree.diff + +.. autoattribute:: pygit2.TreeEntry.name +.. autoattribute:: pygit2.TreeEntry.oid +.. autoattribute:: pygit2.TreeEntry.hex +.. autoattribute:: pygit2.TreeEntry.filemode +.. automethod:: pygit2.TreeEntry.to_object + + +Creating trees +-------------------- + +.. automethod:: pygit2.Repository.TreeBuilder -.. autoclass:: pygit2.TreeEntry - :members: name, oid, hex, filemode, to_object - :show-inheritance: - :undoc-members: +.. automethod:: pygit2.TreeBuilder.insert +.. automethod:: pygit2.TreeBuilder.remove +.. automethod:: pygit2.TreeBuilder.clear +.. automethod:: pygit2.TreeBuilder.write Blobs ------------------ +================= A blob is equivalent to a file in a file system.:: @@ -142,19 +161,28 @@ A blob is equivalent to a file in a file system.:: >>> oid '\x96\xc9\x06um{\x91\xc4S"a|\x92\x95\xe4\xa8\rR\xd1\xc5' -.. autoclass:: pygit2.Blob - :members: - :show-inheritance: - :undoc-members: +.. autoattribute:: pygit2.Blob.data +.. autoattribute:: pygit2.Blob.size + +Creating blobs +-------------------- + +.. automethod:: pygit2.Repository.create_blob +.. automethod:: pygit2.Repository.create_blob_fromfile Tags ------------------ +================= A tag is a static label for a commit. See references for more information. +.. autoattribute:: pygit2.Tag.name +.. autoattribute:: pygit2.Tag.target +.. autoattribute:: pygit2.Tag.tagger +.. autoattribute:: pygit2.Tag.message + + +Creating tags +-------------------- -.. autoclass:: pygit2.Tag - :members: - :show-inheritance: - :undoc-members: +.. automethod:: pygit2.Repository.create_tag diff --git a/docs/references.rst b/docs/references.rst index 2adce486e..0725c9e25 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -2,6 +2,9 @@ References ********************************************************************** +.. automethod:: pygit2.Repository.listall_references +.. automethod:: pygit2.Repository.lookup_reference + Reference lookup:: >>> all_refs = repo.listall_references() @@ -22,9 +25,42 @@ The interface for RefLogEntry:: RefLogEntry.oid_new -- oid of new reference -.. Autogenerated +The Reference type +==================== + +.. autoattribute:: pygit2.Reference.name +.. autoattribute:: pygit2.Reference.oid +.. autoattribute:: pygit2.Reference.hex +.. autoattribute:: pygit2.Reference.target +.. autoattribute:: pygit2.Reference.type + +.. automethod:: pygit2.Reference.delete +.. automethod:: pygit2.Reference.rename +.. automethod:: pygit2.Reference.resolve +.. automethod:: pygit2.Reference.log + + +The reference log +-------------------- + +.. autoattribute:: pygit2.RefLogEntry.oid_new +.. autoattribute:: pygit2.RefLogEntry.oid_old +.. autoattribute:: pygit2.RefLogEntry.message +.. autoattribute:: pygit2.RefLogEntry.committer + + +Notes +==================== + +.. automethod:: pygit2.Repository.notes +.. automethod:: pygit2.Repository.create_note +.. automethod:: pygit2.Repository.lookup_note + + +The Note type +-------------------- -.. autoclass:: pygit2.Reference - :members: - :show-inheritance: - :undoc-members: +.. autoattribute:: pygit2.Note.annotated_id +.. autoattribute:: pygit2.Note.oid +.. autoattribute:: pygit2.Note.message +.. automethod:: pygit2.Note.remove diff --git a/docs/remotes.rst b/docs/remotes.rst new file mode 100644 index 000000000..9042cbb14 --- /dev/null +++ b/docs/remotes.rst @@ -0,0 +1,16 @@ +********************************************************************** +Remotes +********************************************************************** + + +.. autoattribute:: pygit2.Repository.remotes +.. automethod:: pygit2.Repository.create_remote + + +The Remote type +==================== + +.. autoattribute:: pygit2.Remote.name +.. autoattribute:: pygit2.Remote.url +.. autoattribute:: pygit2.Remote.fetchspec +.. automethod:: pygit2.Remote.fetch diff --git a/docs/repository.rst b/docs/repository.rst index a38f052ff..e13160b92 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -2,34 +2,45 @@ The repository ********************************************************************** +Everything starts either by creating a new repository, or by opening an +existing one. + + +Creating a repository +=================================== + .. autofunction:: pygit2.init_repository - This is how to create non-bare repository:: +This is how to create non-bare repository:: - >>> from pygit2 import init_repository - >>> repo = init_repository('test') + >>> from pygit2 import init_repository + >>> repo = init_repository('test') - And this is how to create a bare repository:: +And this is how to create a bare repository:: - >>> from pygit2 import init_repository - >>> repo = init_repository('test', bare=True) + >>> from pygit2 import init_repository + >>> repo = init_repository('test', bare=True) - But one can also do:: +But one can also do:: - >>> from pygit2 import init_repository - >>> repo = init_repository('test', True) + >>> from pygit2 import init_repository + >>> repo = init_repository('test', True) -.. autofunction:: pygit2.discover_repository +The Repository class +=================================== -.. autoclass:: pygit2.Repository - :members: path, workdir, is_bare, is_empty, revparse_single, read, write, - create_blob, create_blob_fromfile, create_commit, create_tag, - TreeBuilder, walk, create_reference, listall_references, - lookup_reference, packall_references, head, head_is_detached, - head_is_orphaned, index, status, status_file, config +To open an existing repository:: - To open an existing repository:: + >>> from pygit2 import Repository + >>> repo = Repository('pygit2/.git') - >>> from pygit2 import Repository - >>> repo = Repository('pygit2/.git') +.. autoattribute:: pygit2.Repository.path +.. autoattribute:: pygit2.Repository.workdir +.. autoattribute:: pygit2.Repository.is_bare +.. autoattribute:: pygit2.Repository.is_empty +.. automethod:: pygit2.Repository.read +.. automethod:: pygit2.Repository.write +.. autoattribute:: pygit2.Repository.head +.. autoattribute:: pygit2.Repository.head_is_detached +.. autoattribute:: pygit2.Repository.head_is_orphaned diff --git a/docs/revparse.rst b/docs/revparse.rst index 42c11a5ef..f3ee4d49e 100644 --- a/docs/revparse.rst +++ b/docs/revparse.rst @@ -2,6 +2,8 @@ Revision parsing ********************************************************************** +.. automethod:: pygit2.Repository.revparse_single + You can use any of the fancy `` forms supported by libgit2:: >>> commit = repo.revparse_single('HEAD^') diff --git a/docs/status.rst b/docs/status.rst deleted file mode 100644 index 6bfdbfcea..000000000 --- a/docs/status.rst +++ /dev/null @@ -1,11 +0,0 @@ -********************************************************************** -Status -********************************************************************** - -Inspect the status of the repository:: - - >>> from pygit2 import GIT_STATUS_CURRENT - >>> status = repo.status() - >>> for filepath, flags in status.items(): - ... if flags != GIT_STATUS_CURRENT: - ... print "Filepath %s isn't clean" % filepath diff --git a/docs/utils.rst b/docs/utils.rst index 42d0d63f1..c68301228 100644 --- a/docs/utils.rst +++ b/docs/utils.rst @@ -2,6 +2,12 @@ Utilities ********************************************************************** +.. autofunction:: pygit2.discover_repository + +.. autofunction:: pygit2.hash + +.. autofunction:: pygit2.hashfile + .. automodule:: pygit2.utils :members: :show-inheritance: diff --git a/docs/working-copy.rst b/docs/working-copy.rst new file mode 100644 index 000000000..593fcd151 --- /dev/null +++ b/docs/working-copy.rst @@ -0,0 +1,66 @@ +********************************************************************** +The Index file and the Working copy +********************************************************************** + +.. autoattribute:: pygit2.Repository.index + +Index read:: + + >>> index = repo.index + >>> index.read() + >>> oid = index['path/to/file'].oid # from path to object id + >>> blob = repo[oid] # from object id to object + +Iterate over all entries of the index:: + + >>> for entry in index: + ... print entry.path, entry.hex + +Index write:: + + >>> index.add('path/to/file') # git add + >>> del index['path/to/file'] # git rm + >>> index.write() # don't forget to save the changes + + +The Index type +==================== + +.. automethod:: pygit2.Index.add +.. automethod:: pygit2.Index.remove +.. automethod:: pygit2.Index.clear +.. automethod:: pygit2.Index.read +.. automethod:: pygit2.Index.write +.. automethod:: pygit2.Index.read_tree +.. automethod:: pygit2.Index.write_tree +.. automethod:: pygit2.Index.diff + + +The IndexEntry type +-------------------- + +.. autoattribute:: pygit2.IndexEntry.oid +.. autoattribute:: pygit2.IndexEntry.hex +.. autoattribute:: pygit2.IndexEntry.path +.. autoattribute:: pygit2.IndexEntry.mode + + +Status +==================== + +.. automethod:: pygit2.Repository.status +.. automethod:: pygit2.Repository.status_file + +Inspect the status of the repository:: + + >>> from pygit2 import GIT_STATUS_CURRENT + >>> status = repo.status() + >>> for filepath, flags in status.items(): + ... if flags != GIT_STATUS_CURRENT: + ... print "Filepath %s isn't clean" % filepath + + +Checkout +==================== + +.. automethod:: pygit2.Repository.checkout diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 12634f1f7..fbaae96cb 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -41,6 +41,14 @@ def init_repository(path, bare=False): """ Creates a new Git repository in the given path. + + Arguments: + + path + Path where to create the repository. + + bare + Whether the repository will be bare or not. """ _pygit2.init_repository(path, bare) return Repository(path) diff --git a/src/pygit2.c b/src/pygit2.c index ef41275a3..cafd5e05c 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -66,9 +66,17 @@ extern PyTypeObject NoteIterType; PyDoc_STRVAR(init_repository__doc__, - "init_repository(path, bare)\n" - "\n" - "Creates a new Git repository in the given path."); + "init_repository(path, bare)\n" + "\n" + "Creates a new Git repository in the given path.\n" + "\n" + "Arguments:\n" + "\n" + "path\n" + " Path where to create the repository.\n" + "\n" + "bare\n" + " Whether the repository will be bare or not.\n"); PyObject * init_repository(PyObject *self, PyObject *args) { @@ -115,7 +123,7 @@ discover_repository(PyObject *self, PyObject *args) }; PyDoc_STRVAR(hashfile__doc__, - "hash(path) -> bytes\n" + "hashfile(path) -> bytes\n" "\n" "Returns the oid of a new blob from a file path without actually writing \n" "to the odb."); @@ -176,10 +184,15 @@ moduleinit(PyObject* m) if (m == NULL) return NULL; + /* Errors */ GitError = PyErr_NewException("_pygit2.GitError", NULL, NULL); + Py_INCREF(GitError); + PyModule_AddObject(m, "GitError", GitError); /* Repository */ INIT_TYPE(RepositoryType, NULL, PyType_GenericNew) + ADD_TYPE(m, Repository); + /* Objects (make them with the Repository.create_XXX methods). */ INIT_TYPE(ObjectType, NULL, NULL) INIT_TYPE(CommitType, &ObjectType, NULL) @@ -190,77 +203,53 @@ moduleinit(PyObject* m) INIT_TYPE(TreeBuilderType, NULL, PyType_GenericNew) INIT_TYPE(BlobType, &ObjectType, NULL) INIT_TYPE(TagType, &ObjectType, NULL) + ADD_TYPE(m, Object); + ADD_TYPE(m, Commit); + ADD_TYPE(m, Signature); + ADD_TYPE(m, Tree); + ADD_TYPE(m, TreeEntry); + ADD_TYPE(m, TreeBuilder); + ADD_TYPE(m, Blob); + ADD_TYPE(m, Tag); + /* References */ INIT_TYPE(ReferenceType, NULL, PyType_GenericNew) INIT_TYPE(RefLogEntryType, NULL, NULL) INIT_TYPE(RefLogIterType, NULL, NULL) + INIT_TYPE(NoteType, NULL, NULL) + INIT_TYPE(NoteIterType, NULL, NULL) + ADD_TYPE(m, Reference); + ADD_TYPE(m, RefLogEntry); + ADD_TYPE(m, Note); + /* Index */ INIT_TYPE(IndexType, NULL, PyType_GenericNew) INIT_TYPE(IndexEntryType, NULL, PyType_GenericNew) INIT_TYPE(IndexIterType, NULL, NULL) + ADD_TYPE(m, Index); + ADD_TYPE(m, IndexEntry); + /* Diff */ INIT_TYPE(DiffType, NULL, NULL) INIT_TYPE(DiffIterType, NULL, NULL) INIT_TYPE(PatchType, NULL, NULL) INIT_TYPE(HunkType, NULL, NULL) + ADD_TYPE(m, Diff); + ADD_TYPE(m, Patch); + ADD_TYPE(m, Hunk); + /* Log */ INIT_TYPE(WalkerType, NULL, PyType_GenericNew) + /* Config */ INIT_TYPE(ConfigType, NULL, PyType_GenericNew) + ADD_TYPE(m, Config); + /* Remote */ INIT_TYPE(RemoteType, NULL, NULL) - /* Notes */ - INIT_TYPE(NoteType, NULL, NULL) - INIT_TYPE(NoteIterType, NULL, NULL) - - Py_INCREF(GitError); - PyModule_AddObject(m, "GitError", GitError); - - Py_INCREF(&RepositoryType); - PyModule_AddObject(m, "Repository", (PyObject *)&RepositoryType); - - Py_INCREF(&ObjectType); - PyModule_AddObject(m, "Object", (PyObject *)&ObjectType); - - Py_INCREF(&CommitType); - PyModule_AddObject(m, "Commit", (PyObject *)&CommitType); - - Py_INCREF(&TreeEntryType); - PyModule_AddObject(m, "TreeEntry", (PyObject *)&TreeEntryType); - - Py_INCREF(&TreeType); - PyModule_AddObject(m, "Tree", (PyObject *)&TreeType); - - Py_INCREF(&ConfigType); - PyModule_AddObject(m, "Config", (PyObject *)&ConfigType); - - Py_INCREF(&BlobType); - PyModule_AddObject(m, "Blob", (PyObject *)&BlobType); - - Py_INCREF(&TagType); - PyModule_AddObject(m, "Tag", (PyObject *)&TagType); - - Py_INCREF(&IndexType); - PyModule_AddObject(m, "Index", (PyObject *)&IndexType); - - Py_INCREF(&IndexEntryType); - PyModule_AddObject(m, "IndexEntry", (PyObject *)&IndexEntryType); - - Py_INCREF(&DiffType); - PyModule_AddObject(m, "Diff", (PyObject *)&DiffType); - - Py_INCREF(&ReferenceType); - PyModule_AddObject(m, "Reference", (PyObject *)&ReferenceType); - - Py_INCREF(&SignatureType); - PyModule_AddObject(m, "Signature", (PyObject *)&SignatureType); - - Py_INCREF(&RemoteType); - PyModule_AddObject(m, "Remote", (PyObject *)&RemoteType); - - Py_INCREF(&NoteType); - PyModule_AddObject(m, "Note", (PyObject *)&NoteType); + ADD_TYPE(m, Remote); + /* Constants */ 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/repository.c b/src/repository.c index 62d190621..d68fcfd47 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1009,7 +1009,7 @@ Repository_TreeBuilder(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_remote__doc__, - "remote_create(name, url) -> Remote\n" + "create_remote(name, url) -> Remote\n" "\n" "Creates a new remote."); @@ -1036,7 +1036,7 @@ Repository_create_remote(Repository *self, PyObject *args) } -PyDoc_STRVAR(Repository_remotes__doc__, "returns all configured remotes"); +PyDoc_STRVAR(Repository_remotes__doc__, "Returns all configured remotes."); PyObject * Repository_remotes__get__(Repository *self) diff --git a/src/utils.h b/src/utils.h index 0b0913aaa..0dbe20bfd 100644 --- a/src/utils.h +++ b/src/utils.h @@ -141,5 +141,9 @@ char * py_str_to_c_str(PyObject *value, const char *encoding); if (new != NULL) type.tp_new = new; \ if (PyType_Ready(&type) < 0) return NULL; +#define ADD_TYPE(module, type) \ + Py_INCREF(& type ## Type); \ + PyModule_AddObject(module, #type, (PyObject *) & type ## Type); + #endif From 69c5e21b8db53bcba307e24ea6dae0b64abe8550 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 5 Apr 2013 14:27:49 +0200 Subject: [PATCH 0442/2237] fixed typo for GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH thanks to cboos for reporting. --- src/pygit2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2.c b/src/pygit2.c index cafd5e05c..d23102518 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -377,7 +377,7 @@ moduleinit(PyObject* m) GIT_CHECKOUT_DONT_UPDATE_INDEX); PyModule_AddIntConstant(m, "GIT_CHECKOUT_NO_REFRESH", GIT_CHECKOUT_NO_REFRESH); - PyModule_AddIntConstant(m, "GIT_CHECKOUT_DISABLE_PATHSPEC_MATC", + PyModule_AddIntConstant(m, "GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH", GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH); return m; From c5b6bce8865a715bf167451817a9e7d7fa8ef387 Mon Sep 17 00:00:00 2001 From: Christian Boos Date: Fri, 5 Apr 2013 14:40:25 +0200 Subject: [PATCH 0443/2237] pygit2 should work with libgit2 built with THREADSAFE=ON --- src/pygit2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pygit2.c b/src/pygit2.c index cafd5e05c..cc6d08acd 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -380,6 +380,9 @@ moduleinit(PyObject* m) PyModule_AddIntConstant(m, "GIT_CHECKOUT_DISABLE_PATHSPEC_MATC", GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH); + /* Global initialization of libgit2 */ + git_threads_init(); + return m; } From 41bae9150d8b69cf66944d5376a5c8d3f4143ccf Mon Sep 17 00:00:00 2001 From: Benjamin Pollack Date: Wed, 10 Apr 2013 15:26:13 +0000 Subject: [PATCH 0444/2237] Fix docstring for Commit.init --- src/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index 52a43c205..8cb035968 100644 --- a/src/config.c +++ b/src/config.c @@ -60,7 +60,7 @@ Config_init(Config *self, PyObject *args, PyObject *kwds) if (kwds) { PyErr_SetString(PyExc_TypeError, - "Repository takes no keyword arguments"); + "Config takes no keyword arguments"); return -1; } From c7785e591b473ba082b919db9e8d94a981bff040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 13 Apr 2013 12:00:18 +0200 Subject: [PATCH 0445/2237] py_str_to_git_oid: check length of raw oid (#205) --- src/oid.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/oid.c b/src/oid.c index c0734031c..413d29ce3 100644 --- a/src/oid.c +++ b/src/oid.c @@ -45,6 +45,10 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) err = PyBytes_AsStringAndSize(py_str, &hex_or_bin, &len); if (err) return -1; + if (len > GIT_OID_RAWSZ) { + PyErr_SetObject(PyExc_ValueError, py_str); + return -1; + } memcpy(oid->id, (const unsigned char*)hex_or_bin, len); return len * 2; } From f8544cc5140f12d0f2413ffcd3049839753d5365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 13 Apr 2013 13:04:52 +0200 Subject: [PATCH 0446/2237] Add Oid type --- src/oid.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++ src/pygit2.c | 5 +++ src/types.h | 6 +++ test/__init__.py | 6 +-- test/test_oid.py | 70 ++++++++++++++++++++++++++++++++++ 5 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 test/test_oid.py diff --git a/src/oid.c b/src/oid.c index 413d29ce3..c671aa6d6 100644 --- a/src/oid.c +++ b/src/oid.c @@ -125,3 +125,102 @@ git_oid_to_py_str(const git_oid *oid) return to_unicode_n(hex, GIT_OID_HEXSZ, "utf-8", "strict"); } + +int +Oid_init(Oid *self, PyObject *args, PyObject *kw) +{ + char *keywords[] = {"raw", "hex", NULL}; + PyObject *raw = NULL, *hex = NULL; + int err; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "|OO", keywords, &raw, &hex)) + return -1; + + /* We expect one or the other, but not both. */ + if (raw == NULL && hex == NULL) { + PyErr_SetString(PyExc_ValueError, "Expected raw or hex."); + return -1; + } + if (raw != NULL && hex != NULL) { + PyErr_SetString(PyExc_ValueError, "Expected raw or hex, not both."); + return -1; + } + + /* Get the oid. */ + if (raw != NULL) + err = py_str_to_git_oid(raw, &self->oid); + else + err = py_str_to_git_oid(hex, &self->oid); + + if (err < 0) + return -1; + + return 0; +} + + +PyDoc_STRVAR(Oid_raw__doc__, "Raw oid."); + +PyObject * +Oid_raw__get__(Oid *self) +{ + return git_oid_to_python(self->oid.id); +} + + +PyDoc_STRVAR(Oid_hex__doc__, "Hex oid."); + +PyObject * +Oid_hex__get__(Oid *self) +{ + return git_oid_to_py_str(&self->oid); +} + +PyGetSetDef Oid_getseters[] = { + GETTER(Oid, raw), + GETTER(Oid, hex), + {NULL}, +}; + +PyDoc_STRVAR(Oid__doc__, "Object id."); + +PyTypeObject OidType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.Oid", /* tp_name */ + sizeof(Oid), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* 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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + Oid__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 */ + Oid_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Oid_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; diff --git a/src/pygit2.c b/src/pygit2.c index f1775fa65..6b9363079 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -38,6 +38,7 @@ extern PyObject *GitError; extern PyTypeObject RepositoryType; +extern PyTypeObject OidType; extern PyTypeObject ObjectType; extern PyTypeObject CommitType; extern PyTypeObject DiffType; @@ -193,6 +194,10 @@ moduleinit(PyObject* m) INIT_TYPE(RepositoryType, NULL, PyType_GenericNew) ADD_TYPE(m, Repository); + /* Oid */ + INIT_TYPE(OidType, NULL, PyType_GenericNew) + ADD_TYPE(m, Oid); + /* Objects (make them with the Repository.create_XXX methods). */ INIT_TYPE(ObjectType, NULL, NULL) INIT_TYPE(CommitType, &ObjectType, NULL) diff --git a/src/types.h b/src/types.h index 2de5872c7..eceeeb9b1 100644 --- a/src/types.h +++ b/src/types.h @@ -46,6 +46,12 @@ typedef struct { } Repository; +typedef struct { + PyObject_HEAD + git_oid oid; +} Oid; + + #define SIMPLE_TYPE(_name, _ptr_type, _ptr_name) \ typedef struct {\ PyObject_HEAD\ diff --git a/test/__init__.py b/test/__init__.py index e81179b99..024476b75 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -35,9 +35,9 @@ import unittest -names = ['blob', 'commit', 'config', 'diff', 'index', 'refs', 'remote', - 'repository', 'revwalk', 'signature', 'status', 'tag', 'tree', - 'treebuilder', 'note'] +names = ['blob', 'commit', 'config', 'diff', 'index', 'note', 'oid', 'refs', + 'remote', 'repository', 'revwalk', 'signature', 'status', 'tag', + 'tree', 'treebuilder'] def test_suite(): # Sometimes importing pygit2 fails, we try this first to get an diff --git a/test/test_oid.py b/test/test_oid.py new file mode 100644 index 000000000..7b8d8a4b6 --- /dev/null +++ b/test/test_oid.py @@ -0,0 +1,70 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2010-2013 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. + +"""Tests for Object ids.""" + +# Import from the future +from __future__ import absolute_import +from __future__ import unicode_literals + +# Import from the Standard Library +from binascii import unhexlify +import unittest + +# Import from pygit2 +from pygit2 import Oid +from . import utils + + +HEX = "15b648aec6ed045b5ca6f57f8b7831a8b4757298" +RAW = unhexlify(HEX.encode('ascii')) + +class OidTest(utils.BareRepoTestCase): + + def test_raw(self): + oid = Oid(raw=RAW) + self.assertEqual(oid.raw, RAW) + self.assertEqual(oid.hex, HEX) + + def test_hex(self): + oid = Oid(hex=HEX) + self.assertEqual(oid.raw, RAW) + self.assertEqual(oid.hex, HEX) + + def test_none(self): + self.assertRaises(ValueError, Oid) + + def test_both(self): + self.assertRaises(ValueError, Oid, raw=RAW, hex=HEX) + + def test_long(self): + self.assertRaises(ValueError, Oid, raw=RAW + b'a') + self.assertRaises(ValueError, Oid, hex=HEX + 'a') + + +if __name__ == '__main__': + unittest.main() From 406c3175728cd931394e7e450b38ec80ace5db55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 14 Apr 2013 12:26:22 +0200 Subject: [PATCH 0447/2237] Return Oid wherever we returned raw oid (bytes) before Changes: - Return Oid wherever we returned raw oid (bytes) before - Now py_str_to_git_oid accepts Oid objects - Add ability to compare two Oid objects --- src/index.c | 4 ++-- src/object.c | 2 +- src/oid.c | 35 +++++++++++++++++++++++++++++++---- src/oid.h | 6 ++---- src/pygit2.c | 4 ++-- src/reference.c | 2 +- src/repository.c | 12 ++++++------ src/tag.c | 2 +- src/tree.c | 2 +- test/test_blob.py | 12 +++++------- test/test_index.py | 6 ++---- test/test_note.py | 6 +++--- test/test_oid.py | 8 ++++++++ test/test_repository.py | 15 ++++++++++----- test/test_tag.py | 2 +- test/utils.py | 3 +-- 16 files changed, 77 insertions(+), 44 deletions(-) diff --git a/src/index.c b/src/index.c index fc101161b..67defb74e 100644 --- a/src/index.c +++ b/src/index.c @@ -423,7 +423,7 @@ Index_write_tree(Index *self) if (err < 0) return Error_set(err); - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } PyMethodDef Index_methods[] = { @@ -585,7 +585,7 @@ PyDoc_STRVAR(IndexEntry_oid__doc__, "Object id."); PyObject * IndexEntry_oid__get__(IndexEntry *self) { - return git_oid_to_python(self->entry->oid.id); + return git_oid_to_python(&self->entry->oid); } diff --git a/src/object.c b/src/object.c index fc7922f45..f5f9c4fe9 100644 --- a/src/object.c +++ b/src/object.c @@ -60,7 +60,7 @@ Object_oid__get__(Object *self) oid = git_object_id(self->obj); assert(oid); - return git_oid_to_python(oid->id); + return git_oid_to_python(oid); } diff --git a/src/oid.c b/src/oid.c index c671aa6d6..3bc79a95a 100644 --- a/src/oid.c +++ b/src/oid.c @@ -32,6 +32,20 @@ #include "error.h" #include "oid.h" +PyTypeObject OidType; + + +PyObject * +git_oid_to_python(const git_oid *oid) +{ + Oid *py_oid; + + py_oid = PyObject_New(Oid, &OidType); + git_oid_cpy(&(py_oid->oid), oid); + return (PyObject*)py_oid; +} + + int py_str_to_git_oid(PyObject *py_str, git_oid *oid) { @@ -40,7 +54,13 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) int err; Py_ssize_t len; - /* Case 1: raw sha */ + /* Case 1: Git Oid */ + if (PyObject_TypeCheck(py_str, (PyTypeObject*)&OidType)) { + git_oid_cpy(oid, &((Oid*)py_str)->oid); + return GIT_OID_RAWSZ; + } + + /* Case 2: raw sha (bytes) */ if (PyBytes_Check(py_str)) { err = PyBytes_AsStringAndSize(py_str, &hex_or_bin, &len); if (err) @@ -53,7 +73,7 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) return len * 2; } - /* Case 2: hex sha */ + /* Case 3: hex sha (unicode) */ if (PyUnicode_Check(py_str)) { py_hex = PyUnicode_AsASCIIString(py_str); if (py_hex == NULL) @@ -159,12 +179,19 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw) } +int +Oid_compare(PyObject *o1, PyObject *o2) +{ + return git_oid_cmp(&((Oid*)o1)->oid, &((Oid*)o2)->oid); +} + + PyDoc_STRVAR(Oid_raw__doc__, "Raw oid."); PyObject * Oid_raw__get__(Oid *self) { - return git_oid_to_python(self->oid.id); + return PyBytes_FromStringAndSize((const char*)self->oid.id, GIT_OID_RAWSZ); } @@ -193,7 +220,7 @@ PyTypeObject OidType = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_compare */ + (cmpfunc)Oid_compare, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ diff --git a/src/oid.h b/src/oid.h index 7dce30300..796d08720 100644 --- a/src/oid.h +++ b/src/oid.h @@ -34,10 +34,8 @@ int py_str_to_git_oid(PyObject *py_str, git_oid *oid); int py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, - git_oid *oid); + git_oid *oid); +PyObject* git_oid_to_python(const git_oid *oid); PyObject* git_oid_to_py_str(const git_oid *oid); -#define git_oid_to_python(id) \ - PyBytes_FromStringAndSize((const char*)id, GIT_OID_RAWSZ) - #endif diff --git a/src/pygit2.c b/src/pygit2.c index 6b9363079..4393870ae 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -142,7 +142,7 @@ hashfile(PyObject *self, PyObject *args) if (err < 0) return Error_set(err); - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } PyDoc_STRVAR(hash__doc__, @@ -166,7 +166,7 @@ hash(PyObject *self, PyObject *args) return Error_set(err); } - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } diff --git a/src/reference.c b/src/reference.c index 1c3ded896..0de113478 100644 --- a/src/reference.c +++ b/src/reference.c @@ -276,7 +276,7 @@ Reference_oid__get__(Reference *self) } /* Convert and return it */ - return git_oid_to_python(oid->id); + return git_oid_to_python(oid); } int diff --git a/src/repository.c b/src/repository.c index d68fcfd47..17f5ada4e 100644 --- a/src/repository.c +++ b/src/repository.c @@ -403,7 +403,7 @@ Repository_write(Repository *self, PyObject *args) stream->write(stream, buffer, buflen); err = stream->finalize_write(&oid, stream); stream->free(stream); - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } @@ -578,7 +578,7 @@ Repository_create_blob(Repository *self, PyObject *args) if (err < 0) return Error_set(err); - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } @@ -601,7 +601,7 @@ Repository_create_blob_fromfile(Repository *self, PyObject *args) if (err < 0) return Error_set(err); - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } @@ -674,7 +674,7 @@ Repository_create_commit(Repository *self, PyObject *args) goto out; } - py_result = git_oid_to_python(oid.id); + py_result = git_oid_to_python(&oid); out: free(message); @@ -722,7 +722,7 @@ Repository_create_tag(Repository *self, PyObject *args) git_object_free(target); if (err < 0) return Error_set_oid(err, &oid, len); - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } @@ -1171,7 +1171,7 @@ Repository_create_note(Repository *self, PyObject* args) if (err < 0) return Error_set(err); - return git_oid_to_python(note_id.id); + return git_oid_to_python(¬e_id); } diff --git a/src/tag.c b/src/tag.c index 4edbde121..bc4eaada7 100644 --- a/src/tag.c +++ b/src/tag.c @@ -43,7 +43,7 @@ Tag_target__get__(Tag *self) const git_oid *oid; oid = git_tag_target_id(self->tag); - return git_oid_to_python(oid->id); + return git_oid_to_python(oid); } diff --git a/src/tree.c b/src/tree.c index 6959a04d9..05ff4b614 100644 --- a/src/tree.c +++ b/src/tree.c @@ -74,7 +74,7 @@ TreeEntry_oid__get__(TreeEntry *self) const git_oid *oid; oid = git_tree_entry_id(self->entry); - return git_oid_to_python(oid->id); + return git_oid_to_python(oid); } diff --git a/test/test_blob.py b/test/test_blob.py index 221ddcc02..9123cbc57 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -49,7 +49,7 @@ class BlobTest(utils.RepoTestCase): def test_read_blob(self): blob = self.repo[BLOB_SHA] self.assertEqual(blob.hex, BLOB_SHA) - sha = utils.oid_to_hex(blob.oid) + sha = blob.oid.hex self.assertEqual(sha, BLOB_SHA) self.assertTrue(isinstance(blob, pygit2.Blob)) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) @@ -66,9 +66,8 @@ def test_create_blob(self): self.assertEqual(blob_oid, blob.oid) self.assertEqual( - utils.gen_blob_sha1(BLOB_NEW_CONTENT), - utils.oid_to_hex(blob_oid) - ) + utils.gen_blob_sha1(BLOB_NEW_CONTENT), + blob_oid.hex) self.assertEqual(BLOB_NEW_CONTENT, blob.data) self.assertEqual(len(BLOB_NEW_CONTENT), blob.size) @@ -84,9 +83,8 @@ def test_create_blob_fromfile(self): self.assertEqual(blob_oid, blob.oid) self.assertEqual( - utils.gen_blob_sha1(BLOB_FILE_CONTENT), - utils.oid_to_hex(blob_oid) - ) + utils.gen_blob_sha1(BLOB_FILE_CONTENT), + blob_oid.hex) self.assertEqual(BLOB_FILE_CONTENT, blob.data) self.assertEqual(len(BLOB_FILE_CONTENT), blob.size) diff --git a/test/test_index.py b/test/test_index.py index 20d98e232..b58137345 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -98,8 +98,7 @@ def test_read_tree(self): self.assertEqual(len(index), 1) # Test read-write returns the same oid oid = index.write_tree() - oid = utils.oid_to_hex(oid) - self.assertEqual(oid, tree_oid) + self.assertEqual(oid.hex, tree_oid) # Test the index is only modified in memory index.read() self.assertEqual(len(index), 2) @@ -107,8 +106,7 @@ def test_read_tree(self): def test_write_tree(self): oid = self.repo.index.write_tree() - sha = utils.oid_to_hex(oid) - self.assertEqual(sha, 'fd937514cb799514d4b81bb24c5fcfeb6472b245') + self.assertEqual(oid.hex, 'fd937514cb799514d4b81bb24c5fcfeb6472b245') def test_iter(self): index = self.repo.index diff --git a/test/test_note.py b/test/test_note.py index b0439e651..a493540cb 100644 --- a/test/test_note.py +++ b/test/test_note.py @@ -38,9 +38,9 @@ NOTES = [ ('ab533997b80705767be3dae8cbb06a0740809f79', 'First Note - HEAD\n', - '784855caf26449a1914d2cf62d12b9374d76ae78'), + '784855caf26449a1914d2cf62d12b9374d76ae78'), ('d879714d880671ed84f8aaed8b27fca23ba01f27', 'Second Note - HEAD~1\n', - 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87') + 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87') ] class NotesTest(utils.BareRepoTestCase): @@ -49,7 +49,7 @@ def test_create_note(self): annotated_id = self.repo.revparse_single('HEAD~3').hex author = committer = Signature('Foo bar', 'foo@bar.com', 12346, 0) note_id = self.repo.create_note(NOTE[1], author, committer, annotated_id) - self.assertEqual(NOTE[0], utils.oid_to_hex(note_id)) + self.assertEqual(NOTE[0], note_id.hex) # check the note blob self.assertEqual(NOTE[1].encode(), self.repo[note_id].data) diff --git a/test/test_oid.py b/test/test_oid.py index 7b8d8a4b6..26e980c67 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -65,6 +65,14 @@ def test_long(self): self.assertRaises(ValueError, Oid, raw=RAW + b'a') self.assertRaises(ValueError, Oid, hex=HEX + 'a') + def test_cmp(self): + oid1 = Oid(raw=RAW) + oid2 = Oid(hex=HEX) + self.assertEqual(oid1, oid2) + + oid2 = Oid(hex="15b648aec6ed045b5ca6f57f8b7831a8b4757299") + self.assertNotEqual(oid1, oid2) + if __name__ == '__main__': unittest.main() diff --git a/test/test_repository.py b/test/test_repository.py index 0e7503daf..6de720bdb 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -27,18 +27,22 @@ """Tests for Repository objects.""" +# Import from the future from __future__ import absolute_import from __future__ import unicode_literals + +# Import from the Standard Library import binascii import unittest import tempfile import os from os.path import join, realpath +# Import from pygit2 from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT from pygit2 import init_repository, discover_repository, Commit, hashfile +from pygit2 import Oid import pygit2 - from . import utils @@ -85,8 +89,7 @@ def test_write(self): self.assertRaises(ValueError, self.repo.write, GIT_OBJ_ANY, data) oid = self.repo.write(GIT_OBJ_BLOB, data) - self.assertEqual(type(oid), bytes) - self.assertEqual(len(oid), 20) + self.assertEqual(type(oid), Oid) def test_contains(self): self.assertRaises(TypeError, lambda: 123 in self.repo) @@ -222,16 +225,18 @@ def test_checkout_head(self): self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE, head=True) self.assertTrue('bye.txt' not in self.repo.status()) + class NewRepositoryTest(utils.NoRepoTestCase): + def test_new_repo(self): repo = init_repository(self._temp_dir, False) oid = repo.write(GIT_OBJ_BLOB, "Test") - self.assertEqual(type(oid), bytes) - self.assertEqual(len(oid), 20) + self.assertEqual(type(oid), Oid) assert os.path.exists(os.path.join(self._temp_dir, '.git')) + class InitRepositoryTest(utils.NoRepoTestCase): # under the assumption that repo.is_bare works diff --git a/test/test_tag.py b/test/test_tag.py index de79e6027..12d89f055 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -72,7 +72,7 @@ def test_new_tag(self): self.assertEqual('3ee44658fd11660e828dfc96b9b5c5f38d5b49bb', tag.hex) self.assertEqual(name, tag.name) - self.assertEqual(target, utils.oid_to_hex(tag.target)) + self.assertEqual(target, tag.target.hex) self.assertEqualSignature(tagger, tag.tagger) self.assertEqual(message, tag.message) self.assertEqual(name, self.repo[tag.hex].name) diff --git a/test/utils.py b/test/utils.py index 0cf41d57f..27da92acd 100644 --- a/test/utils.py +++ b/test/utils.py @@ -47,8 +47,6 @@ def force_rm_handle(remove_path, path, excinfo): ) remove_path(path) -def oid_to_hex(oid): - return b2a_hex(oid).decode('ascii') def gen_blob_sha1(data): # http://stackoverflow.com/questions/552659/assigning-git-sha1s-without-git @@ -58,6 +56,7 @@ def gen_blob_sha1(data): return m.hexdigest() + def rmtree(path): """In Windows a read-only file cannot be removed, and shutil.rmtree fails. So we implement our own version of rmtree to address this issue. From bd54d281570c15cb08d0d23f842a385de8a4c288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 14 Apr 2013 13:50:09 +0200 Subject: [PATCH 0448/2237] Fix for Python3, and add Oid cmp tests --- src/oid.c | 31 ++++++++++++++++++++++++++----- test/test_oid.py | 9 +++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/oid.c b/src/oid.c index 3bc79a95a..ce5ff9d49 100644 --- a/src/oid.c +++ b/src/oid.c @@ -179,10 +179,31 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw) } -int -Oid_compare(PyObject *o1, PyObject *o2) +PyObject * +Oid_richcompare(PyObject *o1, PyObject *o2, int op) { - return git_oid_cmp(&((Oid*)o1)->oid, &((Oid*)o2)->oid); + PyObject *res; + + /* Support only equual (and not-equal). */ + if (op != Py_EQ && op != Py_NE) { + PyErr_SetNone(PyExc_TypeError); + return NULL; + } + + /* Comparing to something else than an Oid is not supported. */ + if (!PyObject_TypeCheck(o2, &OidType)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + /* Ok go. */ + if (git_oid_cmp(&((Oid*)o1)->oid, &((Oid*)o2)->oid) == 0) + res = (op == Py_EQ) ? Py_True : Py_False; + else + res = (op == Py_EQ) ? Py_False : Py_True; + + Py_INCREF(res); + return res; } @@ -220,7 +241,7 @@ PyTypeObject OidType = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - (cmpfunc)Oid_compare, /* tp_compare */ + 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -235,7 +256,7 @@ PyTypeObject OidType = { Oid__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + (richcmpfunc)Oid_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ diff --git a/test/test_oid.py b/test/test_oid.py index 26e980c67..eb2a3fadc 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -67,12 +67,21 @@ def test_long(self): def test_cmp(self): oid1 = Oid(raw=RAW) + + # Equal oid2 = Oid(hex=HEX) self.assertEqual(oid1, oid2) + # Not equal oid2 = Oid(hex="15b648aec6ed045b5ca6f57f8b7831a8b4757299") self.assertNotEqual(oid1, oid2) + # Other + with self.assertRaises(TypeError): oid1 < oid2 + with self.assertRaises(TypeError): oid1 <= oid2 + with self.assertRaises(TypeError): oid1 > oid2 + with self.assertRaises(TypeError): oid1 >= oid2 + if __name__ == '__main__': unittest.main() From a9c9f25ce2461b2766729011ea7cdb44ba674ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 14 Apr 2013 14:08:46 +0200 Subject: [PATCH 0449/2237] oid: support rich comparisons (less than, etc.) --- src/oid.c | 32 ++++++++++++++++++++++---------- test/test_oid.py | 9 +++++---- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/oid.c b/src/oid.c index ce5ff9d49..b40e157da 100644 --- a/src/oid.c +++ b/src/oid.c @@ -183,12 +183,7 @@ PyObject * Oid_richcompare(PyObject *o1, PyObject *o2, int op) { PyObject *res; - - /* Support only equual (and not-equal). */ - if (op != Py_EQ && op != Py_NE) { - PyErr_SetNone(PyExc_TypeError); - return NULL; - } + int cmp; /* Comparing to something else than an Oid is not supported. */ if (!PyObject_TypeCheck(o2, &OidType)) { @@ -197,10 +192,27 @@ Oid_richcompare(PyObject *o1, PyObject *o2, int op) } /* Ok go. */ - if (git_oid_cmp(&((Oid*)o1)->oid, &((Oid*)o2)->oid) == 0) - res = (op == Py_EQ) ? Py_True : Py_False; - else - res = (op == Py_EQ) ? Py_False : Py_True; + cmp = git_oid_cmp(&((Oid*)o1)->oid, &((Oid*)o2)->oid); + switch (op) { + case Py_LT: + res = (cmp <= 0) ? Py_True: Py_False; + break; + case Py_LE: + res = (cmp < 0) ? Py_True: Py_False; + break; + case Py_EQ: + res = (cmp == 0) ? Py_True: Py_False; + break; + case Py_NE: + res = (cmp != 0) ? Py_True: Py_False; + break; + case Py_GT: + res = (cmp > 0) ? Py_True: Py_False; + break; + case Py_GE: + res = (cmp >= 0) ? Py_True: Py_False; + break; + } Py_INCREF(res); return res; diff --git a/test/test_oid.py b/test/test_oid.py index eb2a3fadc..22dcb0b24 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -77,10 +77,11 @@ def test_cmp(self): self.assertNotEqual(oid1, oid2) # Other - with self.assertRaises(TypeError): oid1 < oid2 - with self.assertRaises(TypeError): oid1 <= oid2 - with self.assertRaises(TypeError): oid1 > oid2 - with self.assertRaises(TypeError): oid1 >= oid2 + self.assertTrue(oid1 < oid2) + self.assertTrue(oid1 <= oid2) + self.assertFalse(oid1 == oid2) + self.assertFalse(oid1 > oid2) + self.assertFalse(oid1 >= oid2) if __name__ == '__main__': From 70fdabc4335b1e8d172c4782da979d66b671065e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 15 Apr 2013 11:21:17 +0200 Subject: [PATCH 0450/2237] revparse_single: add support for non ascii paths (#207) --- src/repository.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/repository.c b/src/repository.c index d68fcfd47..02748cd31 100644 --- a/src/repository.c +++ b/src/repository.c @@ -284,11 +284,10 @@ Repository_revparse_single(Repository *self, PyObject *py_spec) { git_object *c_obj; char *c_spec; - char *encoding = "ascii"; int err; /* 1- Get the C revision spec */ - c_spec = py_str_to_c_str(py_spec, encoding); + c_spec = py_str_to_c_str(py_spec, NULL); if (c_spec == NULL) return NULL; From 072cc38607ec8eac1bb75f6bd0a48fcfbdc795ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 16 Apr 2013 22:46:53 +0200 Subject: [PATCH 0451/2237] Release v0.18.0 --- README.rst | 8 +++++--- docs/conf.py | 4 ++-- pygit2/version.py | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index e9bcb41bb..87341613e 100644 --- a/README.rst +++ b/README.rst @@ -20,11 +20,12 @@ Pygit2 links: Quick install guide =================== -1. Download libgit2 v0.17.0 - https://github.com/downloads/libgit2/libgit2/libgit2-0.17.0.tar.gz +1. Checkout libgi2 v0.18.0:: + + $ git clone git://github.com/libgit2/libgit2.git -b v0.18.0 2. Build and install libgit2 - http://libgit2.github.com/#install + https://github.com/libgit2/libgit2/#building-libgit2---using-cmake 3. Install pygit2 with *pip*:: @@ -52,6 +53,7 @@ pygit2 project (sorted alphabetically): - András Veres-Szentkirályi - Ben Davis - Benjamin Kircher +- Benjamin Pollack - Bryan O'Sullivan - Carlos Martín Nieto - Christian Boos diff --git a/docs/conf.py b/docs/conf.py index e2826b5f7..4f3be7fac 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,9 +48,9 @@ # built documents. # # The short X.Y version. -version = '0.17' +version = '0.18' # The full version, including alpha/beta/rc tags. -release = '0.17.3' +release = '0.18.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/pygit2/version.py b/pygit2/version.py index 5607b073b..c5bbeaf95 100644 --- a/pygit2/version.py +++ b/pygit2/version.py @@ -23,4 +23,4 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -__version__ = '0.17.4dev' +__version__ = '0.18.0' From 611e979113e2e2d877ba5c206541beef9ffaf393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 17 Apr 2013 08:16:43 +0200 Subject: [PATCH 0452/2237] Now Repository.head returns a reference (#203) Now Repository.head behaves like libgit2's git_repository_head, it returns the resolved reference. These two lines are equivalent: ref = repo.head ref = repo.lookup_reference('HEAD').resolve() Before it returned a commit. --- docs/diff.rst | 13 ++++++------- docs/objects.rst | 3 --- docs/references.rst | 18 ++++++++++++------ docs/repository.rst | 3 --- src/repository.c | 5 +---- test/test_repository.py | 20 ++++++++++++++------ 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/docs/diff.rst b/docs/diff.rst index 53ace3abc..513d08ed1 100644 --- a/docs/diff.rst +++ b/docs/diff.rst @@ -4,19 +4,18 @@ Diff A diff shows the changes between trees, an index or the working dir:: + >>> head = repo.revparse_single('HEAD') + # Diff two trees - >>> t0 = repo.head.tree - >>> t1 = repo.head.parents[0].tree + >>> t0 = head.tree + >>> t1 = head.parents[0].tree >>> diff = t1.diff(t0) - >>> diff # Diff a tree with the index - >>> tree = repo.head.tree - >>> diff = tree.diff(repo.index) + >>> diff = head.tree.diff(repo.index) # Diff a tree with the current working dir - >>> tree = repo.head.tree - >>> diff = tree.diff() + >>> diff = head.tree.diff() The Diff type diff --git a/docs/objects.rst b/docs/objects.rst index c49a0fbfb..aa7534c78 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -10,9 +10,6 @@ In the first place Git is a key-value storage system. The values stored are called *objects*, there are four types (commits, trees, blobs and tags), for each type pygit2 has a Python class:: - >>> # Get the last commit - >>> head = repo.head - >>> # Show commits and trees >>> commit diff --git a/docs/references.rst b/docs/references.rst index 0725c9e25..8cab025ba 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -17,12 +17,11 @@ Reference log:: >>> for entry in head.log(): ... print(entry.message) -The interface for RefLogEntry:: +Shortcuts:: - RefLogEntry.committer -- Signature of Committer - RefLogEntry.message -- the message of the RefLogEntry - RefLogEntry.oid_old -- oid of old reference - RefLogEntry.oid_new -- oid of new reference + >>> # These two lines are equivalent + >>> head = repo.head + >>> head = repo.lookup_reference('HEAD').resolve() The Reference type @@ -40,6 +39,14 @@ The Reference type .. automethod:: pygit2.Reference.log +The HEAD +-------------------- + +.. autoattribute:: pygit2.Repository.head +.. autoattribute:: pygit2.Repository.head_is_detached +.. autoattribute:: pygit2.Repository.head_is_orphaned + + The reference log -------------------- @@ -48,7 +55,6 @@ The reference log .. autoattribute:: pygit2.RefLogEntry.message .. autoattribute:: pygit2.RefLogEntry.committer - Notes ==================== diff --git a/docs/repository.rst b/docs/repository.rst index e13160b92..08ab3dd6e 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -41,6 +41,3 @@ To open an existing repository:: .. autoattribute:: pygit2.Repository.is_empty .. automethod:: pygit2.Repository.read .. automethod:: pygit2.Repository.write -.. autoattribute:: pygit2.Repository.head -.. autoattribute:: pygit2.Repository.head_is_detached -.. autoattribute:: pygit2.Repository.head_is_orphaned diff --git a/src/repository.c b/src/repository.c index 02748cd31..4df5047b6 100644 --- a/src/repository.c +++ b/src/repository.c @@ -183,10 +183,7 @@ Repository_head__get__(Repository *self) return NULL; } - oid = git_reference_target(head); - pyobj = lookup_object(self, oid, GIT_OBJ_COMMIT); - git_reference_free(head); - return pyobj; + return wrap_reference(head); } diff --git a/test/test_repository.py b/test/test_repository.py index 0e7503daf..8805559f7 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -27,18 +27,21 @@ """Tests for Repository objects.""" +# Import from the future from __future__ import absolute_import from __future__ import unicode_literals + +# Import from the Standard Library import binascii import unittest import tempfile import os from os.path import join, realpath +# Import from pygit2 from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT -from pygit2 import init_repository, discover_repository, Commit, hashfile +from pygit2 import init_repository, discover_repository, Reference, hashfile import pygit2 - from . import utils @@ -59,7 +62,7 @@ def test_is_bare(self): def test_head(self): head = self.repo.head self.assertEqual(HEAD_SHA, head.hex) - self.assertEqual(type(head), Commit) + self.assertEqual(type(head), Reference) self.assertFalse(self.repo.head_is_orphaned) self.assertFalse(self.repo.head_is_detached) @@ -191,10 +194,15 @@ def test_checkout_ref(self): lambda: self.repo.checkout(reference=ref_i18n)) # checkout i18n with GIT_CHECKOUT_FORCE - self.assertTrue('new' not in self.repo.head.tree) + head = self.repo.head + head = self.repo[head.target] + self.assertTrue('new' not in head.tree) self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE, ref_i18n) - self.assertEqual(self.repo.head.hex, self.repo[ref_i18n.target].hex) - self.assertTrue('new' in self.repo.head.tree) + + head = self.repo.head + head = self.repo[head.target] + self.assertEqual(head.hex, self.repo[ref_i18n.target].hex) + self.assertTrue('new' in head.tree) self.assertTrue('bye.txt' not in self.repo.status()) def test_checkout_index(self): From f74a211f1428488b3b3632d2e722c07b00b8551e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 18 Apr 2013 20:32:30 +0200 Subject: [PATCH 0453/2237] oid: now only accept Oid or hex, not raw Every method that takes an oid has changed what it accepts. Before it was (in both Python 2 and 3): - An Oid object - An hex oid, represented as a unicode string - A raw oid, represented as a bytes string Now the behaviour is different between Python 2 and 3. Now in Python 2 we take: - An Oid object - An hex oid, represented as a bytes or unicode string Now in Python 3 we take: - An Oid object - An hex oid, represented as a unicode string We have dropt direct support for raw strings. To use a raw string first build an Oid object: oid = Oid(raw=raw) We have also dropt support for short raw oids. The Oid constructor takes full oids, if passed short oids the behavior is undefined. --- src/oid.c | 85 +++++++++++++++++++++++++---------------- test/test_oid.py | 11 ++++++ test/test_repository.py | 30 +++++++-------- 3 files changed, 79 insertions(+), 47 deletions(-) diff --git a/src/oid.c b/src/oid.c index b40e157da..f1a6f522f 100644 --- a/src/oid.c +++ b/src/oid.c @@ -45,63 +45,71 @@ git_oid_to_python(const git_oid *oid) return (PyObject*)py_oid; } - int -py_str_to_git_oid(PyObject *py_str, git_oid *oid) +_oid_from_hex(PyObject *py_oid, git_oid *oid) { PyObject *py_hex; - char *hex_or_bin; int err; + char *hex; Py_ssize_t len; - /* Case 1: Git Oid */ - if (PyObject_TypeCheck(py_str, (PyTypeObject*)&OidType)) { - git_oid_cpy(oid, &((Oid*)py_str)->oid); - return GIT_OID_RAWSZ; - } - - /* Case 2: raw sha (bytes) */ - if (PyBytes_Check(py_str)) { - err = PyBytes_AsStringAndSize(py_str, &hex_or_bin, &len); +#if PY_MAJOR_VERSION == 2 + /* Bytes (only supported in Python 2) */ + if (PyBytes_Check(py_oid)) { + err = PyBytes_AsStringAndSize(py_oid, &hex, &len); if (err) return -1; - if (len > GIT_OID_RAWSZ) { - PyErr_SetObject(PyExc_ValueError, py_str); + + err = git_oid_fromstrn(oid, hex, len); + if (err < 0) { + PyErr_SetObject(Error_type(err), py_oid); return -1; } - memcpy(oid->id, (const unsigned char*)hex_or_bin, len); - return len * 2; + + return len; } +#endif - /* Case 3: hex sha (unicode) */ - if (PyUnicode_Check(py_str)) { - py_hex = PyUnicode_AsASCIIString(py_str); + /* Unicode */ + if (PyUnicode_Check(py_oid)) { + py_hex = PyUnicode_AsASCIIString(py_oid); if (py_hex == NULL) return -1; - err = PyBytes_AsStringAndSize(py_hex, &hex_or_bin, &len); + + err = PyBytes_AsStringAndSize(py_hex, &hex, &len); if (err) { Py_DECREF(py_hex); return -1; } - err = git_oid_fromstrn(oid, hex_or_bin, len); - + err = git_oid_fromstrn(oid, hex, len); Py_DECREF(py_hex); - if (err < 0) { - PyErr_SetObject(Error_type(err), py_str); + PyErr_SetObject(Error_type(err), py_oid); return -1; } + return len; } /* Type error */ - PyErr_Format(PyExc_TypeError, - "Git object id must be byte or a text string, not: %.200s", - Py_TYPE(py_str)->tp_name); + PyErr_SetObject(PyExc_TypeError, py_oid); return -1; } +int +py_str_to_git_oid(PyObject *py_oid, git_oid *oid) +{ + /* Oid */ + if (PyObject_TypeCheck(py_oid, (PyTypeObject*)&OidType)) { + git_oid_cpy(oid, &((Oid*)py_oid)->oid); + return GIT_OID_RAWSZ; + } + + /* Hex */ + return _oid_from_hex(py_oid, oid); +} + int py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, git_oid *oid) { @@ -152,6 +160,8 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw) char *keywords[] = {"raw", "hex", NULL}; PyObject *raw = NULL, *hex = NULL; int err; + char *bytes; + Py_ssize_t len; if (!PyArg_ParseTupleAndKeywords(args, kw, "|OO", keywords, &raw, &hex)) return -1; @@ -166,12 +176,23 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw) return -1; } - /* Get the oid. */ - if (raw != NULL) - err = py_str_to_git_oid(raw, &self->oid); - else - err = py_str_to_git_oid(hex, &self->oid); + /* Case 1: raw */ + if (raw != NULL) { + err = PyBytes_AsStringAndSize(raw, &bytes, &len); + if (err) + return -1; + + if (len > GIT_OID_RAWSZ) { + PyErr_SetObject(PyExc_ValueError, raw); + return -1; + } + + memcpy(self->oid.id, (const unsigned char*)bytes, len); + return 0; + } + /* Case 2: hex */ + err = _oid_from_hex(hex, &self->oid); if (err < 0) return -1; diff --git a/test/test_oid.py b/test/test_oid.py index 22dcb0b24..97c79791e 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -33,6 +33,7 @@ # Import from the Standard Library from binascii import unhexlify +from sys import version_info import unittest # Import from pygit2 @@ -55,6 +56,16 @@ def test_hex(self): self.assertEqual(oid.raw, RAW) self.assertEqual(oid.hex, HEX) + def test_hex_bytes(self): + if version_info.major == 2: + hex = bytes(HEX) + oid = Oid(hex=hex) + self.assertEqual(oid.raw, RAW) + self.assertEqual(oid.hex, HEX) + else: + hex = bytes(HEX, "ascii") + self.assertRaises(TypeError, Oid, hex=hex) + def test_none(self): self.assertRaises(ValueError, Oid) diff --git a/test/test_repository.py b/test/test_repository.py index 39fea4000..373730e7b 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -48,8 +48,9 @@ HEAD_SHA = '784855caf26449a1914d2cf62d12b9374d76ae78' PARENT_SHA = 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87' # HEAD^ -A_HEX_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' -A_BIN_SHA = binascii.unhexlify(A_HEX_SHA.encode('ascii')) +BLOB_HEX = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' +BLOB_RAW = binascii.unhexlify(BLOB_HEX.encode('ascii')) +BLOB_OID = Oid(raw=BLOB_RAW) class RepositoryTest(utils.BareRepoTestCase): @@ -71,15 +72,15 @@ def test_read(self): self.assertRaises(TypeError, self.repo.read, 123) self.assertRaisesWithArg(KeyError, '1' * 40, self.repo.read, '1' * 40) - ab = self.repo.read(A_BIN_SHA) - a = self.repo.read(A_HEX_SHA) + ab = self.repo.read(BLOB_OID) + a = self.repo.read(BLOB_HEX) self.assertEqual(ab, a) self.assertEqual((GIT_OBJ_BLOB, b'a contents\n'), a) a2 = self.repo.read('7f129fd57e31e935c6d60a0c794efe4e6927664b') self.assertEqual((GIT_OBJ_BLOB, b'a contents 2\n'), a2) - a_hex_prefix = A_HEX_SHA[:4] + a_hex_prefix = BLOB_HEX[:4] a3 = self.repo.read(a_hex_prefix) self.assertEqual((GIT_OBJ_BLOB, b'a contents\n'), a3) @@ -93,29 +94,28 @@ def test_write(self): def test_contains(self): self.assertRaises(TypeError, lambda: 123 in self.repo) - self.assertTrue(A_BIN_SHA in self.repo) - self.assertTrue(A_BIN_SHA[:10] in self.repo) - self.assertTrue(A_HEX_SHA in self.repo) - self.assertTrue(A_HEX_SHA[:10] in self.repo) + self.assertTrue(BLOB_OID in self.repo) + self.assertTrue(BLOB_HEX in self.repo) + self.assertTrue(BLOB_HEX[:10] in self.repo) self.assertFalse('a' * 40 in self.repo) self.assertFalse('a' * 20 in self.repo) def test_iterable(self): l = [ obj for obj in self.repo ] - self.assertTrue(A_HEX_SHA in l) + self.assertTrue(BLOB_HEX in l) def test_lookup_blob(self): self.assertRaises(TypeError, lambda: self.repo[123]) - self.assertEqual(self.repo[A_BIN_SHA].hex, A_HEX_SHA) - a = self.repo[A_HEX_SHA] + self.assertEqual(self.repo[BLOB_OID].hex, BLOB_HEX) + a = self.repo[BLOB_HEX] self.assertEqual(b'a contents\n', a.read_raw()) - self.assertEqual(A_HEX_SHA, a.hex) + self.assertEqual(BLOB_HEX, a.hex) self.assertEqual(GIT_OBJ_BLOB, a.type) def test_lookup_blob_prefix(self): - a = self.repo[A_HEX_SHA[:5]] + a = self.repo[BLOB_HEX[:5]] self.assertEqual(b'a contents\n', a.read_raw()) - self.assertEqual(A_HEX_SHA, a.hex) + self.assertEqual(BLOB_HEX, a.hex) self.assertEqual(GIT_OBJ_BLOB, a.type) def test_lookup_commit(self): From 4cf1fc2371f9eb9caf6db3fd93190391ae05ba16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 18 Apr 2013 23:57:19 +0200 Subject: [PATCH 0454/2237] Make Oid hashable --- src/oid.c | 10 +++++++++- src/utils.h | 5 +++++ test/test_oid.py | 10 ++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/oid.c b/src/oid.c index f1a6f522f..ae91d04c1 100644 --- a/src/oid.c +++ b/src/oid.c @@ -200,6 +200,14 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw) } +Py_hash_t +Oid_hash(PyObject *oid) +{ + /* TODO Randomize (use _Py_HashSecret) to avoid collission DoS attacks? */ + return *(Py_hash_t*) ((Oid*)oid)->oid.id; +} + + PyObject * Oid_richcompare(PyObject *o1, PyObject *o2, int op) { @@ -279,7 +287,7 @@ PyTypeObject OidType = { 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ - 0, /* tp_hash */ + (hashfunc)Oid_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ diff --git a/src/utils.h b/src/utils.h index 0dbe20bfd..2ce80746f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -54,6 +54,11 @@ #define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict") #endif +#ifndef Py_hash_t + #define Py_hash_t long +#endif + + #define CHECK_REFERENCE(self)\ if (self->reference == NULL) {\ PyErr_SetString(GitError, "deleted reference");\ diff --git a/test/test_oid.py b/test/test_oid.py index 97c79791e..5e6b22387 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -94,6 +94,16 @@ def test_cmp(self): self.assertFalse(oid1 > oid2) self.assertFalse(oid1 >= oid2) + def test_hash(self): + s = set() + s.add(Oid(raw=RAW)) + s.add(Oid(hex=HEX)) + self.assertEqual(len(s), 1) + + s.add(Oid(hex="0000000000000000000000000000000000000000")) + s.add(Oid(hex="0000000000000000000000000000000000000001")) + self.assertEqual(len(s), 3) + if __name__ == '__main__': unittest.main() From 94f44feb2275d41a25efef68728f5ccf16f68d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 18 Apr 2013 23:59:34 +0200 Subject: [PATCH 0455/2237] Fix unit tests with Python 2.6 --- test/test_oid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_oid.py b/test/test_oid.py index 5e6b22387..c303ce611 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -57,7 +57,7 @@ def test_hex(self): self.assertEqual(oid.hex, HEX) def test_hex_bytes(self): - if version_info.major == 2: + if version_info[0] == 2: hex = bytes(HEX) oid = Oid(hex=hex) self.assertEqual(oid.raw, RAW) From cf7146f0815143a8ff548c44f40da83c6df04a2b Mon Sep 17 00:00:00 2001 From: Jun Omae Date: Fri, 19 Apr 2013 16:58:40 +0900 Subject: [PATCH 0456/2237] Fixed crashes in `Repository.create_remote` caused by missing `Py_INCREF()` --- src/repository.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/repository.c b/src/repository.c index 02748cd31..db6268000 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1028,6 +1028,7 @@ Repository_create_remote(Repository *self, PyObject *args) return Error_set(err); py_remote = PyObject_New(Remote, &RemoteType); + Py_INCREF(self); py_remote->repo = self; py_remote->remote = remote; From aa3f0cf99d67a763ccf1d1508e1777c0171bbe07 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 19 Apr 2013 14:54:29 +0200 Subject: [PATCH 0457/2237] refactoring Repository_git_reference_symbolic_create --- src/repository.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/repository.c b/src/repository.c index 4df5047b6..5b39d6ecb 100644 --- a/src/repository.c +++ b/src/repository.c @@ -854,30 +854,15 @@ PyObject * Repository_git_reference_symbolic_create(Repository *self, PyObject *args, PyObject *kw) { - PyObject *py_obj; git_reference *c_reference; char *c_name, *c_target; int err, force; - if (!PyArg_ParseTuple(args, "sOi", &c_name, &py_obj, &force)) - return NULL; - - #if PY_MAJOR_VERSION == 2 - c_target = PyBytes_AsString(py_obj); - #else - // increases ref counter, so we have to release it afterwards - PyObject* py_str = PyUnicode_AsASCIIString(py_obj); - c_target = PyBytes_AsString(py_str); - #endif - if (c_target == NULL) + if (!PyArg_ParseTuple(args, "ssi", &c_name, &c_target, &force)) return NULL; err = git_reference_symbolic_create(&c_reference, self->repo, c_name, c_target, force); - #if PY_MAJOR_VERSION > 2 - Py_CLEAR(py_str); - #endif - if (err < 0) return Error_set(err); From 067b00de4f8ec6f7dda6406a0fdbf7a143fc219f Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 19 Apr 2013 15:08:48 +0200 Subject: [PATCH 0458/2237] added basic diff example and fixed misleading doc --- docs/diff.rst | 6 ++++++ src/diff.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/diff.rst b/docs/diff.rst index 513d08ed1..4d50ed81b 100644 --- a/docs/diff.rst +++ b/docs/diff.rst @@ -17,6 +17,12 @@ A diff shows the changes between trees, an index or the working dir:: # Diff a tree with the current working dir >>> diff = head.tree.diff() + # Get all patches for a diff + >>> t0 = repo.revparse_single('HEAD^').tree + >>> t1 = repo.revparse_single('HEAD~3').tree + >>> diff = t0.diff(t1) + >>> patches = [p for p in diff] + The Diff type ==================== diff --git a/src/diff.c b/src/diff.c index d7c2b6927..aa69ee2e0 100644 --- a/src/diff.c +++ b/src/diff.c @@ -227,7 +227,7 @@ PyTypeObject DiffIterType = { }; -PyDoc_STRVAR(Diff_patch__doc__, "Patch."); +PyDoc_STRVAR(Diff_patch__doc__, "Patch diff string."); PyObject * Diff_patch__get__(Diff *self) From c6a7cdd7f6ecedc88d80f99aa928bab1a6ac2c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 19 Apr 2013 17:55:22 +0200 Subject: [PATCH 0459/2237] Sort list of authors by number of commits As shown by "git shortlog -sn". So those who have done more are more visible. --- README.rst | 51 ++++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/README.rst b/README.rst index 87341613e..86790494d 100644 --- a/README.rst +++ b/README.rst @@ -45,47 +45,48 @@ for the topic), send a pull request. Authors ============== -The following people have contributed at least one patch to the -pygit2 project (sorted alphabetically): +This is the list of authors of pygit2, sorted by number of commits (as shown by +``git shortlog -sn``): +- J David Ibáñez +- Nico von Geyso +- W Trevor King +- Dave Borowitz +- Carlos Martín Nieto +- Christian Boos +- Julien Miotte +- Richo Healey +- Martin Lenders +- Xavier Delannoy +- Yonggang Luo +- Valentin Haenel +- John Szakmeister +- David Versmisse +- Petr Hosek +- Sebastian Thiel +- Han-Wen Nienhuys +- Petr Viktorin - Alex Chamberlain - Amit Bakshi -- András Veres-Szentkirályi - Ben Davis +- Jared Flatow +- Sarath Lakshman +- Vicent Marti +- Zoran Zaric +- András Veres-Szentkirályi - Benjamin Kircher - Benjamin Pollack - Bryan O'Sullivan -- Carlos Martín Nieto -- Christian Boos -- David Borowitz (*Original author*) - David Sanders -- David Versmisse - Eric Davis - Eric Schrijver - Erik van Zijst - Ferengee -- Han-Wen Nienhuys - Hugh Cole-Baker -- J David Ibáñez (*Current maintainer*) -- Jared Flatow -- John Szakmeister - Josh Bleecher Snyder -- Julien Miotte -- Martin Lenders -- Nico von Geyso -- Petr Hosek -- Petr Viktorin -- Richo Healey +- Jun Omae - Ridge Kennedy - Rui Abreu Ferreira -- Sarath Lakshman -- Sebastian Thiel -- Valentin Haenel -- Vicent Marti -- W Trevor King -- Xavier Delannoy -- Yonggang Luo -- Zoran Zaric - pistacchio From 37f0fed249194044d4a00d398bfc8bb32cd8a880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 20 Apr 2013 00:03:01 +0200 Subject: [PATCH 0460/2237] docs: add a section about the Oid type --- docs/objects.rst | 62 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index aa7534c78..1b6177591 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -5,10 +5,66 @@ Git objects .. contents:: Contents :local: +In the first place Git is a key-value storage system. The keys are called +OIDs, for Object id, and the values stored are called Objects. -In the first place Git is a key-value storage system. The values stored are -called *objects*, there are four types (commits, trees, blobs and tags), -for each type pygit2 has a Python class:: +Oids +================= + +The oid is the `SHA-1 `_ hash of an +object. It is 20 bytes long: + +- When we represent an oid as a 20 bytes Python string, we say it is a raw + oid. + +- When we represent an oid as a 40 chars Python string, we sayt it is a hex + oid. + +However, most of the time we will use the Oid type. We can explicetly create +an Oid object from its raw or hexadecimal form:: + + >>> hex = "cff3ceaefc955f0dbe1957017db181bc49913781" + >>> oid1 = Oid(hex=hex) + + >>> from binascii import unhexlify + >>> raw = unhexlify(hex) + >>> oid2 = Oid(raw=raw) + + >>> print oid1 == oid2 + True + +And in the opposite direction, we can get the raw or hexadecimal form from +an Oid object: + +.. autoattribute:: pygit2.Oid.raw +.. autoattribute:: pygit2.Oid.hex + +The Oid type supports: + +- rich comparisons, not just for equality, also: lesser-than, lesser-or-equal, + etc. + +- hashing, so Oid objects can be used as keys in a dictionary + + +Python 2 and Python 3 +--------------------- + +There is a difference on how the library handles hex oids, depending on +whether we are using Python 2 or 3. + +- In Python 2, we can represent an hexadecimal oid using a bytes string + (``str``) or a text string (``unicode``) + +- In Python 3, hexadecimal oids can only be represented using unicode + strings. + + +Objects +================= + +There are four types (commits, trees, blobs and tags), for each type pygit2 +has a Python class:: >>> # Show commits and trees >>> commit From 7261f4e1e7f5c0aa895b4a99c3ce10c2b963bc7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 20 Apr 2013 08:48:03 +0200 Subject: [PATCH 0461/2237] Now Note.oid returns an Oid object, not an hex --- src/note.c | 2 +- test/test_note.py | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/note.c b/src/note.c index 73dc90a83..c7ef0f2ca 100644 --- a/src/note.c +++ b/src/note.c @@ -72,7 +72,7 @@ PyDoc_STRVAR(Note_oid__doc__, PyObject * Note_oid__get__(Note *self) { - return git_oid_to_py_str(git_note_oid(self->note)); + return git_oid_to_python(git_note_oid(self->note)); } diff --git a/test/test_note.py b/test/test_note.py index a493540cb..37c00fde8 100644 --- a/test/test_note.py +++ b/test/test_note.py @@ -37,18 +37,19 @@ NOTE = ('6c8980ba963cad8b25a9bcaf68d4023ee57370d8', 'note message') NOTES = [ - ('ab533997b80705767be3dae8cbb06a0740809f79', 'First Note - HEAD\n', - '784855caf26449a1914d2cf62d12b9374d76ae78'), - ('d879714d880671ed84f8aaed8b27fca23ba01f27', 'Second Note - HEAD~1\n', - 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87') -] + ('ab533997b80705767be3dae8cbb06a0740809f79', 'First Note - HEAD\n', + '784855caf26449a1914d2cf62d12b9374d76ae78'), + ('d879714d880671ed84f8aaed8b27fca23ba01f27', 'Second Note - HEAD~1\n', + 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87') + ] class NotesTest(utils.BareRepoTestCase): - + def test_create_note(self): annotated_id = self.repo.revparse_single('HEAD~3').hex author = committer = Signature('Foo bar', 'foo@bar.com', 12346, 0) - note_id = self.repo.create_note(NOTE[1], author, committer, annotated_id) + note_id = self.repo.create_note(NOTE[1], author, committer, + annotated_id) self.assertEqual(NOTE[0], note_id.hex) # check the note blob @@ -57,22 +58,23 @@ def test_create_note(self): def test_lookup_note(self): annotated_id = self.repo.head.hex note = self.repo.lookup_note(annotated_id) - self.assertEqual(NOTES[0][0], note.oid) + self.assertEqual(NOTES[0][0], note.oid.hex) self.assertEqual(NOTES[0][1], note.message) def test_remove_note(self): note = self.repo.lookup_note(self.repo.head.hex) author = committer = Signature('Foo bar', 'foo@bar.com', 12346, 0) note.remove(author, committer) - self.assertRaises(KeyError, lambda: self.repo.lookup_note(self.repo.head.hex)) + self.assertRaises(KeyError, self.repo.lookup_note, self.repo.head.hex) def test_iterate_notes(self): for i, note in enumerate(self.repo.notes()): - entry = (note.oid, note.message, note.annotated_id) - self.assertEqual(NOTES[i],entry) + entry = (note.oid.hex, note.message, note.annotated_id) + self.assertEqual(NOTES[i], entry) def test_iterate_non_existing_ref(self): - self.assertRaises(KeyError, lambda: self.repo.notes("refs/notes/bad_ref")) + self.assertRaises(KeyError, self.repo.notes, "refs/notes/bad_ref") + if __name__ == '__main__': unittest.main() From a3a928a513f681613a4e470cf33921dc645b6919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 20 Apr 2013 09:03:13 +0200 Subject: [PATCH 0462/2237] Now Reference.target returns an Oid, not hex If the reference is direct, of course. --- src/reference.c | 39 +++++++++++++++++---------------------- test/test_refs.py | 2 +- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/reference.c b/src/reference.c index 0de113478..880fc06a1 100644 --- a/src/reference.c +++ b/src/reference.c @@ -204,18 +204,17 @@ Reference_target__get__(Reference *self) CHECK_REFERENCE(self); - /* Get the target */ - if (GIT_REF_OID == git_reference_type(self->reference)) { - return git_oid_to_py_str(git_reference_target(self->reference)); - } else { - c_name = git_reference_symbolic_target(self->reference); - if (c_name == NULL) { - PyErr_SetString(PyExc_ValueError, "no target available"); - return NULL; - } + /* Case 1: Direct */ + if (GIT_REF_OID == git_reference_type(self->reference)) + return git_oid_to_python(git_reference_target(self->reference)); + + /* Case 2: Symbolic */ + c_name = git_reference_symbolic_target(self->reference); + if (c_name == NULL) { + PyErr_SetString(PyExc_ValueError, "no target available"); + return NULL; } - /* Make a PyString and return it */ return to_path(c_name); } @@ -262,21 +261,17 @@ PyDoc_STRVAR(Reference_oid__doc__, "Object id."); PyObject * Reference_oid__get__(Reference *self) { - const git_oid *oid; - CHECK_REFERENCE(self); - /* Get the oid (only for "direct" references) */ - oid = git_reference_target(self->reference); - if (oid == NULL) { - PyErr_SetString(PyExc_ValueError, - "oid is only available if the reference is direct " - "(i.e. not symbolic)"); - return NULL; - } + /* Case 1: Direct */ + if (GIT_REF_OID == git_reference_type(self->reference)) + return git_oid_to_python(git_reference_target(self->reference)); - /* Convert and return it */ - return git_oid_to_python(oid); + /* Get the oid (only for "direct" references) */ + PyErr_SetString(PyExc_ValueError, + "oid is only available if the reference is direct " + "(i.e. not symbolic)"); + return NULL; } int diff --git a/test/test_refs.py b/test/test_refs.py index fb001822d..6ef1b9954 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -176,7 +176,7 @@ def test_create_reference(self): self.assertTrue('refs/tags/version1' in refs) reference = self.repo.lookup_reference('refs/tags/version1') self.assertEqual(reference.hex, LAST_COMMIT) - self.assertEqual(reference.target, LAST_COMMIT) + self.assertEqual(reference.target.hex, LAST_COMMIT) # try to create existing reference self.assertRaises(ValueError, self.repo.create_reference, From fd6768550008745309c39ead6a3c2a41b5f58adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 20 Apr 2013 09:38:12 +0200 Subject: [PATCH 0463/2237] The repository iterator now returns Oids, not hexs --- src/repository.c | 11 +++++------ test/test_repository.py | 3 ++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/repository.c b/src/repository.c index 74c7a50eb..10a000778 100644 --- a/src/repository.c +++ b/src/repository.c @@ -132,10 +132,10 @@ static int Repository_build_as_iter(const git_oid *oid, void *accum) { int err; - PyObject *oid_str = git_oid_to_py_str(oid); + PyObject *py_oid = git_oid_to_python(oid); - err = PyList_Append((PyObject*)accum, oid_str); - Py_DECREF(oid_str); + err = PyList_Append((PyObject*)accum, py_oid); + Py_DECREF(py_oid); return err; } @@ -152,11 +152,10 @@ Repository_as_iter(Repository *self) err = git_odb_foreach(odb, Repository_build_as_iter, (void*)accum); git_odb_free(odb); - if (err == GIT_EUSER) { + if (err == GIT_EUSER) return NULL; - } else if (err < 0) { + if (err < 0) return Error_set(err); - } return PyObject_GetIter(accum); } diff --git a/test/test_repository.py b/test/test_repository.py index 373730e7b..5bc5f6107 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -102,7 +102,8 @@ def test_contains(self): def test_iterable(self): l = [ obj for obj in self.repo ] - self.assertTrue(BLOB_HEX in l) + oid = Oid(hex=BLOB_HEX) + self.assertTrue(oid in l) def test_lookup_blob(self): self.assertRaises(TypeError, lambda: self.repo[123]) From fbd93f82aab001cae101525fa1872044cc93436c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 20 Apr 2013 10:09:52 +0200 Subject: [PATCH 0464/2237] Python 2: Return a byte string for hex oids --- src/oid.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/oid.c b/src/oid.c index ae91d04c1..0e32355f0 100644 --- a/src/oid.c +++ b/src/oid.c @@ -150,7 +150,12 @@ git_oid_to_py_str(const git_oid *oid) char hex[GIT_OID_HEXSZ]; git_oid_fmt(hex, oid); + + #if PY_MAJOR_VERSION == 2 + return PyBytes_FromStringAndSize(hex, GIT_OID_HEXSZ); + #else return to_unicode_n(hex, GIT_OID_HEXSZ, "utf-8", "strict"); + #endif } From e86fee5c7ebdccdce759e69d71feeb58a91196b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 20 Apr 2013 22:27:09 +0200 Subject: [PATCH 0465/2237] Release 0.18.1 Changes: - (#203) Now Repository.head returns the resolved reference, not the commit - (#210) Fix refcount error in Repository.create_remote - (#212) Improved diff documentation Thanks to Jun Omae and Nico von Geyso. --- docs/conf.py | 2 +- pygit2/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 4f3be7fac..d854107b4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,7 +50,7 @@ # The short X.Y version. version = '0.18' # The full version, including alpha/beta/rc tags. -release = '0.18.0' +release = '0.18.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/pygit2/version.py b/pygit2/version.py index c5bbeaf95..ffe9169ea 100644 --- a/pygit2/version.py +++ b/pygit2/version.py @@ -23,4 +23,4 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -__version__ = '0.18.0' +__version__ = '0.18.1' From 979cda9a9a26b18a51d4f7c68b110a72e83c8956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 21 Apr 2013 10:46:04 +0200 Subject: [PATCH 0466/2237] refs: improve API (#213) Changes: - Reference.oid and Reference.hex removed - Now Reference.target can be assigned an oid --- docs/references.rst | 2 - src/reference.c | 109 ++++++++++------------------------------ test/test_diff.py | 7 +-- test/test_note.py | 7 +-- test/test_refs.py | 21 ++++---- test/test_repository.py | 2 +- 6 files changed, 45 insertions(+), 103 deletions(-) diff --git a/docs/references.rst b/docs/references.rst index 8cab025ba..2ea5731fe 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -28,8 +28,6 @@ The Reference type ==================== .. autoattribute:: pygit2.Reference.name -.. autoattribute:: pygit2.Reference.oid -.. autoattribute:: pygit2.Reference.hex .. autoattribute:: pygit2.Reference.target .. autoattribute:: pygit2.Reference.type diff --git a/src/reference.c b/src/reference.c index 880fc06a1..25b40cfcc 100644 --- a/src/reference.c +++ b/src/reference.c @@ -214,35 +214,53 @@ Reference_target__get__(Reference *self) PyErr_SetString(PyExc_ValueError, "no target available"); return NULL; } - return to_path(c_name); } int -Reference_target__set__(Reference *self, PyObject *py_name) +Reference_target__set__(Reference *self, PyObject *py_target) { + git_oid oid; char *c_name; int err; git_reference *new_ref; + git_repository *repo; CHECK_REFERENCE_INT(self); - /* Get the C name */ - c_name = py_path_to_c_str(py_name); + /* Case 1: Direct */ + if (GIT_REF_OID == git_reference_type(self->reference)) { + repo = git_reference_owner(self->reference); + err = py_str_to_git_oid_expand(repo, py_target, &oid); + if (err < 0) + goto error; + + err = git_reference_set_target(&new_ref, self->reference, &oid); + if (err < 0) + goto error; + + git_reference_free(self->reference); + self->reference = new_ref; + return 0; + } + + /* Case 2: Symbolic */ + c_name = py_path_to_c_str(py_target); if (c_name == NULL) return -1; - /* Set the new target */ err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name); free(c_name); - if (err < 0) { - Error_set(err); - return -1; - } + if (err < 0) + goto error; git_reference_free(self->reference); self->reference = new_ref; return 0; + +error: + Error_set(err); + return -1; } @@ -256,77 +274,6 @@ Reference_name__get__(Reference *self) } -PyDoc_STRVAR(Reference_oid__doc__, "Object id."); - -PyObject * -Reference_oid__get__(Reference *self) -{ - CHECK_REFERENCE(self); - - /* Case 1: Direct */ - if (GIT_REF_OID == git_reference_type(self->reference)) - return git_oid_to_python(git_reference_target(self->reference)); - - /* Get the oid (only for "direct" references) */ - PyErr_SetString(PyExc_ValueError, - "oid is only available if the reference is direct " - "(i.e. not symbolic)"); - return NULL; -} - -int -Reference_oid__set__(Reference *self, PyObject *py_hex) -{ - git_oid oid; - int err; - git_reference *new_ref; - - CHECK_REFERENCE_INT(self); - - /* Get the oid */ - err = py_str_to_git_oid_expand(git_reference_owner(self->reference), - py_hex, &oid); - if (err < 0) { - Error_set(err); - return -1; - } - - /* Set the oid */ - err = git_reference_set_target(&new_ref, self->reference, &oid); - if (err < 0) { - Error_set(err); - return -1; - } - - git_reference_free(self->reference); - self->reference = new_ref; - return 0; -} - - -PyDoc_STRVAR(Reference_hex__doc__, "Hex oid."); - -PyObject * -Reference_hex__get__(Reference *self) -{ - const git_oid *oid; - - CHECK_REFERENCE(self); - - /* Get the oid (only for "direct" references) */ - oid = git_reference_target(self->reference); - if (oid == NULL) { - PyErr_SetString(PyExc_ValueError, - "oid is only available if the reference is direct " - "(i.e. not symbolic)"); - return NULL; - } - - /* Convert and return it */ - return git_oid_to_py_str(oid); -} - - PyDoc_STRVAR(Reference_type__doc__, "Type (GIT_REF_OID or GIT_REF_SYMBOLIC)."); @@ -460,8 +407,6 @@ PyMethodDef Reference_methods[] = { PyGetSetDef Reference_getseters[] = { GETTER(Reference, name), - GETSET(Reference, oid), - GETTER(Reference, hex), GETSET(Reference, target), GETTER(Reference, type), {NULL} diff --git a/test/test_diff.py b/test/test_diff.py index 54290fc34..71e15cda6 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -91,7 +91,7 @@ class DiffDirtyTest(utils.DirtyRepoTestCase): def test_diff_empty_index(self): repo = self.repo - head = repo[repo.lookup_reference('HEAD').resolve().oid] + head = repo[repo.lookup_reference('HEAD').resolve().target] diff = head.tree.diff(repo.index) files = [patch.new_file_path for patch in diff] @@ -99,12 +99,13 @@ def test_diff_empty_index(self): def test_workdir_to_tree(self): repo = self.repo - head = repo[repo.lookup_reference('HEAD').resolve().oid] + head = repo[repo.lookup_reference('HEAD').resolve().target] diff = head.tree.diff() files = [patch.new_file_path for patch in diff] self.assertEqual(DIFF_WORKDIR_EXPECTED, files) + class DiffTest(utils.BareRepoTestCase): def test_diff_invalid(self): @@ -114,7 +115,7 @@ def test_diff_invalid(self): def test_diff_empty_index(self): repo = self.repo - head = repo[repo.lookup_reference('HEAD').resolve().oid] + head = repo[repo.lookup_reference('HEAD').resolve().target] diff = head.tree.diff(repo.index) files = [patch.new_file_path.split('/')[0] for patch in diff] diff --git a/test/test_note.py b/test/test_note.py index 37c00fde8..d6ea11f16 100644 --- a/test/test_note.py +++ b/test/test_note.py @@ -56,16 +56,17 @@ def test_create_note(self): self.assertEqual(NOTE[1].encode(), self.repo[note_id].data) def test_lookup_note(self): - annotated_id = self.repo.head.hex + annotated_id = self.repo.head.target.hex note = self.repo.lookup_note(annotated_id) self.assertEqual(NOTES[0][0], note.oid.hex) self.assertEqual(NOTES[0][1], note.message) def test_remove_note(self): - note = self.repo.lookup_note(self.repo.head.hex) + head = self.repo.head + note = self.repo.lookup_note(head.target.hex) author = committer = Signature('Foo bar', 'foo@bar.com', 12346, 0) note.remove(author, committer) - self.assertRaises(KeyError, self.repo.lookup_note, self.repo.head.hex) + self.assertRaises(KeyError, self.repo.lookup_note, head.target.hex) def test_iterate_notes(self): for i, note in enumerate(self.repo.notes()): diff --git a/test/test_refs.py b/test/test_refs.py index 6ef1b9954..823c1ba0f 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -61,7 +61,7 @@ def test_list_all_references(self): def test_head(self): head = self.repo.head - self.assertEqual(LAST_COMMIT, self.repo[head.oid].hex) + self.assertEqual(LAST_COMMIT, self.repo[head.target].hex) def test_lookup_reference(self): repo = self.repo @@ -76,20 +76,20 @@ def test_lookup_reference(self): def test_reference_get_sha(self): reference = self.repo.lookup_reference('refs/heads/master') - self.assertEqual(reference.hex, LAST_COMMIT) + self.assertEqual(reference.target.hex, LAST_COMMIT) def test_reference_set_sha(self): NEW_COMMIT = '5ebeeebb320790caf276b9fc8b24546d63316533' reference = self.repo.lookup_reference('refs/heads/master') - reference.oid = NEW_COMMIT - self.assertEqual(reference.hex, NEW_COMMIT) + reference.target = NEW_COMMIT + self.assertEqual(reference.target.hex, NEW_COMMIT) def test_reference_set_sha_prefix(self): NEW_COMMIT = '5ebeeebb320790caf276b9fc8b24546d63316533' reference = self.repo.lookup_reference('refs/heads/master') - reference.oid = NEW_COMMIT[0:6] - self.assertEqual(reference.hex, NEW_COMMIT) + reference.target = NEW_COMMIT[0:6] + self.assertEqual(reference.target.hex, NEW_COMMIT) def test_reference_get_type(self): @@ -123,10 +123,8 @@ def test_delete(self): # Access the deleted reference self.assertRaises(GitError, getattr, reference, 'name') self.assertRaises(GitError, getattr, reference, 'type') - self.assertRaises(GitError, getattr, reference, 'oid') - self.assertRaises(GitError, setattr, reference, 'oid', LAST_COMMIT) - self.assertRaises(GitError, getattr, reference, 'hex') self.assertRaises(GitError, getattr, reference, 'target') + self.assertRaises(GitError, setattr, reference, 'target', LAST_COMMIT) self.assertRaises(GitError, setattr, reference, 'target', "a/b/c") self.assertRaises(GitError, reference.delete) self.assertRaises(GitError, reference.resolve) @@ -159,7 +157,7 @@ def test_reference_resolve(self): self.assertEqual(reference.type, GIT_REF_SYMBOLIC) reference = reference.resolve() self.assertEqual(reference.type, GIT_REF_OID) - self.assertEqual(reference.hex, LAST_COMMIT) + self.assertEqual(reference.target.hex, LAST_COMMIT) def test_reference_resolve_identity(self): @@ -175,7 +173,6 @@ def test_create_reference(self): refs = self.repo.listall_references() self.assertTrue('refs/tags/version1' in refs) reference = self.repo.lookup_reference('refs/tags/version1') - self.assertEqual(reference.hex, LAST_COMMIT) self.assertEqual(reference.target.hex, LAST_COMMIT) # try to create existing reference @@ -185,7 +182,7 @@ def test_create_reference(self): # try to create existing reference with force reference = self.repo.create_reference('refs/tags/version1', LAST_COMMIT, force=True) - self.assertEqual(reference.hex, LAST_COMMIT) + self.assertEqual(reference.target.hex, LAST_COMMIT) def test_create_symbolic_reference(self): diff --git a/test/test_repository.py b/test/test_repository.py index 5bc5f6107..f16888c8c 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -63,7 +63,7 @@ def test_is_bare(self): def test_head(self): head = self.repo.head - self.assertEqual(HEAD_SHA, head.hex) + self.assertEqual(HEAD_SHA, head.target.hex) self.assertEqual(type(head), Reference) self.assertFalse(self.repo.head_is_orphaned) self.assertFalse(self.repo.head_is_detached) From aafea91d6c709908261b2bc3dad05ce0af8bb037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 26 Apr 2013 14:24:59 +0200 Subject: [PATCH 0467/2237] Issue #214: Cannot call TreeEntry() / IndexEntry() --- src/pygit2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index 4393870ae..be1282b4d 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -203,7 +203,7 @@ moduleinit(PyObject* m) INIT_TYPE(CommitType, &ObjectType, NULL) INIT_TYPE(SignatureType, NULL, PyType_GenericNew) INIT_TYPE(TreeType, &ObjectType, NULL) - INIT_TYPE(TreeEntryType, NULL, PyType_GenericNew) + INIT_TYPE(TreeEntryType, NULL, NULL) INIT_TYPE(TreeIterType, NULL, NULL) INIT_TYPE(TreeBuilderType, NULL, PyType_GenericNew) INIT_TYPE(BlobType, &ObjectType, NULL) @@ -229,7 +229,7 @@ moduleinit(PyObject* m) /* Index */ INIT_TYPE(IndexType, NULL, PyType_GenericNew) - INIT_TYPE(IndexEntryType, NULL, PyType_GenericNew) + INIT_TYPE(IndexEntryType, NULL, NULL) INIT_TYPE(IndexIterType, NULL, NULL) ADD_TYPE(m, Index); ADD_TYPE(m, IndexEntry); From 7953ad3ec0b62cc960b428930f253fefc199203c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 26 Apr 2013 14:32:06 +0200 Subject: [PATCH 0468/2237] Fix a couple of compilation warnings --- src/repository.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/repository.c b/src/repository.c index 10a000778..da3f4ba6e 100644 --- a/src/repository.c +++ b/src/repository.c @@ -168,8 +168,6 @@ PyObject * Repository_head__get__(Repository *self) { git_reference *head; - const git_oid *oid; - PyObject *pyobj; int err; err = git_repository_head(&head, self->repo); From cce62db2c59807372e39a76ac8d4dbe698762730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 26 Apr 2013 14:32:48 +0200 Subject: [PATCH 0469/2237] travis: we are back to use libgit2's master branch --- .travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.sh b/.travis.sh index 1d73b003d..26985cf4c 100755 --- a/.travis.sh +++ b/.travis.sh @@ -2,7 +2,7 @@ cd ~ -git clone --depth=1 -b development https://github.com/libgit2/libgit2.git +git clone --depth=1 -b master https://github.com/libgit2/libgit2.git cd libgit2/ mkdir build && cd build From bbb78a9cec580a2d61decc52763851c27ffee376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 27 Apr 2013 11:06:46 +0200 Subject: [PATCH 0470/2237] Add GIT_OID_ constants - GIT_OID_RAWSZ - GIT_OID_HEXSZ - GIT_OID_HEX_ZERO - GIT_OID_MINPREFIXLEN --- src/pygit2.c | 287 +++++++++++++++++++++++---------------------------- src/utils.h | 4 + 2 files changed, 133 insertions(+), 158 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index be1282b4d..e4a7c64f3 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -185,6 +185,12 @@ moduleinit(PyObject* m) if (m == NULL) return NULL; + /* libgit2 version info */ + ADD_CONSTANT_INT(m, LIBGIT2_VER_MAJOR) + ADD_CONSTANT_INT(m, LIBGIT2_VER_MINOR) + ADD_CONSTANT_INT(m, LIBGIT2_VER_REVISION) + ADD_CONSTANT_STR(m, LIBGIT2_VERSION) + /* Errors */ GitError = PyErr_NewException("_pygit2.GitError", NULL, NULL); Py_INCREF(GitError); @@ -192,13 +198,19 @@ moduleinit(PyObject* m) /* Repository */ INIT_TYPE(RepositoryType, NULL, PyType_GenericNew) - ADD_TYPE(m, Repository); + ADD_TYPE(m, Repository) /* Oid */ INIT_TYPE(OidType, NULL, PyType_GenericNew) - ADD_TYPE(m, Oid); - - /* Objects (make them with the Repository.create_XXX methods). */ + ADD_TYPE(m, Oid) + ADD_CONSTANT_INT(m, GIT_OID_RAWSZ) + ADD_CONSTANT_INT(m, GIT_OID_HEXSZ) + ADD_CONSTANT_STR(m, GIT_OID_HEX_ZERO) + ADD_CONSTANT_INT(m, GIT_OID_MINPREFIXLEN) + + /* + * Objects + */ INIT_TYPE(ObjectType, NULL, NULL) INIT_TYPE(CommitType, &ObjectType, NULL) INIT_TYPE(SignatureType, NULL, PyType_GenericNew) @@ -208,182 +220,141 @@ moduleinit(PyObject* m) INIT_TYPE(TreeBuilderType, NULL, PyType_GenericNew) INIT_TYPE(BlobType, &ObjectType, NULL) INIT_TYPE(TagType, &ObjectType, NULL) - ADD_TYPE(m, Object); - ADD_TYPE(m, Commit); - ADD_TYPE(m, Signature); - ADD_TYPE(m, Tree); - ADD_TYPE(m, TreeEntry); - ADD_TYPE(m, TreeBuilder); - ADD_TYPE(m, Blob); - ADD_TYPE(m, Tag); - - /* References */ + ADD_TYPE(m, Object) + ADD_TYPE(m, Commit) + ADD_TYPE(m, Signature) + ADD_TYPE(m, Tree) + ADD_TYPE(m, TreeEntry) + ADD_TYPE(m, TreeBuilder) + ADD_TYPE(m, Blob) + ADD_TYPE(m, Tag) + ADD_CONSTANT_INT(m, GIT_OBJ_ANY) + ADD_CONSTANT_INT(m, GIT_OBJ_COMMIT) + ADD_CONSTANT_INT(m, GIT_OBJ_TREE) + ADD_CONSTANT_INT(m, GIT_OBJ_BLOB) + ADD_CONSTANT_INT(m, GIT_OBJ_TAG) + /* Valid modes for index and tree entries. */ + ADD_CONSTANT_INT(m, GIT_FILEMODE_NEW) + ADD_CONSTANT_INT(m, GIT_FILEMODE_TREE) + ADD_CONSTANT_INT(m, GIT_FILEMODE_BLOB) + ADD_CONSTANT_INT(m, GIT_FILEMODE_BLOB_EXECUTABLE) + ADD_CONSTANT_INT(m, GIT_FILEMODE_LINK) + ADD_CONSTANT_INT(m, GIT_FILEMODE_COMMIT) + + /* + * Log + */ + INIT_TYPE(WalkerType, NULL, PyType_GenericNew) + ADD_CONSTANT_INT(m, GIT_SORT_NONE) + ADD_CONSTANT_INT(m, GIT_SORT_TOPOLOGICAL) + ADD_CONSTANT_INT(m, GIT_SORT_TIME) + ADD_CONSTANT_INT(m, GIT_SORT_REVERSE) + + /* + * References + */ INIT_TYPE(ReferenceType, NULL, PyType_GenericNew) INIT_TYPE(RefLogEntryType, NULL, NULL) INIT_TYPE(RefLogIterType, NULL, NULL) INIT_TYPE(NoteType, NULL, NULL) INIT_TYPE(NoteIterType, NULL, NULL) - ADD_TYPE(m, Reference); - ADD_TYPE(m, RefLogEntry); - ADD_TYPE(m, Note); - - /* Index */ + ADD_TYPE(m, Reference) + ADD_TYPE(m, RefLogEntry) + ADD_TYPE(m, Note) + ADD_CONSTANT_INT(m, GIT_REF_INVALID) + ADD_CONSTANT_INT(m, GIT_REF_OID) + ADD_CONSTANT_INT(m, GIT_REF_SYMBOLIC) + ADD_CONSTANT_INT(m, GIT_REF_LISTALL) + + /* + * Index & Working copy + */ INIT_TYPE(IndexType, NULL, PyType_GenericNew) INIT_TYPE(IndexEntryType, NULL, NULL) INIT_TYPE(IndexIterType, NULL, NULL) - ADD_TYPE(m, Index); - ADD_TYPE(m, IndexEntry); - - /* Diff */ + ADD_TYPE(m, Index) + ADD_TYPE(m, IndexEntry) + /* Status */ + ADD_CONSTANT_INT(m, GIT_STATUS_CURRENT) + ADD_CONSTANT_INT(m, GIT_STATUS_INDEX_NEW) + ADD_CONSTANT_INT(m, GIT_STATUS_INDEX_MODIFIED) + ADD_CONSTANT_INT(m, GIT_STATUS_INDEX_DELETED) + ADD_CONSTANT_INT(m, GIT_STATUS_WT_NEW) + ADD_CONSTANT_INT(m, GIT_STATUS_WT_MODIFIED) + ADD_CONSTANT_INT(m, GIT_STATUS_WT_DELETED) + ADD_CONSTANT_INT(m, GIT_STATUS_IGNORED) /* Flags for ignored files */ + /* Different checkout strategies */ + ADD_CONSTANT_INT(m, GIT_CHECKOUT_NONE) + ADD_CONSTANT_INT(m, GIT_CHECKOUT_SAFE) + ADD_CONSTANT_INT(m, GIT_CHECKOUT_SAFE_CREATE) + ADD_CONSTANT_INT(m, GIT_CHECKOUT_FORCE) + ADD_CONSTANT_INT(m, GIT_CHECKOUT_ALLOW_CONFLICTS) + ADD_CONSTANT_INT(m, GIT_CHECKOUT_REMOVE_UNTRACKED) + ADD_CONSTANT_INT(m, GIT_CHECKOUT_REMOVE_IGNORED) + ADD_CONSTANT_INT(m, GIT_CHECKOUT_UPDATE_ONLY) + ADD_CONSTANT_INT(m, GIT_CHECKOUT_DONT_UPDATE_INDEX) + ADD_CONSTANT_INT(m, GIT_CHECKOUT_NO_REFRESH) + ADD_CONSTANT_INT(m, GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) + + /* + * Diff + */ INIT_TYPE(DiffType, NULL, NULL) INIT_TYPE(DiffIterType, NULL, NULL) INIT_TYPE(PatchType, NULL, NULL) INIT_TYPE(HunkType, NULL, NULL) - ADD_TYPE(m, Diff); - ADD_TYPE(m, Patch); - ADD_TYPE(m, Hunk); - - /* Log */ - INIT_TYPE(WalkerType, NULL, PyType_GenericNew) - - /* Config */ - INIT_TYPE(ConfigType, NULL, PyType_GenericNew) - ADD_TYPE(m, Config); - - /* Remote */ - INIT_TYPE(RemoteType, NULL, NULL) - ADD_TYPE(m, Remote); - - /* Constants */ - 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); - PyModule_AddIntConstant(m, "GIT_OBJ_BLOB", GIT_OBJ_BLOB); - PyModule_AddIntConstant(m, "GIT_OBJ_TAG", GIT_OBJ_TAG); - PyModule_AddIntConstant(m, "GIT_SORT_NONE", GIT_SORT_NONE); - PyModule_AddIntConstant(m, "GIT_SORT_TOPOLOGICAL", GIT_SORT_TOPOLOGICAL); - PyModule_AddIntConstant(m, "GIT_SORT_TIME", GIT_SORT_TIME); - PyModule_AddIntConstant(m, "GIT_SORT_REVERSE", GIT_SORT_REVERSE); - PyModule_AddIntConstant(m, "GIT_REF_INVALID", GIT_REF_INVALID); - PyModule_AddIntConstant(m, "GIT_REF_OID", GIT_REF_OID); - PyModule_AddIntConstant(m, "GIT_REF_SYMBOLIC", GIT_REF_SYMBOLIC); - PyModule_AddIntConstant(m, "GIT_REF_LISTALL", GIT_REF_LISTALL); - - /* Git status flags */ - PyModule_AddIntConstant(m, "GIT_STATUS_CURRENT", GIT_STATUS_CURRENT); - PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_NEW", GIT_STATUS_INDEX_NEW); - PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_MODIFIED", - GIT_STATUS_INDEX_MODIFIED); - PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_DELETED" , - GIT_STATUS_INDEX_DELETED); - PyModule_AddIntConstant(m, "GIT_STATUS_WT_NEW", GIT_STATUS_WT_NEW); - PyModule_AddIntConstant(m, "GIT_STATUS_WT_MODIFIED" , - GIT_STATUS_WT_MODIFIED); - PyModule_AddIntConstant(m, "GIT_STATUS_WT_DELETED", GIT_STATUS_WT_DELETED); - - /* Flags for ignored files */ - PyModule_AddIntConstant(m, "GIT_STATUS_IGNORED", GIT_STATUS_IGNORED); - - /* Git diff flags */ - PyModule_AddIntConstant(m, "GIT_DIFF_NORMAL", GIT_DIFF_NORMAL); - PyModule_AddIntConstant(m, "GIT_DIFF_REVERSE", GIT_DIFF_REVERSE); - PyModule_AddIntConstant(m, "GIT_DIFF_FORCE_TEXT", GIT_DIFF_FORCE_TEXT); - PyModule_AddIntConstant(m, "GIT_DIFF_IGNORE_WHITESPACE", - GIT_DIFF_IGNORE_WHITESPACE); - PyModule_AddIntConstant(m, "GIT_DIFF_IGNORE_WHITESPACE_CHANGE", - GIT_DIFF_IGNORE_WHITESPACE_CHANGE); - PyModule_AddIntConstant(m, "GIT_DIFF_IGNORE_WHITESPACE_EOL", - GIT_DIFF_IGNORE_WHITESPACE_EOL); - PyModule_AddIntConstant(m, "GIT_DIFF_IGNORE_SUBMODULES", - GIT_DIFF_IGNORE_SUBMODULES); - PyModule_AddIntConstant(m, "GIT_DIFF_PATIENCE", GIT_DIFF_PATIENCE); - PyModule_AddIntConstant(m, "GIT_DIFF_INCLUDE_IGNORED", - GIT_DIFF_INCLUDE_IGNORED); - PyModule_AddIntConstant(m, "GIT_DIFF_INCLUDE_UNTRACKED", - GIT_DIFF_INCLUDE_UNTRACKED); - PyModule_AddIntConstant(m, "GIT_DIFF_INCLUDE_UNMODIFIED", - GIT_DIFF_INCLUDE_UNMODIFIED); - PyModule_AddIntConstant(m, "GIT_DIFF_RECURSE_UNTRACKED_DIRS", - GIT_DIFF_RECURSE_UNTRACKED_DIRS); - + ADD_TYPE(m, Diff) + ADD_TYPE(m, Patch) + ADD_TYPE(m, Hunk) + ADD_CONSTANT_INT(m, GIT_DIFF_NORMAL) + ADD_CONSTANT_INT(m, GIT_DIFF_REVERSE) + ADD_CONSTANT_INT(m, GIT_DIFF_FORCE_TEXT) + ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_WHITESPACE) + ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_WHITESPACE_CHANGE) + ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_WHITESPACE_EOL) + ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_SUBMODULES) + ADD_CONSTANT_INT(m, GIT_DIFF_PATIENCE) + ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_IGNORED) + ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNTRACKED) + ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNMODIFIED) + ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_UNTRACKED_DIRS) /* Flags for diff find similar */ /* --find-renames */ - PyModule_AddIntConstant(m, "GIT_DIFF_FIND_RENAMES", - GIT_DIFF_FIND_RENAMES); + ADD_CONSTANT_INT(m, GIT_DIFF_FIND_RENAMES) /* --break-rewrites=N */ - PyModule_AddIntConstant(m, "GIT_DIFF_FIND_RENAMES_FROM_REWRITES", - GIT_DIFF_FIND_RENAMES_FROM_REWRITES); + ADD_CONSTANT_INT(m, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) /* --find-copies */ - PyModule_AddIntConstant(m, "GIT_DIFF_FIND_COPIES", - GIT_DIFF_FIND_COPIES); + ADD_CONSTANT_INT(m, GIT_DIFF_FIND_COPIES) /* --find-copies-harder */ - PyModule_AddIntConstant(m, "GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED", - GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED); + ADD_CONSTANT_INT(m, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED) /* --break-rewrites=/M */ - PyModule_AddIntConstant(m, "GIT_DIFF_FIND_AND_BREAK_REWRITES", - GIT_DIFF_FIND_AND_BREAK_REWRITES); - + ADD_CONSTANT_INT(m, GIT_DIFF_FIND_AND_BREAK_REWRITES) /* Flags for diff deltas */ - PyModule_AddIntConstant(m, "GIT_DELTA_UNMODIFIED", GIT_DELTA_UNMODIFIED); - PyModule_AddIntConstant(m, "GIT_DELTA_ADDED", GIT_DELTA_ADDED); - PyModule_AddIntConstant(m, "GIT_DELTA_DELETED", GIT_DELTA_DELETED); - PyModule_AddIntConstant(m, "GIT_DELTA_MODIFIED", GIT_DELTA_MODIFIED); - PyModule_AddIntConstant(m, "GIT_DELTA_RENAMED", GIT_DELTA_RENAMED); - PyModule_AddIntConstant(m, "GIT_DELTA_COPIED", GIT_DELTA_COPIED); - PyModule_AddIntConstant(m, "GIT_DELTA_IGNORED", GIT_DELTA_IGNORED); - PyModule_AddIntConstant(m, "GIT_DELTA_UNTRACKED", GIT_DELTA_UNTRACKED); - + ADD_CONSTANT_INT(m, GIT_DELTA_UNMODIFIED) + ADD_CONSTANT_INT(m, GIT_DELTA_ADDED) + ADD_CONSTANT_INT(m, GIT_DELTA_DELETED) + ADD_CONSTANT_INT(m, GIT_DELTA_MODIFIED) + ADD_CONSTANT_INT(m, GIT_DELTA_RENAMED) + ADD_CONSTANT_INT(m, GIT_DELTA_COPIED) + ADD_CONSTANT_INT(m, GIT_DELTA_IGNORED) + ADD_CONSTANT_INT(m, GIT_DELTA_UNTRACKED) /* Flags for diffed lines origin */ - PyModule_AddIntConstant(m, "GIT_DIFF_LINE_CONTEXT", GIT_DIFF_LINE_CONTEXT); - PyModule_AddIntConstant(m, "GIT_DIFF_LINE_ADDITION", - GIT_DIFF_LINE_ADDITION); - PyModule_AddIntConstant(m, "GIT_DIFF_LINE_DELETION", - GIT_DIFF_LINE_DELETION); - PyModule_AddIntConstant(m, "GIT_DIFF_LINE_ADD_EOFNL", - GIT_DIFF_LINE_ADD_EOFNL); - PyModule_AddIntConstant(m, "GIT_DIFF_LINE_DEL_EOFNL", - GIT_DIFF_LINE_DEL_EOFNL); - PyModule_AddIntConstant(m, "GIT_DIFF_LINE_FILE_HDR", - GIT_DIFF_LINE_FILE_HDR); - PyModule_AddIntConstant(m, "GIT_DIFF_LINE_HUNK_HDR", - GIT_DIFF_LINE_HUNK_HDR); - PyModule_AddIntConstant(m, "GIT_DIFF_LINE_BINARY", GIT_DIFF_LINE_BINARY); - - /* Valid modes for index and tree entries. */ - PyModule_AddIntConstant(m, "GIT_FILEMODE_NEW", GIT_FILEMODE_NEW); - PyModule_AddIntConstant(m, "GIT_FILEMODE_TREE", GIT_FILEMODE_TREE); - PyModule_AddIntConstant(m, "GIT_FILEMODE_BLOB", GIT_FILEMODE_BLOB); - PyModule_AddIntConstant(m, "GIT_FILEMODE_BLOB_EXECUTABLE", - GIT_FILEMODE_BLOB_EXECUTABLE); - PyModule_AddIntConstant(m, "GIT_FILEMODE_LINK", GIT_FILEMODE_LINK); - PyModule_AddIntConstant(m, "GIT_FILEMODE_COMMIT", GIT_FILEMODE_COMMIT); + ADD_CONSTANT_INT(m, GIT_DIFF_LINE_CONTEXT) + ADD_CONSTANT_INT(m, GIT_DIFF_LINE_ADDITION) + ADD_CONSTANT_INT(m, GIT_DIFF_LINE_DELETION) + ADD_CONSTANT_INT(m, GIT_DIFF_LINE_ADD_EOFNL) + ADD_CONSTANT_INT(m, GIT_DIFF_LINE_DEL_EOFNL) + ADD_CONSTANT_INT(m, GIT_DIFF_LINE_FILE_HDR) + ADD_CONSTANT_INT(m, GIT_DIFF_LINE_HUNK_HDR) + ADD_CONSTANT_INT(m, GIT_DIFF_LINE_BINARY) - /* libgit2 version info */ - PyModule_AddIntConstant(m, "LIBGIT2_VER_MAJOR", LIBGIT2_VER_MAJOR); - PyModule_AddIntConstant(m, "LIBGIT2_VER_MINOR", LIBGIT2_VER_MINOR); - PyModule_AddIntConstant(m, "LIBGIT2_VER_REVISION", LIBGIT2_VER_REVISION); - PyModule_AddStringConstant(m, "LIBGIT2_VERSION", LIBGIT2_VERSION); + /* Config */ + INIT_TYPE(ConfigType, NULL, PyType_GenericNew) + ADD_TYPE(m, Config) - /* Different checkout strategies */ - PyModule_AddIntConstant(m, "GIT_CHECKOUT_NONE", GIT_CHECKOUT_NONE); - PyModule_AddIntConstant(m, "GIT_CHECKOUT_SAFE", GIT_CHECKOUT_SAFE); - PyModule_AddIntConstant(m, "GIT_CHECKOUT_SAFE_CREATE", - GIT_CHECKOUT_SAFE_CREATE); - PyModule_AddIntConstant(m, "GIT_CHECKOUT_FORCE", GIT_CHECKOUT_FORCE); - PyModule_AddIntConstant(m, "GIT_CHECKOUT_ALLOW_CONFLICTS", - GIT_CHECKOUT_ALLOW_CONFLICTS); - PyModule_AddIntConstant(m, "GIT_CHECKOUT_REMOVE_UNTRACKED", - GIT_CHECKOUT_REMOVE_UNTRACKED); - PyModule_AddIntConstant(m, "GIT_CHECKOUT_REMOVE_IGNORED", - GIT_CHECKOUT_REMOVE_IGNORED); - PyModule_AddIntConstant(m, "GIT_CHECKOUT_UPDATE_ONLY", - GIT_CHECKOUT_UPDATE_ONLY); - PyModule_AddIntConstant(m, "GIT_CHECKOUT_DONT_UPDATE_INDEX", - GIT_CHECKOUT_DONT_UPDATE_INDEX); - PyModule_AddIntConstant(m, "GIT_CHECKOUT_NO_REFRESH", - GIT_CHECKOUT_NO_REFRESH); - PyModule_AddIntConstant(m, "GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH", - GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH); + /* Remotes */ + INIT_TYPE(RemoteType, NULL, NULL) + ADD_TYPE(m, Remote) /* Global initialization of libgit2 */ git_threads_init(); diff --git a/src/utils.h b/src/utils.h index 2ce80746f..104b0e8e7 100644 --- a/src/utils.h +++ b/src/utils.h @@ -150,5 +150,9 @@ char * py_str_to_c_str(PyObject *value, const char *encoding); Py_INCREF(& type ## Type); \ PyModule_AddObject(module, #type, (PyObject *) & type ## Type); +#define ADD_CONSTANT_INT(m, name) PyModule_AddIntConstant(m, #name, name); + +#define ADD_CONSTANT_STR(m, name) PyModule_AddStringConstant(m, #name, name); + #endif From 67417c7f5636a89832561cb35de59b529bc8f047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 27 Apr 2013 11:39:37 +0200 Subject: [PATCH 0471/2237] create_reference, drop the symbolic param (#213) Now we figure out whether it is a direct or symbolic reference based on the type and value of the target. This guessing will fail in very very rare situations, for that we still have the explicit lower level API. --- pygit2/repository.py | 34 +++++++++++++++++++++------------- test/test_refs.py | 10 ++++------ 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index 4513d0919..0cb57e383 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -25,8 +25,12 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. +# Import from the Standard Library +from string import hexdigits + # Import from pygit2 from _pygit2 import Repository as _Repository +from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN class Repository(_Repository): @@ -53,30 +57,34 @@ def __contains__(self, key): # # References # - def create_reference(self, name, target, force=False, symbolic=False): + def create_reference(self, name, target, force=False): """ - Create a new reference "name" which points to a object or another + Create a new reference "name" which points to an object or to another reference. + Based on the type and value of the target parameter, this method tries + to guess whether it is a direct or a symbolic reference. + Keyword arguments: force If True references will be overridden, otherwise (the default) an exception is raised. - symbolic - If True a symbolic reference will be created, then source has to - be a valid existing reference name; if False (the default) a - normal reference will be created, then source must has to be a - valid SHA hash. - Examples:: repo.create_reference('refs/heads/foo', repo.head.hex) - repo.create_reference('refs/tags/foo', 'refs/heads/master', - symbolic=True) + repo.create_reference('refs/tags/foo', 'refs/heads/master') + repo.create_reference('refs/tags/foo', 'bbb78a9cec580') """ - if symbolic: - return self.git_reference_symbolic_create(name, target, force) + direct = ( + type(target) is Oid + or ( + all(c in hexdigits for c in target) + and GIT_OID_MINPREFIXLEN <= len(target) <= GIT_OID_HEXSZ) + ) + + if direct: + return self.git_reference_create(name, target, force) - return self.git_reference_create(name, target, force) + return self.git_reference_symbolic_create(name, target, force) diff --git a/test/test_refs.py b/test/test_refs.py index 823c1ba0f..3c2af0c21 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -49,8 +49,7 @@ def test_list_all_references(self): ['refs/heads/i18n', 'refs/heads/master']) # We add a symbolic reference - repo.create_reference('refs/tags/version1', 'refs/heads/master', - symbolic=True) + repo.create_reference('refs/tags/version1', 'refs/heads/master') self.assertEqual(sorted(repo.listall_references()), ['refs/heads/i18n', 'refs/heads/master', 'refs/tags/version1']) @@ -189,19 +188,18 @@ def test_create_symbolic_reference(self): # We add a tag as a new symbolic reference that always points to # "refs/heads/master" reference = self.repo.create_reference('refs/tags/beta', - 'refs/heads/master', symbolic=True) + 'refs/heads/master') self.assertEqual(reference.type, GIT_REF_SYMBOLIC) self.assertEqual(reference.target, 'refs/heads/master') # try to create existing symbolic reference self.assertRaises(ValueError, self.repo.create_reference, - 'refs/tags/beta', 'refs/heads/master', - symbolic=True) + 'refs/tags/beta', 'refs/heads/master') # try to create existing symbolic reference with force reference = self.repo.create_reference('refs/tags/beta', - 'refs/heads/master', force=True, symbolic=True) + 'refs/heads/master', force=True) self.assertEqual(reference.type, GIT_REF_SYMBOLIC) self.assertEqual(reference.target, 'refs/heads/master') From 31659fa673ac4921fca58aad66298b63a196bb73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 28 Apr 2013 11:16:31 +0200 Subject: [PATCH 0472/2237] docs: fix/improve references chapter a little bit --- docs/references.rst | 33 +++++++++++++++++---------------- src/reference.c | 9 ++++++--- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/docs/references.rst b/docs/references.rst index 2ea5731fe..943c36712 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -2,26 +2,16 @@ References ********************************************************************** +.. contents:: + .. automethod:: pygit2.Repository.listall_references .. automethod:: pygit2.Repository.lookup_reference -Reference lookup:: +Example:: >>> all_refs = repo.listall_references() >>> master_ref = repo.lookup_reference("refs/heads/master") - >>> commit = repo[master_ref.oid] - -Reference log:: - - >>> head = repo.lookup_reference('refs/heads/master') - >>> for entry in head.log(): - ... print(entry.message) - -Shortcuts:: - - >>> # These two lines are equivalent - >>> head = repo.head - >>> head = repo.lookup_reference('HEAD').resolve() + >>> commit = repo[master_ref.target] The Reference type @@ -38,7 +28,12 @@ The Reference type The HEAD --------------------- +==================== + +Example. These two lines are equivalent:: + + >>> head = repo.lookup_reference('HEAD').resolve() + >>> head = repo.head .. autoattribute:: pygit2.Repository.head .. autoattribute:: pygit2.Repository.head_is_detached @@ -46,7 +41,13 @@ The HEAD The reference log --------------------- +==================== + +Example:: + + >>> head = repo.lookup_reference('refs/heads/master') + >>> for entry in head.log(): + ... print(entry.message) .. autoattribute:: pygit2.RefLogEntry.oid_new .. autoattribute:: pygit2.RefLogEntry.oid_old diff --git a/src/reference.c b/src/reference.c index 25b40cfcc..f4478dbed 100644 --- a/src/reference.c +++ b/src/reference.c @@ -195,7 +195,10 @@ Reference_resolve(Reference *self, PyObject *args) } -PyDoc_STRVAR(Reference_target__doc__, "Target."); +PyDoc_STRVAR(Reference_target__doc__, + "The reference target: If direct the value will be an Oid object, if it\n" + "is symbolic it will be an string with the full name of the target\n" + "reference."); PyObject * Reference_target__get__(Reference *self) @@ -264,7 +267,7 @@ Reference_target__set__(Reference *self, PyObject *py_target) } -PyDoc_STRVAR(Reference_name__doc__, "The full name of a reference."); +PyDoc_STRVAR(Reference_name__doc__, "The full name of the reference."); PyObject * Reference_name__get__(Reference *self) @@ -275,7 +278,7 @@ Reference_name__get__(Reference *self) PyDoc_STRVAR(Reference_type__doc__, - "Type (GIT_REF_OID or GIT_REF_SYMBOLIC)."); + "Type, either GIT_REF_OID or GIT_REF_SYMBOLIC."); PyObject * Reference_type__get__(Reference *self) From 00a4a52a8f5ea9427c0e465522633e289de90113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 28 Apr 2013 11:25:33 +0200 Subject: [PATCH 0473/2237] Check errors on module initialization --- src/utils.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/utils.h b/src/utils.h index 104b0e8e7..d35f4677a 100644 --- a/src/utils.h +++ b/src/utils.h @@ -147,12 +147,15 @@ char * py_str_to_c_str(PyObject *value, const char *encoding); if (PyType_Ready(&type) < 0) return NULL; #define ADD_TYPE(module, type) \ - Py_INCREF(& type ## Type); \ - PyModule_AddObject(module, #type, (PyObject *) & type ## Type); + Py_INCREF(& type ## Type);\ + if (PyModule_AddObject(module, #type, (PyObject*) & type ## Type) == -1)\ + return NULL; -#define ADD_CONSTANT_INT(m, name) PyModule_AddIntConstant(m, #name, name); +#define ADD_CONSTANT_INT(m, name) \ + if (PyModule_AddIntConstant(m, #name, name) == -1) return NULL; -#define ADD_CONSTANT_STR(m, name) PyModule_AddStringConstant(m, #name, name); +#define ADD_CONSTANT_STR(m, name) \ + if (PyModule_AddStringConstant(m, #name, name) == -1) return NULL; #endif From 61c330f57dd4d0558bfbd3d4c9d39d224dc5add4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Cauwelier?= Date: Mon, 29 Apr 2013 23:24:02 +0200 Subject: [PATCH 0474/2237] split create_blob_fromfile into fromworkdir and fromdisk to match libgit2 Both create loose blobs but fromworkdir assert the file is inside the working directory. --- src/repository.c | 34 +++++++++++++++++++++++++++++----- test/test_blob.py | 19 +++++++++++++++++-- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/repository.c b/src/repository.c index 83d1b9869..6342b6f2d 100644 --- a/src/repository.c +++ b/src/repository.c @@ -578,13 +578,13 @@ Repository_create_blob(Repository *self, PyObject *args) } -PyDoc_STRVAR(Repository_create_blob_fromfile__doc__, - "create_blob_fromfile(path) -> bytes\n" +PyDoc_STRVAR(Repository_create_blob_fromworkdir__doc__, + "create_blob_fromworkdir(path) -> bytes\n" "\n" - "Create a new blob from file."); + "Create a new blob from a file within the working directory."); PyObject * -Repository_create_blob_fromfile(Repository *self, PyObject *args) +Repository_create_blob_fromworkdir(Repository *self, PyObject *args) { git_oid oid; const char* path; @@ -601,6 +601,29 @@ Repository_create_blob_fromfile(Repository *self, PyObject *args) } +PyDoc_STRVAR(Repository_create_blob_fromdisk__doc__, + "create_blob_fromdisk(path) -> bytes\n" + "\n" + "Create a new blob from file."); + +PyObject * +Repository_create_blob_fromdisk(Repository *self, PyObject *args) +{ + git_oid oid; + const char* path; + int err; + + if (!PyArg_ParseTuple(args, "s", &path)) + return NULL; + + err = git_blob_create_fromdisk(&oid, self->repo, path); + if (err < 0) + return Error_set(err); + + return git_oid_to_python(oid.id); +} + + PyDoc_STRVAR(Repository_create_commit__doc__, "create_commit(reference, author, committer, message, tree, parents[, encoding]) -> bytes\n" "\n" @@ -1181,7 +1204,8 @@ Repository_lookup_note(Repository *self, PyObject* args) PyMethodDef Repository_methods[] = { METHOD(Repository, create_blob, METH_VARARGS), - METHOD(Repository, create_blob_fromfile, METH_VARARGS), + METHOD(Repository, create_blob_fromworkdir, METH_VARARGS), + METHOD(Repository, create_blob_fromdisk, METH_VARARGS), METHOD(Repository, create_commit, METH_VARARGS), METHOD(Repository, create_tag, METH_VARARGS), METHOD(Repository, TreeBuilder, METH_VARARGS), diff --git a/test/test_blob.py b/test/test_blob.py index 221ddcc02..64f76b703 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -29,6 +29,7 @@ from __future__ import absolute_import from __future__ import unicode_literals +import os import unittest import pygit2 @@ -74,9 +75,9 @@ def test_create_blob(self): self.assertEqual(len(BLOB_NEW_CONTENT), blob.size) self.assertEqual(BLOB_NEW_CONTENT, blob.read_raw()) - def test_create_blob_fromfile(self): + def test_create_blob_fromworkdir(self): - blob_oid = self.repo.create_blob_fromfile("bye.txt") + blob_oid = self.repo.create_blob_fromworkdir("bye.txt") blob = self.repo[blob_oid] self.assertTrue(isinstance(blob, pygit2.Blob)) @@ -92,5 +93,19 @@ def test_create_blob_fromfile(self): self.assertEqual(len(BLOB_FILE_CONTENT), blob.size) self.assertEqual(BLOB_FILE_CONTENT, blob.read_raw()) + def test_create_blob_outside_workdir(self): + + path = os.path.join(os.path.dirname(__file__), 'data', self.repo_dir + '.tar') + self.assertRaises(KeyError, self.repo.create_blob_fromworkdir, path) + + def test_create_blob_fromdisk(self): + + path = os.path.join(os.path.dirname(__file__), 'data', self.repo_dir + '.tar') + blob_oid = self.repo.create_blob_fromdisk(path) + blob = self.repo[blob_oid] + + self.assertTrue(isinstance(blob, pygit2.Blob)) + self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) + if __name__ == '__main__': unittest.main() From b12a59606860ea2d88cee519ef5a6bb975ee6ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 2 May 2013 20:28:14 +0200 Subject: [PATCH 0475/2237] Remove "del index[xxx]" from the API Use "index.remove(xxx)" instead. --- src/index.c | 81 +++++++++++++--------------------------------- test/test_index.py | 7 ++-- 2 files changed, 25 insertions(+), 63 deletions(-) diff --git a/src/index.c b/src/index.c index 67defb74e..9176564f8 100644 --- a/src/index.c +++ b/src/index.c @@ -227,43 +227,6 @@ Index_write(Index *self) Py_RETURN_NONE; } -/* This is an internal function, used by Index_getitem and Index_setitem */ -size_t -Index_get_position(Index *self, PyObject *value) -{ - char *path; - size_t idx; - int err; - - /* Case 1: integer */ - if (PyLong_Check(value)) { - err = (int)PyLong_AsLong(value); - if (err == -1 && PyErr_Occurred()) - return -1; - if (err < 0) { - PyErr_SetObject(PyExc_ValueError, value); - return -1; - } - return err; - } - - /* Case 2: byte or text string */ - path = py_path_to_c_str(value); - if (!path) - return -1; - - err = git_index_find(&idx, self->index, path); - if (err < 0) { - Error_set_str(err, path); - free(path); - return -1; - } - - free(path); - - return idx; -} - int Index_contains(Index *self, PyObject *value) { @@ -322,19 +285,34 @@ wrap_index_entry(const git_index_entry *entry, Index *index) PyObject * Index_getitem(Index *self, PyObject *value) { - size_t idx; + long idx; + char *path; const git_index_entry *index_entry; - idx = Index_get_position(self, value); - if (idx == -1) - return NULL; + /* Case 1: integer */ + if (PyLong_Check(value)) { + idx = PyLong_AsLong(value); + if (idx == -1 && PyErr_Occurred()) + return NULL; + if (idx < 0) { + PyErr_SetObject(PyExc_ValueError, value); + return NULL; + } + index_entry = git_index_get_byindex(self->index, (size_t)idx); + /* Case 2: byte or text string */ + } else { + path = py_path_to_c_str(value); + if (!path) + return NULL; + + index_entry = git_index_get_bypath(self->index, path, 0); + free(path); + } - index_entry = git_index_get_byindex(self->index, idx); if (!index_entry) { PyErr_SetObject(PyExc_KeyError, value); return NULL; } - return wrap_index_entry(index_entry, self); } @@ -362,21 +340,6 @@ Index_remove(Index *self, PyObject *args) Py_RETURN_NONE; } -int -Index_setitem(Index *self, PyObject *key, PyObject *value) -{ - if (value != NULL) { - PyErr_SetString(PyExc_NotImplementedError, - "set item on index not yet implemented"); - return -1; - } - - if (Index_remove(self, Py_BuildValue("(N)", key)) == NULL) - return -1; - - return 0; -} - PyDoc_STRVAR(Index_read_tree__doc__, "read_tree(tree)\n" @@ -453,7 +416,7 @@ PySequenceMethods Index_as_sequence = { PyMappingMethods Index_as_mapping = { (lenfunc)Index_len, /* mp_length */ (binaryfunc)Index_getitem, /* mp_subscript */ - (objobjargproc)Index_setitem, /* mp_ass_subscript */ + NULL, /* mp_ass_subscript */ }; PyDoc_STRVAR(Index__doc__, "Index file."); diff --git a/test/test_index.py b/test/test_index.py index b58137345..39f41b8e8 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -133,13 +133,12 @@ def test_bare_index(self): self.assertRaises(pygit2.GitError, lambda: index.add('bye.txt')) - def test_del(self): - index = self.repo.index - del index['hello.txt'] - def test_remove(self): index = self.repo.index + self.assertTrue('hello.txt' in index) index.remove('hello.txt') + self.assertFalse('hello.txt' in index) + if __name__ == '__main__': unittest.main() From cc9f39125bc05afae20d42c852991a6b2cb4ec5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 2 May 2013 20:33:22 +0200 Subject: [PATCH 0476/2237] Fix Repository.create_blob_fromdisk --- src/repository.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repository.c b/src/repository.c index 5ff23116b..27f621b9b 100644 --- a/src/repository.c +++ b/src/repository.c @@ -617,7 +617,7 @@ Repository_create_blob_fromdisk(Repository *self, PyObject *args) if (err < 0) return Error_set(err); - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } From f5082b320b595bf4264e9a26d3dccded9fb83803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 3 May 2013 23:31:43 +0200 Subject: [PATCH 0477/2237] Add "len(treebuider)" to the API --- src/treebuilder.c | 28 +++++++++++++++++++--------- test/test_treebuilder.py | 10 +++++++++- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/treebuilder.c b/src/treebuilder.c index 30712aa64..4329a4d37 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -56,20 +56,16 @@ TreeBuilder_insert(TreeBuilder *self, PyObject *args) git_oid oid; const char *fname; - if (!PyArg_ParseTuple(args, "sOi", &fname, &py_oid, &attr)) { + if (!PyArg_ParseTuple(args, "sOi", &fname, &py_oid, &attr)) return NULL; - } len = py_str_to_git_oid(py_oid, &oid); - if (len < 0) { + if (len < 0) return NULL; - } err = git_treebuilder_insert(NULL, self->bld, fname, &oid, attr); - if (err < 0) { - Error_set(err); - return NULL; - } + if (err < 0) + return Error_set(err); Py_RETURN_NONE; } @@ -138,6 +134,20 @@ PyMethodDef TreeBuilder_methods[] = { }; +Py_ssize_t +TreeBuilder_len(TreeBuilder *self) +{ + return (Py_ssize_t)git_treebuilder_entrycount(self->bld); +} + + +PyMappingMethods TreeBuilder_as_mapping = { + (lenfunc)TreeBuilder_len, /* mp_length */ + 0, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + + PyDoc_STRVAR(TreeBuilder__doc__, "TreeBuilder objects."); PyTypeObject TreeBuilderType = { @@ -153,7 +163,7 @@ PyTypeObject TreeBuilderType = { 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + &TreeBuilder_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index 161d40072..2c30f7677 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -41,25 +41,33 @@ class TreeBuilderTest(utils.BareRepoTestCase): def test_new_empty_treebuilder(self): self.repo.TreeBuilder() + def test_noop_treebuilder(self): tree = self.repo[TREE_SHA] bld = self.repo.TreeBuilder(TREE_SHA) result = bld.write() + + self.assertEqual(len(bld), len(tree)) self.assertEqual(tree.oid, result) + def test_noop_treebuilder_from_tree(self): tree = self.repo[TREE_SHA] bld = self.repo.TreeBuilder(tree) result = bld.write() + + self.assertEqual(len(bld), len(tree)) self.assertEqual(tree.oid, result) + def test_rebuild_treebuilder(self): tree = self.repo[TREE_SHA] bld = self.repo.TreeBuilder() for e in tree: bld.insert(e.name, e.hex, e.filemode) - result = bld.write() + + self.assertEqual(len(bld), len(tree)) self.assertEqual(tree.oid, result) From 3474dca78f2b87ed7ce8222eb0ce168e59437e23 Mon Sep 17 00:00:00 2001 From: XTao Date: Thu, 2 May 2013 11:25:23 +0800 Subject: [PATCH 0478/2237] Add merge-base method. --- docs/repository.rst | 1 + src/repository.c | 33 +++++++++++++++++++++++++++++++++ test/test_repository.py | 4 ++++ 3 files changed, 38 insertions(+) diff --git a/docs/repository.rst b/docs/repository.rst index 08ab3dd6e..e04ca2d61 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -41,3 +41,4 @@ To open an existing repository:: .. autoattribute:: pygit2.Repository.is_empty .. automethod:: pygit2.Repository.read .. automethod:: pygit2.Repository.write +.. automethod:: pygit2.Repository.merge_base diff --git a/src/repository.c b/src/repository.c index da3f4ba6e..5a7ab48fa 100644 --- a/src/repository.c +++ b/src/repository.c @@ -497,6 +497,38 @@ Repository_config__get__(Repository *self) return self->config; } +PyDoc_STRVAR(Repository_merge_base__doc__, + "merge_base(oid, oid) -> commit\n" + "\n" + "Find as good common ancestors as possible for a merge."); + +PyObject * +Repository_merge_base(Repository *self, PyObject *args) +{ + PyObject *value1; + PyObject *value2; + git_oid oid; + git_oid oid1; + git_oid oid2; + int err; + + if (!PyArg_ParseTuple(args, "OO", &value1, &value2)) + return NULL; + + err = py_str_to_git_oid_expand(self->repo, value1, &oid1); + if (err < 0) + return NULL; + + err = py_str_to_git_oid_expand(self->repo, value2, &oid2); + if (err < 0) + return NULL; + + err = git_merge_base(&oid, self->repo, &oid1, &oid2); + if (err < 0) + return Error_set(err); + + return git_oid_to_python(&oid); +} PyDoc_STRVAR(Repository_walk__doc__, "walk(oid, sort_mode) -> iterator\n" @@ -1183,6 +1215,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, create_tag, METH_VARARGS), METHOD(Repository, TreeBuilder, METH_VARARGS), METHOD(Repository, walk, METH_VARARGS), + METHOD(Repository, merge_base, METH_VARARGS), METHOD(Repository, read, METH_O), METHOD(Repository, write, METH_VARARGS), METHOD(Repository, git_reference_create, METH_VARARGS), diff --git a/test/test_repository.py b/test/test_repository.py index f16888c8c..93c4cc7dd 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -231,6 +231,10 @@ def test_checkout_head(self): self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE, head=True) self.assertTrue('bye.txt' not in self.repo.status()) + def test_merge_base(self): + commit = self.repo.merge_base('5ebeeebb320790caf276b9fc8b24546d63316533', '4ec4389a8068641da2d6578db0419484972284c8') + self.assertEqual(commit.hex, 'acecd5ea2924a4b900e7e149496e1f4b57976e51') + class NewRepositoryTest(utils.NoRepoTestCase): From 42aed417d40ede6dc6e49a2b491728a95ec24fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 4 May 2013 00:31:03 +0200 Subject: [PATCH 0479/2237] Add TreeBuilder.get and remove TreeEntry.to_object Also, check errors from git_tree_entry_dup --- docs/objects.rst | 1 - src/tree.c | 58 ++++++++++++++++------------------------ src/tree.h | 2 +- src/treebuilder.c | 58 ++++++++++++++++++++++++++++++---------- src/types.h | 1 - test/test_tree.py | 19 +++++++------ test/test_treebuilder.py | 7 +++-- 7 files changed, 84 insertions(+), 62 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index 1b6177591..a07736b09 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -187,7 +187,6 @@ interfaces:: .. autoattribute:: pygit2.TreeEntry.oid .. autoattribute:: pygit2.TreeEntry.hex .. autoattribute:: pygit2.TreeEntry.filemode -.. automethod:: pygit2.TreeEntry.to_object Creating trees diff --git a/src/tree.c b/src/tree.c index 05ff4b614..648b29d66 100644 --- a/src/tree.c +++ b/src/tree.c @@ -42,7 +42,6 @@ extern PyTypeObject IndexType; void TreeEntry_dealloc(TreeEntry *self) { - Py_CLEAR(self->owner); git_tree_entry_free((git_tree_entry*)self->entry); PyObject_Del(self); } @@ -87,22 +86,6 @@ TreeEntry_hex__get__(TreeEntry *self) } -PyDoc_STRVAR(TreeEntry_to_object__doc__, - "to_object() -> Object\n" - "\n" - "Look up the corresponding object in the repo."); - -PyObject * -TreeEntry_to_object(TreeEntry *self) -{ - const git_oid *entry_oid; - Repository *repo; - - repo = ((Object*)(self->owner))->repo; - entry_oid = git_tree_entry_id(self->entry); - return lookup_object(repo, entry_oid, GIT_OBJ_ANY); -} - PyGetSetDef TreeEntry_getseters[] = { GETTER(TreeEntry, filemode), GETTER(TreeEntry, name), @@ -111,11 +94,6 @@ PyGetSetDef TreeEntry_getseters[] = { {NULL} }; -PyMethodDef TreeEntry_methods[] = { - METHOD(TreeEntry, to_object, METH_NOARGS), - {NULL} -}; - PyDoc_STRVAR(TreeEntry__doc__, "TreeEntry objects."); @@ -147,7 +125,7 @@ PyTypeObject TreeEntryType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - TreeEntry_methods, /* tp_methods */ + 0, /* tp_methods */ 0, /* tp_members */ TreeEntry_getseters, /* tp_getset */ 0, /* tp_base */ @@ -181,16 +159,14 @@ Tree_contains(Tree *self, PyObject *py_name) } TreeEntry * -wrap_tree_entry(const git_tree_entry *entry, Tree *tree) +wrap_tree_entry(const git_tree_entry *entry) { TreeEntry *py_entry; py_entry = PyObject_New(TreeEntry, &TreeEntryType); - if (py_entry) { + if (py_entry) py_entry->entry = entry; - py_entry->owner = (PyObject*)tree; - Py_INCREF(tree); - } + return py_entry; } @@ -252,7 +228,14 @@ Tree_getitem_by_index(Tree *self, PyObject *py_index) PyErr_SetObject(PyExc_IndexError, py_index); return NULL; } - return wrap_tree_entry(git_tree_entry_dup(entry), self); + + entry = git_tree_entry_dup(entry); + if (entry == NULL) { + PyErr_SetNone(PyExc_MemoryError); + return NULL; + } + + return wrap_tree_entry(entry); } TreeEntry * @@ -283,7 +266,7 @@ Tree_getitem(Tree *self, PyObject *value) return (TreeEntry*)Error_set(err); /* git_tree_entry_dup is already done in git_tree_entry_bypath */ - return wrap_tree_entry(entry, self); + return wrap_tree_entry(entry); } @@ -431,15 +414,20 @@ TreeIter_dealloc(TreeIter *self) TreeEntry * TreeIter_iternext(TreeIter *self) { - const git_tree_entry *tree_entry; + const git_tree_entry *entry; - tree_entry = git_tree_entry_byindex(self->owner->tree, self->i); - if (!tree_entry) + entry = git_tree_entry_byindex(self->owner->tree, self->i); + if (!entry) return NULL; self->i += 1; - return (TreeEntry*)wrap_tree_entry(git_tree_entry_dup(tree_entry), - self->owner); + + entry = git_tree_entry_dup(entry); + if (entry == NULL) { + PyErr_SetNone(PyExc_MemoryError); + return NULL; + } + return wrap_tree_entry(entry); } diff --git a/src/tree.h b/src/tree.h index fef1facc5..94d0ff184 100644 --- a/src/tree.h +++ b/src/tree.h @@ -33,11 +33,11 @@ #include #include "types.h" +TreeEntry * wrap_tree_entry(const git_tree_entry *entry); PyObject* TreeEntry_get_filemode(TreeEntry *self); PyObject* TreeEntry_get_name(TreeEntry *self); PyObject* TreeEntry_get_oid(TreeEntry *self); PyObject* TreeEntry_get_hex(TreeEntry *self); -PyObject* TreeEntry_to_object(TreeEntry *self); TreeEntry* Tree_getitem_by_index(Tree *self, PyObject *py_index); TreeEntry* Tree_getitem(Tree *self, PyObject *value); diff --git a/src/treebuilder.c b/src/treebuilder.c index 4329a4d37..38c3a56c9 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -32,6 +32,7 @@ #include "utils.h" #include "oid.h" #include "treebuilder.h" +#include "tree.h" void @@ -44,9 +45,9 @@ TreeBuilder_dealloc(TreeBuilder *self) PyDoc_STRVAR(TreeBuilder_insert__doc__, - "insert(name, oid, attr)\n" - "\n" - "Insert or replace an entry in the treebuilder."); + "insert(name, oid, attr)\n" + "\n" + "Insert or replace an entry in the treebuilder."); PyObject * TreeBuilder_insert(TreeBuilder *self, PyObject *args) @@ -72,9 +73,9 @@ TreeBuilder_insert(TreeBuilder *self, PyObject *args) PyDoc_STRVAR(TreeBuilder_write__doc__, - "write() -> bytes\n" - "\n" - "Write the tree to the given repository."); + "write() -> bytes\n" + "\n" + "Write the tree to the given repository."); PyObject * TreeBuilder_write(TreeBuilder *self) @@ -90,10 +91,38 @@ TreeBuilder_write(TreeBuilder *self) } +PyDoc_STRVAR(TreeBuilder_get__doc__, + "get(name) -> TreeEntry\n" + "\n" + "Return the TreeEntry for the given name, or None if there is not."); + +PyObject * +TreeBuilder_get(TreeBuilder *self, PyObject *py_filename) +{ + char *filename; + const git_tree_entry *entry; + + filename = py_path_to_c_str(py_filename); + if (filename == NULL) + return NULL; + + entry = git_treebuilder_get(self->bld, filename); + if (entry == NULL) + Py_RETURN_NONE; + + entry = git_tree_entry_dup(entry); + if (entry == NULL) { + PyErr_SetNone(PyExc_MemoryError); + return NULL; + } + return (PyObject*)wrap_tree_entry(entry); +} + + PyDoc_STRVAR(TreeBuilder_remove__doc__, - "remove(name)\n" - "\n" - "Remove an entry from the builder."); + "remove(name)\n" + "\n" + "Remove an entry from the builder."); PyObject * TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename) @@ -114,9 +143,9 @@ TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename) PyDoc_STRVAR(TreeBuilder_clear__doc__, - "clear()\n" - "\n" - "Clear all the entries in the builder."); + "clear()\n" + "\n" + "Clear all the entries in the builder."); PyObject * TreeBuilder_clear(TreeBuilder *self) @@ -126,10 +155,11 @@ TreeBuilder_clear(TreeBuilder *self) } PyMethodDef TreeBuilder_methods[] = { + METHOD(TreeBuilder, clear, METH_NOARGS), + METHOD(TreeBuilder, get, METH_O), METHOD(TreeBuilder, insert, METH_VARARGS), - METHOD(TreeBuilder, write, METH_NOARGS), METHOD(TreeBuilder, remove, METH_O), - METHOD(TreeBuilder, clear, METH_NOARGS), + METHOD(TreeBuilder, write, METH_NOARGS), {NULL} }; diff --git a/src/types.h b/src/types.h index eceeeb9b1..72eba9293 100644 --- a/src/types.h +++ b/src/types.h @@ -130,7 +130,6 @@ SIMPLE_TYPE(TreeBuilder, git_treebuilder, bld) typedef struct { PyObject_HEAD - PyObject *owner; /* Tree or TreeBuilder */ const git_tree_entry *entry; } TreeEntry; diff --git a/test/test_tree.py b/test/test_tree.py index ecc07b3b1..16674ec32 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -71,23 +71,26 @@ def test_read_tree(self): self.assertTreeEntryEqual(tree['c/d'], sha, 'd', 0o0100644) self.assertRaisesWithArg(KeyError, 'ab/cd', lambda: tree['ab/cd']) + def test_read_subtree(self): tree = self.repo[TREE_SHA] subtree_entry = tree['c'] self.assertTreeEntryEqual(subtree_entry, SUBTREE_SHA, 'c', 0o0040000) - subtree = subtree_entry.to_object() + subtree = self.repo[subtree_entry.oid] self.assertEqual(1, len(subtree)) sha = '297efb891a47de80be0cfe9c639e4b8c9b450989' self.assertTreeEntryEqual(subtree[0], sha, 'd', 0o0100644) + def test_new_tree(self): - b0 = self.repo.create_blob('1') - b1 = self.repo.create_blob('2') - t = self.repo.TreeBuilder() + repo = self.repo + b0 = repo.create_blob('1') + b1 = repo.create_blob('2') + t = repo.TreeBuilder() t.insert('x', b0, 0o0100644) t.insert('y', b1, 0o0100755) - tree = self.repo[t.write()] + tree = repo[t.write()] self.assertTrue('x' in tree) self.assertTrue('y' in tree) @@ -97,9 +100,8 @@ def test_new_tree(self): self.assertEqual(x.filemode, 0o0100644) self.assertEqual(y.filemode, 0o0100755) - self.assertEqual(x.to_object().oid, b0) - self.assertEqual(y.to_object().oid, b1) - + self.assertEqual(repo[x.oid].oid, b0) + self.assertEqual(repo[y.oid].oid, b1) def test_modify_tree(self): @@ -107,6 +109,7 @@ def test_modify_tree(self): self.assertRaises(TypeError, operator.setitem, 'c', tree['a']) self.assertRaises(TypeError, operator.delitem, 'c') + def test_iterate_tree(self): """ Testing that we're able to iterate of a Tree object and that the diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index 2c30f7677..196b2412d 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -63,8 +63,11 @@ def test_noop_treebuilder_from_tree(self): def test_rebuild_treebuilder(self): tree = self.repo[TREE_SHA] bld = self.repo.TreeBuilder() - for e in tree: - bld.insert(e.name, e.hex, e.filemode) + for entry in tree: + name = entry.name + self.assertIsNone(bld.get(name)) + bld.insert(name, entry.hex, entry.filemode) + self.assertEqual(bld.get(name).oid, entry.oid) result = bld.write() self.assertEqual(len(bld), len(tree)) From 723cae1186328e54773ef0c064e0c504147cece6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 4 May 2013 00:40:32 +0200 Subject: [PATCH 0480/2237] Fix tests for Python 2.6 --- test/test_treebuilder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index 196b2412d..51e7a1062 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -65,7 +65,7 @@ def test_rebuild_treebuilder(self): bld = self.repo.TreeBuilder() for entry in tree: name = entry.name - self.assertIsNone(bld.get(name)) + self.assertTrue(bld.get(name) is None) bld.insert(name, entry.hex, entry.filemode) self.assertEqual(bld.get(name).oid, entry.oid) result = bld.write() From 3a0bafa12bbd9d8abee91141b8a46ba7372cc517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 4 May 2013 09:30:37 +0200 Subject: [PATCH 0481/2237] Rename "low level" reference creation methods Rename Repository methods: - git_reference_create => create_reference_direct - git_reference_symbolic_create => create_reference_symbolic --- pygit2/repository.py | 4 ++-- src/repository.c | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index 0cb57e383..ce4890311 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -85,6 +85,6 @@ def create_reference(self, name, target, force=False): ) if direct: - return self.git_reference_create(name, target, force) + return self.create_reference_direct(name, target, force) - return self.git_reference_symbolic_create(name, target, force) + return self.create_reference_symbolic(name, target, force) diff --git a/src/repository.c b/src/repository.c index 27f621b9b..6b9c446c8 100644 --- a/src/repository.c +++ b/src/repository.c @@ -816,7 +816,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) return wrap_reference(c_reference); } -PyDoc_STRVAR(Repository_git_reference_create__doc__, +PyDoc_STRVAR(Repository_create_reference_direct__doc__, "git_reference_create(name, target, force) -> Reference\n" "\n" "Create a new reference \"name\" which points to an object.\n" @@ -832,8 +832,8 @@ PyDoc_STRVAR(Repository_git_reference_create__doc__, " repo.git_reference_create('refs/heads/foo', repo.head.hex, False)"); PyObject * -Repository_git_reference_create(Repository *self, PyObject *args, - PyObject *kw) +Repository_create_reference_direct(Repository *self, PyObject *args, + PyObject *kw) { PyObject *py_obj; git_reference *c_reference; @@ -855,7 +855,7 @@ Repository_git_reference_create(Repository *self, PyObject *args, return wrap_reference(c_reference); } -PyDoc_STRVAR(Repository_git_reference_symbolic_create__doc__, +PyDoc_STRVAR(Repository_create_reference_symbolic__doc__, "git_reference_symbolic_create(name, source, force) -> Reference\n" "\n" "Create a new reference \"name\" which points to another reference.\n" @@ -871,8 +871,8 @@ PyDoc_STRVAR(Repository_git_reference_symbolic_create__doc__, " repo.git_reference_symbolic_create('refs/tags/foo', 'refs/heads/master', False)"); PyObject * -Repository_git_reference_symbolic_create(Repository *self, PyObject *args, - PyObject *kw) +Repository_create_reference_symbolic(Repository *self, PyObject *args, + PyObject *kw) { git_reference *c_reference; char *c_name, *c_target; @@ -1209,8 +1209,8 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, walk, METH_VARARGS), METHOD(Repository, read, METH_O), METHOD(Repository, write, METH_VARARGS), - METHOD(Repository, git_reference_create, METH_VARARGS), - METHOD(Repository, git_reference_symbolic_create, METH_VARARGS), + METHOD(Repository, create_reference_direct, METH_VARARGS), + METHOD(Repository, create_reference_symbolic, METH_VARARGS), METHOD(Repository, listall_references, METH_VARARGS), METHOD(Repository, lookup_reference, METH_O), METHOD(Repository, revparse_single, METH_O), From 7ac3f50a615f35a0df1f84881c802634129de547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Sat, 4 May 2013 20:04:37 +0200 Subject: [PATCH 0482/2237] Remove all warnings during compilation. - Many PyLong_FromLong changed into PyLong_FromLongLong. - A long used as out argument for git_config_parse_int64 changed into a int64_t. - Many len variables changed from int into Py_ssize_t. Removed some castings related to those variables. - Functions py_str_to_git_oid(_expand) return a Py_ssize_t, which is the return type of PyBytes_AsStringAndSize. Error values are usually casted to int, since the only error returned from those functions is -1. - Changed RefLogIter i and size fields from int into size_t. - Marked to_unicode_n and to_bytes inline functions as "unused". Not all compilation units which include utils.h use them. Tested with: $ clang --version Apple clang version 4.0 (tags/Apple/clang-421.0.57) (based on LLVM 3.1svn) Target: x86_64-apple-darwin12.3.0 Thread model: posix --- src/blob.c | 2 +- src/commit.c | 2 +- src/config.c | 4 ++-- src/index.c | 6 +++--- src/oid.c | 12 ++++++------ src/oid.h | 6 +++--- src/reference.c | 7 +++++-- src/repository.c | 35 ++++++++++++++++++++--------------- src/signature.c | 2 +- src/treebuilder.c | 3 ++- src/types.h | 4 ++-- src/utils.h | 8 ++++++++ src/walker.c | 14 ++++++++------ 13 files changed, 62 insertions(+), 43 deletions(-) diff --git a/src/blob.c b/src/blob.c index ec760ce43..289160ca5 100644 --- a/src/blob.c +++ b/src/blob.c @@ -37,7 +37,7 @@ PyDoc_STRVAR(Blob_size__doc__, "Size."); PyObject * Blob_size__get__(Blob *self) { - return PyLong_FromLong(git_blob_rawsize(self->blob)); + return PyLong_FromLongLong(git_blob_rawsize(self->blob)); } diff --git a/src/commit.c b/src/commit.c index 139c8fd99..d87ec576b 100644 --- a/src/commit.c +++ b/src/commit.c @@ -77,7 +77,7 @@ PyDoc_STRVAR(Commit_commit_time__doc__, "Commit time."); PyObject * Commit_commit_time__get__(Commit *commit) { - return PyLong_FromLong(git_commit_time(commit->commit)); + return PyLong_FromLongLong(git_commit_time(commit->commit)); } diff --git a/src/config.c b/src/config.c index 8cb035968..6fa58d986 100644 --- a/src/config.c +++ b/src/config.c @@ -172,7 +172,7 @@ Config_contains(Config *self, PyObject *py_key) { PyObject * Config_getitem(Config *self, PyObject *py_key) { - long value_int; + int64_t value_int; int err, value_bool; const char *value_str; char *key; @@ -187,7 +187,7 @@ Config_getitem(Config *self, PyObject *py_key) goto cleanup; if (git_config_parse_int64(&value_int, value_str) == 0) - py_value = PyLong_FromLong(value_int); + py_value = PyLong_FromLongLong(value_int); else if(git_config_parse_bool(&value_bool, value_str) == 0) py_value = PyBool_FromLong(value_bool); else diff --git a/src/index.c b/src/index.c index 9176564f8..55fe01a33 100644 --- a/src/index.c +++ b/src/index.c @@ -351,14 +351,14 @@ Index_read_tree(Index *self, PyObject *value) { git_oid oid; git_tree *tree; - int err, len; + int err; + Py_ssize_t len; len = py_str_to_git_oid(value, &oid); if (len < 0) return NULL; - err = git_tree_lookup_prefix(&tree, self->repo->repo, &oid, - (unsigned int)len); + err = git_tree_lookup_prefix(&tree, self->repo->repo, &oid, len); if (err < 0) return Error_set(err); diff --git a/src/oid.c b/src/oid.c index 0e32355f0..8a32b20f7 100644 --- a/src/oid.c +++ b/src/oid.c @@ -45,7 +45,7 @@ git_oid_to_python(const git_oid *oid) return (PyObject*)py_oid; } -int +Py_ssize_t _oid_from_hex(PyObject *py_oid, git_oid *oid) { PyObject *py_hex; @@ -97,7 +97,7 @@ _oid_from_hex(PyObject *py_oid, git_oid *oid) return -1; } -int +Py_ssize_t py_str_to_git_oid(PyObject *py_oid, git_oid *oid) { /* Oid */ @@ -110,11 +110,11 @@ py_str_to_git_oid(PyObject *py_oid, git_oid *oid) return _oid_from_hex(py_oid, oid); } -int +Py_ssize_t py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, git_oid *oid) { int err; - int len; + Py_ssize_t len; git_odb *odb; git_odb_object *obj; @@ -197,8 +197,8 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw) } /* Case 2: hex */ - err = _oid_from_hex(hex, &self->oid); - if (err < 0) + len = _oid_from_hex(hex, &self->oid); + if (len < 0) return -1; return 0; diff --git a/src/oid.h b/src/oid.h index 796d08720..ca6567c1a 100644 --- a/src/oid.h +++ b/src/oid.h @@ -32,9 +32,9 @@ #include #include -int py_str_to_git_oid(PyObject *py_str, git_oid *oid); -int py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, - git_oid *oid); +Py_ssize_t py_str_to_git_oid(PyObject *py_str, git_oid *oid); +Py_ssize_t py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, + git_oid *oid); PyObject* git_oid_to_python(const git_oid *oid); PyObject* git_oid_to_py_str(const git_oid *oid); diff --git a/src/reference.c b/src/reference.c index f4478dbed..17537e73e 100644 --- a/src/reference.c +++ b/src/reference.c @@ -226,6 +226,7 @@ Reference_target__set__(Reference *self, PyObject *py_target) git_oid oid; char *c_name; int err; + Py_ssize_t len; git_reference *new_ref; git_repository *repo; @@ -234,9 +235,11 @@ Reference_target__set__(Reference *self, PyObject *py_target) /* Case 1: Direct */ if (GIT_REF_OID == git_reference_type(self->reference)) { repo = git_reference_owner(self->reference); - err = py_str_to_git_oid_expand(repo, py_target, &oid); - if (err < 0) + len = py_str_to_git_oid_expand(repo, py_target, &oid); + if (len < 0) { + err = (int)len; goto error; + } err = git_reference_set_target(&new_ref, self->reference, &oid); if (err < 0) diff --git a/src/repository.c b/src/repository.c index 6b9c446c8..39ec15a2c 100644 --- a/src/repository.c +++ b/src/repository.c @@ -246,7 +246,8 @@ PyDoc_STRVAR(Repository_git_object_lookup_prefix__doc__, PyObject * Repository_git_object_lookup_prefix(Repository *self, PyObject *key) { - int err, len; + int err; + Py_ssize_t len; git_oid oid; git_object *obj; @@ -254,8 +255,7 @@ Repository_git_object_lookup_prefix(Repository *self, PyObject *key) if (len < 0) return NULL; - err = git_object_lookup_prefix(&obj, self->repo, &oid, - (unsigned int)len, GIT_OBJ_ANY); + err = git_object_lookup_prefix(&obj, self->repo, &oid, len, GIT_OBJ_ANY); if (err == 0) return wrap_object(obj, self); @@ -332,7 +332,7 @@ Repository_read(Repository *self, PyObject *py_hex) { git_oid oid; git_odb_object *obj; - int len; + Py_ssize_t len; PyObject* tuple; len = py_str_to_git_oid(py_hex, &oid); @@ -509,6 +509,7 @@ Repository_walk(Repository *self, PyObject *args) PyObject *value; unsigned int sort; int err; + Py_ssize_t len; git_oid oid; git_revwalk *walk; Walker *py_walker; @@ -525,10 +526,10 @@ Repository_walk(Repository *self, PyObject *args) /* Push */ if (value != Py_None) { - err = py_str_to_git_oid_expand(self->repo, value, &oid); - if (err < 0) { + len = py_str_to_git_oid_expand(self->repo, value, &oid); + if (len < 0) { git_revwalk_free(walk); - return Error_set(err); + return Error_set((int)len); } err = git_revwalk_push(walk, &oid); @@ -639,7 +640,8 @@ Repository_create_commit(Repository *self, PyObject *args) git_tree *tree = NULL; int parent_count; git_commit **parents = NULL; - int err = 0, i = 0, len; + int err = 0, i = 0; + Py_ssize_t len; if (!PyArg_ParseTuple(args, "zO!O!OOO!|s", &update_ref, @@ -659,7 +661,7 @@ Repository_create_commit(Repository *self, PyObject *args) if (message == NULL) goto out; - err = git_tree_lookup_prefix(&tree, self->repo, &oid, (unsigned int)len); + err = git_tree_lookup_prefix(&tree, self->repo, &oid, len); if (err < 0) { Error_set(err); goto out; @@ -717,7 +719,8 @@ Repository_create_tag(Repository *self, PyObject *args) char *tag_name, *message; git_oid oid; git_object *target = NULL; - int err, target_type, len; + int err, target_type; + Py_ssize_t len; if (!PyArg_ParseTuple(args, "sOiO!s", &tag_name, @@ -840,13 +843,14 @@ Repository_create_reference_direct(Repository *self, PyObject *args, char *c_name; git_oid oid; int err, force; + Py_ssize_t len; if (!PyArg_ParseTuple(args, "sOi", &c_name, &py_obj, &force)) return NULL; - err = py_str_to_git_oid_expand(self->repo, py_obj, &oid); - if (err < 0) - return Error_set(err); + len = py_str_to_git_oid_expand(self->repo, py_obj, &oid); + if (len < 0) + return Error_set((int)len); err = git_reference_create(&c_reference, self->repo, c_name, &oid, force); if (err < 0) @@ -967,6 +971,7 @@ Repository_TreeBuilder(Repository *self, PyObject *args) git_tree *tree = NULL; git_tree *must_free = NULL; int err; + Py_ssize_t len; if (!PyArg_ParseTuple(args, "|O", &py_src)) return NULL; @@ -980,8 +985,8 @@ Repository_TreeBuilder(Repository *self, PyObject *args) } tree = py_tree->tree; } else { - err = py_str_to_git_oid_expand(self->repo, py_src, &oid); - if (err < 0) + len = py_str_to_git_oid_expand(self->repo, py_src, &oid); + if (len < 0) return NULL; err = git_tree_lookup(&tree, self->repo, &oid); diff --git a/src/signature.c b/src/signature.c index 31da0bd6c..24e02a83e 100644 --- a/src/signature.c +++ b/src/signature.c @@ -148,7 +148,7 @@ PyDoc_STRVAR(Signature_time__doc__, "Unix time."); PyObject * Signature_time__get__(Signature *self) { - return PyLong_FromLong(self->signature->when.time); + return PyLong_FromLongLong(self->signature->when.time); } diff --git a/src/treebuilder.c b/src/treebuilder.c index 38c3a56c9..55601e58f 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -53,7 +53,8 @@ PyObject * TreeBuilder_insert(TreeBuilder *self, PyObject *args) { PyObject *py_oid; - int len, err, attr; + Py_ssize_t len; + int err, attr; git_oid oid; const char *fname; diff --git a/src/types.h b/src/types.h index 72eba9293..fd4e4cafd 100644 --- a/src/types.h +++ b/src/types.h @@ -174,8 +174,8 @@ typedef struct { typedef struct { PyObject_HEAD git_reflog *reflog; - int i; - int size; + size_t i; + size_t size; } RefLogIter; diff --git a/src/utils.h b/src/utils.h index d35f4677a..1fb246699 100644 --- a/src/utils.h +++ b/src/utils.h @@ -33,6 +33,12 @@ #include #include "types.h" +#ifdef __GNUC__ +# define PYGIT2_FN_UNUSED __attribute__((unused)) +#else +# define PYGIT2_FN_UNUSED +#endif + /* Python 2 support */ #if PY_MAJOR_VERSION == 2 #define PyLong_FromSize_t PyInt_FromSize_t @@ -75,6 +81,7 @@ /* Utilities */ #define to_unicode(x, encoding, errors) to_unicode_n(x, strlen(x), encoding, errors) +PYGIT2_FN_UNUSED Py_LOCAL_INLINE(PyObject*) to_unicode_n(const char *value, size_t len, const char *encoding, const char *errors) { @@ -90,6 +97,7 @@ to_unicode_n(const char *value, size_t len, const char *encoding, const char *er return PyUnicode_Decode(value, len, encoding, errors); } +PYGIT2_FN_UNUSED Py_LOCAL_INLINE(PyObject*) to_bytes(const char * value) { diff --git a/src/walker.c b/src/walker.c index 2799feb4f..1952fbe32 100644 --- a/src/walker.c +++ b/src/walker.c @@ -53,11 +53,12 @@ PyObject * Walker_hide(Walker *self, PyObject *py_hex) { int err; + Py_ssize_t len; git_oid oid; - err = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); - if (err < 0) - return Error_set(err); + len = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); + if (len < 0) + return Error_set((int)len); err = git_revwalk_hide(self->walk, &oid); if (err < 0) @@ -76,11 +77,12 @@ PyObject * Walker_push(Walker *self, PyObject *py_hex) { int err; + Py_ssize_t len; git_oid oid; - err = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); - if (err < 0) - return Error_set(err); + len = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); + if (len < 0) + return Error_set((int)len); err = git_revwalk_push(self->walk, &oid); if (err < 0) From 63820aa608c6cdfcd498048b4c0efefc536a78ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Cauwelier?= Date: Sun, 5 May 2013 10:35:19 +0200 Subject: [PATCH 0483/2237] update documentation on creating blobs --- docs/objects.rst | 4 ++-- src/repository.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index a07736b09..dd849bb76 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -220,8 +220,8 @@ Creating blobs -------------------- .. automethod:: pygit2.Repository.create_blob -.. automethod:: pygit2.Repository.create_blob_fromfile - +.. automethod:: pygit2.Repository.create_blob_fromworkdir +.. automethod:: pygit2.Repository.create_blob_fromdisk Tags ================= diff --git a/src/repository.c b/src/repository.c index 6b9c446c8..bbe1bcfe1 100644 --- a/src/repository.c +++ b/src/repository.c @@ -578,7 +578,7 @@ Repository_create_blob(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_blob_fromworkdir__doc__, "create_blob_fromworkdir(path) -> bytes\n" "\n" - "Create a new blob from a file within the working directory."); + "Create a new blob from a file within the working directory (raise an error otherwise)."); PyObject * Repository_create_blob_fromworkdir(Repository *self, PyObject *args) @@ -601,7 +601,7 @@ Repository_create_blob_fromworkdir(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_blob_fromdisk__doc__, "create_blob_fromdisk(path) -> bytes\n" "\n" - "Create a new blob from file."); + "Create a new blob from a file anywhere (no working directory check)."); PyObject * Repository_create_blob_fromdisk(Repository *self, PyObject *args) From 974f16ca69afc3af43412b3ee3e4928bb16ee9b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 5 May 2013 21:34:07 +0200 Subject: [PATCH 0484/2237] Re-work the checkout API New API: Repository.head = refname Repository.checkout_head(strategy) Repository.checkout_index(strategy) Repository.checkout_tree(treeish, strategy) Changed API: # Before Repository.checkout(strategy=GIT_CHECKOUT_SAFE_CREATE, reference=None, head=False) # Now Repository.checkout(refname=None, strategy=GIT_CHECKOUT_SAFE_CREATE) --- docs/working-copy.rst | 6 +++ pygit2/repository.py | 40 ++++++++++++++ src/repository.c | 117 ++++++++++++++++++++++++++++------------ src/utils.c | 7 +-- test/test_repository.py | 20 +++---- 5 files changed, 141 insertions(+), 49 deletions(-) diff --git a/docs/working-copy.rst b/docs/working-copy.rst index 593fcd151..349155245 100644 --- a/docs/working-copy.rst +++ b/docs/working-copy.rst @@ -64,3 +64,9 @@ Checkout ==================== .. automethod:: pygit2.Repository.checkout + +Lower level API: + +.. automethod:: pygit2.Repository.checkout_head +.. automethod:: pygit2.Repository.checkout_tree +.. automethod:: pygit2.Repository.checkout_index diff --git a/pygit2/repository.py b/pygit2/repository.py index ce4890311..93f8f5447 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -31,6 +31,8 @@ # Import from pygit2 from _pygit2 import Repository as _Repository from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN +from _pygit2 import GIT_CHECKOUT_SAFE_CREATE +from _pygit2 import Reference class Repository(_Repository): @@ -88,3 +90,41 @@ def create_reference(self, name, target, force=False): return self.create_reference_direct(name, target, force) return self.create_reference_symbolic(name, target, force) + + + # + # Checkout + # + def checkout(self, refname=None, strategy=GIT_CHECKOUT_SAFE_CREATE): + """ + Checkout the given reference using the given strategy, and update + the HEAD. + The reference may be a reference name or a Reference object. + The default strategy is GIT_CHECKOUT_SAFE_CREATE. + + To checkout from the HEAD, just pass 'HEAD':: + + >>> checkout('HEAD') + + If no reference is given, checkout from the index. + + """ + # Case 1: Checkout index + if refname is None: + return self.checkout_index(strategy) + + # Case 2: Checkout head + if refname == 'HEAD': + return self.checkout_head(strategy) + + # Case 3: Reference + if type(refname) is Reference: + reference = refname + refname = refname.name + else: + reference = self.lookup_reference(refname) + + oid = reference.resolve().target + treeish = self[oid] + self.checkout_tree(treeish, strategy) + self.head = refname diff --git a/src/repository.c b/src/repository.c index c5acb8d43..f39c1c73a 100644 --- a/src/repository.c +++ b/src/repository.c @@ -42,6 +42,7 @@ extern PyObject *GitError; extern PyTypeObject IndexType; extern PyTypeObject WalkerType; extern PyTypeObject SignatureType; +extern PyTypeObject ObjectType; extern PyTypeObject TreeType; extern PyTypeObject TreeBuilderType; extern PyTypeObject ConfigType; @@ -183,6 +184,25 @@ Repository_head__get__(Repository *self) return wrap_reference(head); } +int +Repository_head__set__(Repository *self, PyObject *py_refname) +{ + int err; + const char *refname; + + refname = py_str_to_c_str(py_refname, NULL); + if (refname == NULL) + return -1; + + err = git_repository_set_head(self->repo, refname); + if (err < 0) { + Error_set_str(err, refname); + return -1; + } + + return 0; +} + PyDoc_STRVAR(Repository_head_is_detached__doc__, "A repository's HEAD is detached when it points directly to a commit\n" @@ -1101,47 +1121,72 @@ Repository_remotes__get__(Repository *self) } -PyDoc_STRVAR(Repository_checkout__doc__, - "checkout([strategy:int, reference:Reference])\n" - "\n" - "Checks out a tree by a given reference and modifies the HEAD pointer\n" - "Standard checkout strategy is pygit2.GIT_CHECKOUT_SAFE_CREATE\n" - "If no reference is given, checkout will use HEAD instead."); +PyDoc_STRVAR(Repository_checkout_head__doc__, + "checkout_head(strategy)\n" + "\n" + "Checkout the head using the given strategy."); PyObject * -Repository_checkout(Repository *self, PyObject *args, PyObject *kw) +Repository_checkout_head(Repository *self, PyObject *args) { git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; - unsigned int strategy = GIT_CHECKOUT_SAFE_CREATE; - Reference* ref = NULL; - git_object* object; - const git_oid* id; - int err, head = 0; + unsigned int strategy; + int err; + + if (!PyArg_ParseTuple(args, "I", &strategy)) + return NULL; + + opts.checkout_strategy = strategy; + err = git_checkout_head(self->repo, &opts); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + - static char *kwlist[] = {"strategy", "reference", "head", NULL}; +PyDoc_STRVAR(Repository_checkout_index__doc__, + "checkout_index(strategy)\n" + "\n" + "Checkout the index using the given strategy."); - if (!PyArg_ParseTupleAndKeywords(args, kw, "|IO!i", kwlist, - &strategy, &ReferenceType, &ref, &head)) +PyObject * +Repository_checkout_index(Repository *self, PyObject *args) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + unsigned int strategy; + int err; + + if (!PyArg_ParseTuple(args, "I", &strategy)) return NULL; - if (ref != NULL) { /* checkout from treeish */ - id = git_reference_target(ref->reference); - err = git_object_lookup(&object, self->repo, id, GIT_OBJ_COMMIT); - if (err == GIT_OK) { - opts.checkout_strategy = strategy; - err = git_checkout_tree(self->repo, object, &opts); - if (err == GIT_OK) { - err = git_repository_set_head(self->repo, - git_reference_name(ref->reference)); - } - git_object_free(object); - } - } else { /* checkout from head / index */ - opts.checkout_strategy = strategy; - err = (!head) ? git_checkout_index(self->repo, NULL, &opts) : - git_checkout_head(self->repo, &opts); - } + opts.checkout_strategy = strategy; + err = git_checkout_index(self->repo, NULL, &opts); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + + +PyDoc_STRVAR(Repository_checkout_tree__doc__, + "checkout_tree(treeish, strategy)\n" + "\n" + "Checkout the given tree, commit or tag, using the given strategy."); + +PyObject * +Repository_checkout_tree(Repository *self, PyObject *args) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + unsigned int strategy; + Object *py_object; + int err; + + if (!PyArg_ParseTuple(args, "O!I", &ObjectType, &py_object, &strategy)) + return NULL; + opts.checkout_strategy = strategy; + err = git_checkout_tree(self->repo, py_object->obj, &opts); if (err < 0) return Error_set(err); @@ -1152,7 +1197,7 @@ Repository_checkout(Repository *self, PyObject *args, PyObject *kw) PyDoc_STRVAR(Repository_notes__doc__, ""); PyObject * -Repository_notes(Repository *self, PyObject* args) +Repository_notes(Repository *self, PyObject *args) { NoteIter *iter = NULL; char *ref = "refs/notes/commits"; @@ -1255,7 +1300,9 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, status, METH_NOARGS), METHOD(Repository, status_file, METH_O), METHOD(Repository, create_remote, METH_VARARGS), - METHOD(Repository, checkout, METH_VARARGS|METH_KEYWORDS), + METHOD(Repository, checkout_head, METH_VARARGS), + METHOD(Repository, checkout_index, METH_VARARGS), + METHOD(Repository, checkout_tree, METH_VARARGS), METHOD(Repository, notes, METH_VARARGS), METHOD(Repository, create_note, METH_VARARGS), METHOD(Repository, lookup_note, METH_VARARGS), @@ -1266,7 +1313,7 @@ PyMethodDef Repository_methods[] = { PyGetSetDef Repository_getseters[] = { GETTER(Repository, index), GETTER(Repository, path), - GETTER(Repository, head), + GETSET(Repository, head), GETTER(Repository, head_is_detached), GETTER(Repository, head_is_orphaned), GETTER(Repository, is_empty), diff --git a/src/utils.c b/src/utils.c index 01a24c3a7..31f0472aa 100644 --- a/src/utils.c +++ b/src/utils.c @@ -34,7 +34,8 @@ extern PyTypeObject ReferenceType; /* py_str_to_c_str() returns a newly allocated C string holding * the string contained in the value argument. */ -char * py_str_to_c_str(PyObject *value, const char *encoding) +char * +py_str_to_c_str(PyObject *value, const char *encoding) { char *c_str = NULL; /* Case 1: byte string */ @@ -43,7 +44,6 @@ char * py_str_to_c_str(PyObject *value, const char *encoding) /* Case 2: text string */ if (PyUnicode_Check(value)) { - if (encoding == NULL) value = PyUnicode_AsUTF8String(value); else @@ -60,6 +60,3 @@ char * py_str_to_c_str(PyObject *value, const char *encoding) Py_TYPE(value)->tp_name); return NULL; } - - - diff --git a/test/test_repository.py b/test/test_repository.py index 93c4cc7dd..b11c804ad 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -191,18 +191,17 @@ def test_checkout_ref(self): # checkout i18n with conflicts and default strategy should # not be possible - self.assertRaises(pygit2.GitError, - lambda: self.repo.checkout(reference=ref_i18n)) + self.assertRaises(pygit2.GitError, self.repo.checkout, ref_i18n) # checkout i18n with GIT_CHECKOUT_FORCE head = self.repo.head head = self.repo[head.target] self.assertTrue('new' not in head.tree) - self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE, ref_i18n) + self.repo.checkout(ref_i18n, pygit2.GIT_CHECKOUT_FORCE) head = self.repo.head head = self.repo[head.target] - self.assertEqual(head.hex, self.repo[ref_i18n.target].hex) + self.assertEqual(head.hex, ref_i18n.target.hex) self.assertTrue('new' in head.tree) self.assertTrue('bye.txt' not in self.repo.status()) @@ -213,7 +212,7 @@ def test_checkout_index(self): # checkout index self.assertTrue('hello.txt' in self.repo.status()) - self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE) + self.repo.checkout(strategy=pygit2.GIT_CHECKOUT_FORCE) self.assertTrue('hello.txt' not in self.repo.status()) def test_checkout_head(self): @@ -224,16 +223,19 @@ def test_checkout_head(self): # checkout from index should not change anything self.assertTrue('bye.txt' in self.repo.status()) - self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE) + self.repo.checkout(strategy=pygit2.GIT_CHECKOUT_FORCE) self.assertTrue('bye.txt' in self.repo.status()) # checkout from head will reset index as well - self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE, head=True) + self.repo.checkout('HEAD', pygit2.GIT_CHECKOUT_FORCE) self.assertTrue('bye.txt' not in self.repo.status()) def test_merge_base(self): - commit = self.repo.merge_base('5ebeeebb320790caf276b9fc8b24546d63316533', '4ec4389a8068641da2d6578db0419484972284c8') - self.assertEqual(commit.hex, 'acecd5ea2924a4b900e7e149496e1f4b57976e51') + commit = self.repo.merge_base( + '5ebeeebb320790caf276b9fc8b24546d63316533', + '4ec4389a8068641da2d6578db0419484972284c8') + self.assertEqual(commit.hex, + 'acecd5ea2924a4b900e7e149496e1f4b57976e51') class NewRepositoryTest(utils.NoRepoTestCase): From a14e6bfbe5b6b8192c4e94c6e522a6d62ff36c58 Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Mon, 6 May 2013 12:29:56 +1000 Subject: [PATCH 0485/2237] Include git2/odb_backend --- src/repository.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/repository.c b/src/repository.c index f60bb8664..5b81e5d2c 100644 --- a/src/repository.c +++ b/src/repository.c @@ -36,6 +36,7 @@ #include "note.h" #include "repository.h" #include "remote.h" +#include extern PyObject *GitError; From af5d0d07dd3f59a016b5b72840055895c012898f Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Mon, 6 May 2013 13:51:15 +1000 Subject: [PATCH 0486/2237] Include git2/remote.h --- src/remote.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/remote.h b/src/remote.h index 726a52aa8..7f6e131de 100644 --- a/src/remote.h +++ b/src/remote.h @@ -31,6 +31,7 @@ #define PY_SSIZE_T_CLEAN #include #include +#include PyObject* Remote_init(Remote *self, PyObject *args, PyObject *kwds); PyObject* Remote_fetch(Remote *self, PyObject *args); From f972d99d5f9f82ce3c04af6b3987ca2a337815e3 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 6 May 2013 13:22:48 +0200 Subject: [PATCH 0487/2237] Added line origins for Hunks in Pygit2.Patch --- src/diff.c | 5 +++-- src/pygit2.c | 9 --------- src/types.h | 1 + test/test_diff.py | 1 + 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/diff.c b/src/diff.c index aa69ee2e0..3c30589b5 100644 --- a/src/diff.c +++ b/src/diff.c @@ -88,8 +88,8 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) PyList_SetItem(py_hunk->lines, 0, to_unicode_n(header, header_len, NULL, NULL)); for (j=1; j < lines_in_hunk + 1; ++j) { - err = git_diff_patch_get_line_in_hunk(NULL, &line, - &line_len, NULL, NULL, patch, i, j - 1); + err = git_diff_patch_get_line_in_hunk(&py_hunk->origin, + &line, &line_len, NULL, NULL, patch, i, j - 1); if (err < 0) goto cleanup; @@ -279,6 +279,7 @@ Hunk_dealloc(Hunk *self) } PyMemberDef Hunk_members[] = { + MEMBER(Hunk, origin, T_CHAR, "origin."), MEMBER(Hunk, old_start, T_INT, "Old start."), MEMBER(Hunk, old_lines, T_INT, "Old lines."), MEMBER(Hunk, new_start, T_INT, "New start."), diff --git a/src/pygit2.c b/src/pygit2.c index e4a7c64f3..71a543705 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -338,15 +338,6 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_DELTA_COPIED) ADD_CONSTANT_INT(m, GIT_DELTA_IGNORED) ADD_CONSTANT_INT(m, GIT_DELTA_UNTRACKED) - /* Flags for diffed lines origin */ - ADD_CONSTANT_INT(m, GIT_DIFF_LINE_CONTEXT) - ADD_CONSTANT_INT(m, GIT_DIFF_LINE_ADDITION) - ADD_CONSTANT_INT(m, GIT_DIFF_LINE_DELETION) - ADD_CONSTANT_INT(m, GIT_DIFF_LINE_ADD_EOFNL) - ADD_CONSTANT_INT(m, GIT_DIFF_LINE_DEL_EOFNL) - ADD_CONSTANT_INT(m, GIT_DIFF_LINE_FILE_HDR) - ADD_CONSTANT_INT(m, GIT_DIFF_LINE_HUNK_HDR) - ADD_CONSTANT_INT(m, GIT_DIFF_LINE_BINARY) /* Config */ INIT_TYPE(ConfigType, NULL, PyType_GenericNew) diff --git a/src/types.h b/src/types.h index fd4e4cafd..05c068e3b 100644 --- a/src/types.h +++ b/src/types.h @@ -118,6 +118,7 @@ typedef struct { typedef struct { PyObject_HEAD PyObject* lines; + char origin; int old_start; int old_lines; int new_start; diff --git a/test/test_diff.py b/test/test_diff.py index 71e15cda6..3074f8d72 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -134,6 +134,7 @@ def test_diff_tree(self): patch = diff[0] hunk = patch.hunks[0] + self.assertEqual(hunk.origin, '+') self.assertEqual(hunk.old_start, 1) self.assertEqual(hunk.old_lines, 1) self.assertEqual(hunk.new_start, 1) From f572a8fb00fcc7d2b72916c917e6aab96d32cfb0 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 6 May 2013 13:29:37 +0200 Subject: [PATCH 0488/2237] use chars like +,-,... for diff_delta_t in Patch.status --- src/diff.c | 4 ++-- src/pygit2.c | 9 --------- src/types.h | 2 +- test/test_diff.py | 4 ++-- 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/diff.c b/src/diff.c index 3c30589b5..8e87c70cf 100644 --- a/src/diff.c +++ b/src/diff.c @@ -62,7 +62,7 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) if (py_patch != NULL) { py_patch->old_file_path = delta->old_file.path; py_patch->new_file_path = delta->new_file.path; - py_patch->status = delta->status; + py_patch->status = git_diff_status_char(delta->status); py_patch->similarity = delta->similarity; py_patch->old_oid = git_oid_allocfmt(&delta->old_file.oid); py_patch->new_oid = git_oid_allocfmt(&delta->new_file.oid); @@ -126,7 +126,7 @@ PyMemberDef Patch_members[] = { MEMBER(Patch, new_file_path, T_STRING, "new file path"), MEMBER(Patch, old_oid, T_STRING, "old oid"), MEMBER(Patch, new_oid, T_STRING, "new oid"), - MEMBER(Patch, status, T_INT, "status"), + MEMBER(Patch, status, T_CHAR, "status"), MEMBER(Patch, similarity, T_INT, "similarity"), MEMBER(Patch, hunks, T_OBJECT, "hunks"), {NULL} diff --git a/src/pygit2.c b/src/pygit2.c index 71a543705..0533b2aca 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -329,15 +329,6 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED) /* --break-rewrites=/M */ ADD_CONSTANT_INT(m, GIT_DIFF_FIND_AND_BREAK_REWRITES) - /* Flags for diff deltas */ - ADD_CONSTANT_INT(m, GIT_DELTA_UNMODIFIED) - ADD_CONSTANT_INT(m, GIT_DELTA_ADDED) - ADD_CONSTANT_INT(m, GIT_DELTA_DELETED) - ADD_CONSTANT_INT(m, GIT_DELTA_MODIFIED) - ADD_CONSTANT_INT(m, GIT_DELTA_RENAMED) - ADD_CONSTANT_INT(m, GIT_DELTA_COPIED) - ADD_CONSTANT_INT(m, GIT_DELTA_IGNORED) - ADD_CONSTANT_INT(m, GIT_DELTA_UNTRACKED) /* Config */ INIT_TYPE(ConfigType, NULL, PyType_GenericNew) diff --git a/src/types.h b/src/types.h index 05c068e3b..a1a01052e 100644 --- a/src/types.h +++ b/src/types.h @@ -111,7 +111,7 @@ typedef struct { const char * new_file_path; char* old_oid; char* new_oid; - unsigned status; + char status; unsigned similarity; } Patch; diff --git a/test/test_diff.py b/test/test_diff.py index 3074f8d72..37fcc7bee 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -219,9 +219,9 @@ def test_find_similar(self): #~ Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate #~ --find-copies-harder during rename transformion... diff = commit_a.tree.diff(commit_b.tree, GIT_DIFF_INCLUDE_UNMODIFIED) - self.assertAll(lambda x: x.status is not pygit2.GIT_DELTA_RENAMED, diff) + self.assertAll(lambda x: x.status != 'R', diff) diff.find_similar() - self.assertAny(lambda x: x.status is pygit2.GIT_DELTA_RENAMED, diff) + self.assertAny(lambda x: x.status == 'R', diff) if __name__ == '__main__': unittest.main() From b7b9728115b5a054bbc908dedfff5208e6957348 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 6 May 2013 14:41:08 +0200 Subject: [PATCH 0489/2237] support for diffing the empty tree added missing support for diffing tree-to-tree with NULL as argument. (see issue #222) --- src/tree.c | 42 +++++++++++++++++++++--------------------- test/test_diff.py | 6 ++++++ 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/tree.c b/src/tree.c index 648b29d66..f6a01cf1b 100644 --- a/src/tree.c +++ b/src/tree.c @@ -286,38 +286,38 @@ PyDoc_STRVAR(Tree_diff__doc__, " TODO"); PyObject * -Tree_diff(Tree *self, PyObject *args) +Tree_diff(Tree *self, PyObject *args, PyObject *kwds) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; - int err; + git_tree* tree = NULL; + git_index* index; + git_repository* repo; + int err, empty_tree = 0; + char *keywords[] = {"obj", "flags", "empty_tree", NULL}; Diff *py_diff; PyObject *py_obj = NULL; - if (!PyArg_ParseTuple(args, "|Oi", &py_obj, &opts.flags)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oii", keywords, + &py_obj, &opts.flags, &empty_tree)) return NULL; + repo = self->repo->repo; if (py_obj == NULL) { - err = git_diff_tree_to_workdir( - &diff, - self->repo->repo, - self->tree, - &opts); + if (empty_tree > 0) + err = git_diff_tree_to_tree(&diff, repo, self->tree, NULL, &opts); + else + err = git_diff_tree_to_workdir(&diff, repo, self->tree, &opts); + } else if (PyObject_TypeCheck(py_obj, &TreeType)) { - err = git_diff_tree_to_tree( - &diff, - self->repo->repo, - self->tree, - ((Tree *)py_obj)->tree, - &opts); + tree = ((Tree *)py_obj)->tree; + err = git_diff_tree_to_tree(&diff, repo, self->tree, tree, &opts); + } else if (PyObject_TypeCheck(py_obj, &IndexType)) { - err = git_diff_tree_to_index( - &diff, - self->repo->repo, - self->tree, - ((Index *)py_obj)->index, - &opts); + index = ((Index *)py_obj)->index; + err = git_diff_tree_to_index(&diff, repo, self->tree, index, &opts); + } else { PyErr_SetObject(PyExc_TypeError, py_obj); return NULL; @@ -355,7 +355,7 @@ PyMappingMethods Tree_as_mapping = { }; PyMethodDef Tree_methods[] = { - METHOD(Tree, diff, METH_VARARGS), + METHOD(Tree, diff, METH_VARARGS | METH_KEYWORDS), {NULL} }; diff --git a/test/test_diff.py b/test/test_diff.py index 71e15cda6..c2f514e7c 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -142,6 +142,12 @@ def test_diff_tree(self): self.assertEqual(patch.old_file_path, 'a') self.assertEqual(patch.new_file_path, 'a') + def test_diff_empty_tree(self): + commit_a = self.repo[COMMIT_SHA1_1] + diff = commit_a.tree.diff(empty_tree=True) + entries = [p.new_file_path for p in diff] + self.assertAll(lambda x: commit_a.tree[x], entries) + def test_diff_tree_opts(self): commit_c = self.repo[COMMIT_SHA1_3] commit_d = self.repo[COMMIT_SHA1_4] From b9c3340c99d0fa035054de7c0a49a0261a60369e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 6 May 2013 19:18:24 +0200 Subject: [PATCH 0490/2237] Fix py_str_to_git_oid (closes #227) --- src/oid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oid.c b/src/oid.c index 8a32b20f7..b9a50b842 100644 --- a/src/oid.c +++ b/src/oid.c @@ -103,7 +103,7 @@ py_str_to_git_oid(PyObject *py_oid, git_oid *oid) /* Oid */ if (PyObject_TypeCheck(py_oid, (PyTypeObject*)&OidType)) { git_oid_cpy(oid, &((Oid*)py_oid)->oid); - return GIT_OID_RAWSZ; + return GIT_OID_HEXSZ; } /* Hex */ From d6c1d49ef68f108368c597ea811f708cca5c3f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 6 May 2013 23:16:50 +0200 Subject: [PATCH 0491/2237] Use git_{object,tree}_owner (#228) Note that libgit2's git_commit_owner is missing from the public interface, so we cannot use it. --- src/commit.c | 26 +++++++++++++++++++------- src/object.c | 5 +++-- src/repository.c | 14 -------------- src/tree.c | 4 ++-- src/types.h | 2 -- 5 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/commit.c b/src/commit.c index d87ec576b..b8e8fbfe8 100644 --- a/src/commit.c +++ b/src/commit.c @@ -31,6 +31,7 @@ #include "utils.h" #include "signature.h" #include "commit.h" +#include "object.h" extern PyTypeObject TreeType; @@ -149,32 +150,43 @@ Commit_tree__get__(Commit *commit) PyDoc_STRVAR(Commit_parents__doc__, "The list of parent commits."); PyObject * -Commit_parents__get__(Commit *commit) +Commit_parents__get__(Commit *self) { + git_repository *repo; unsigned int i, parent_count; const git_oid *parent_oid; - PyObject *obj; + git_commit *parent; + int err; + PyObject *py_parent; PyObject *list; - parent_count = git_commit_parentcount(commit->commit); + parent_count = git_commit_parentcount(self->commit); list = PyList_New(parent_count); if (!list) return NULL; + repo = git_object_owner((git_object*)self->commit); for (i=0; i < parent_count; i++) { - parent_oid = git_commit_parent_id(commit->commit, i); + parent_oid = git_commit_parent_id(self->commit, i); if (parent_oid == NULL) { Py_DECREF(list); Error_set(GIT_ENOTFOUND); return NULL; } - obj = lookup_object(commit->repo, parent_oid, GIT_OBJ_COMMIT); - if (obj == NULL) { + + err = git_commit_lookup(&parent, repo, parent_oid); + if (err < 0) { + Py_DECREF(list); + return Error_set_oid(err, parent_oid, GIT_OID_HEXSZ); + } + + py_parent = wrap_object((git_object*)parent, self->repo); + if (py_parent == NULL) { Py_DECREF(list); return NULL; } - PyList_SET_ITEM(list, i, obj); + PyList_SET_ITEM(list, i, py_parent); } return list; diff --git a/src/object.c b/src/object.c index f5f9c4fe9..67bff887a 100644 --- a/src/object.c +++ b/src/object.c @@ -96,14 +96,15 @@ PyDoc_STRVAR(Object_read_raw__doc__, PyObject * Object_read_raw(Object *self) { + git_repository *repo; const git_oid *oid; git_odb_object *obj; PyObject *aux; + repo = git_object_owner(self->obj); oid = git_object_id(self->obj); - assert(oid); - obj = Repository_read_raw(self->repo->repo, oid, GIT_OID_HEXSZ); + obj = Repository_read_raw(repo, oid, GIT_OID_HEXSZ); if (obj == NULL) return NULL; diff --git a/src/repository.c b/src/repository.c index 5b81e5d2c..eb9e2f646 100644 --- a/src/repository.c +++ b/src/repository.c @@ -65,20 +65,6 @@ int_to_loose_object_type(int type_id) } } -PyObject * -lookup_object(Repository *repo, const git_oid *oid, git_otype type) -{ - int err; - git_object *obj; - - err = git_object_lookup_prefix(&obj, repo->repo, oid, GIT_OID_HEXSZ, - type); - if (err < 0) - return Error_set_oid(err, oid, GIT_OID_HEXSZ); - - return wrap_object(obj, repo); -} - int Repository_init(Repository *self, PyObject *args, PyObject *kwds) { diff --git a/src/tree.c b/src/tree.c index f6a01cf1b..a3ac026f5 100644 --- a/src/tree.c +++ b/src/tree.c @@ -292,7 +292,7 @@ Tree_diff(Tree *self, PyObject *args, PyObject *kwds) git_diff_list *diff; git_tree* tree = NULL; git_index* index; - git_repository* repo; + git_repository *repo; int err, empty_tree = 0; char *keywords[] = {"obj", "flags", "empty_tree", NULL}; @@ -303,7 +303,7 @@ Tree_diff(Tree *self, PyObject *args, PyObject *kwds) &py_obj, &opts.flags, &empty_tree)) return NULL; - repo = self->repo->repo; + repo = git_tree_owner(self->tree); if (py_obj == NULL) { if (empty_tree > 0) err = git_diff_tree_to_tree(&diff, repo, self->tree, NULL, &opts); diff --git a/src/types.h b/src/types.h index a1a01052e..fdc451e95 100644 --- a/src/types.h +++ b/src/types.h @@ -193,6 +193,4 @@ typedef struct { SIMPLE_TYPE(Remote, git_remote, remote) -PyObject* lookup_object(Repository *repo, const git_oid *oid, git_otype type); - #endif From 2d812b671a9fb5f6a96842b1395ee26452b7636f Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 7 May 2013 13:23:17 +0200 Subject: [PATCH 0492/2237] fixed line_origin in Hunks every line in a hunk has a origin like '+','-' or ' '. --- src/diff.c | 17 +++++++++-------- src/types.h | 1 - test/test_diff.py | 9 ++++----- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/diff.c b/src/diff.c index 8e87c70cf..099420994 100644 --- a/src/diff.c +++ b/src/diff.c @@ -50,6 +50,7 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) git_diff_patch* patch = NULL; size_t i, j, hunk_amounts, lines_in_hunk, line_len, header_len; const char* line, *header; + char line_origin; int err; Hunk *py_hunk = NULL; Patch *py_patch = NULL; @@ -84,18 +85,19 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) py_hunk->new_start = range->new_start; py_hunk->new_lines = range->new_lines; - py_hunk->lines = PyList_New(lines_in_hunk + 1); - PyList_SetItem(py_hunk->lines, 0, - to_unicode_n(header, header_len, NULL, NULL)); - for (j=1; j < lines_in_hunk + 1; ++j) { - err = git_diff_patch_get_line_in_hunk(&py_hunk->origin, - &line, &line_len, NULL, NULL, patch, i, j - 1); + py_hunk->lines = PyList_New(lines_in_hunk); + for (j=0; j < lines_in_hunk; ++j) { + err = git_diff_patch_get_line_in_hunk(&line_origin, + &line, &line_len, NULL, NULL, patch, i, j); if (err < 0) goto cleanup; PyList_SetItem(py_hunk->lines, j, - to_unicode_n(line, line_len, NULL, NULL)); + Py_BuildValue("cO", line_origin, + to_unicode_n(line, line_len, NULL, NULL) + ) + ); } PyList_SetItem((PyObject*) py_patch->hunks, i, @@ -279,7 +281,6 @@ Hunk_dealloc(Hunk *self) } PyMemberDef Hunk_members[] = { - MEMBER(Hunk, origin, T_CHAR, "origin."), MEMBER(Hunk, old_start, T_INT, "Old start."), MEMBER(Hunk, old_lines, T_INT, "Old lines."), MEMBER(Hunk, new_start, T_INT, "New start."), diff --git a/src/types.h b/src/types.h index a1a01052e..3c559ead1 100644 --- a/src/types.h +++ b/src/types.h @@ -118,7 +118,6 @@ typedef struct { typedef struct { PyObject_HEAD PyObject* lines; - char origin; int old_start; int old_lines; int new_start; diff --git a/test/test_diff.py b/test/test_diff.py index 3246b5126..7dd5a0175 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -83,9 +83,8 @@ 'subdir/modified_file' ] -HUNK_EXPECTED = """@@ -1 +1 @@ -a contents 2 -a contents +HUNK_EXPECTED = """- a contents 2 ++ a contents """ class DiffDirtyTest(utils.DirtyRepoTestCase): @@ -134,7 +133,6 @@ def test_diff_tree(self): patch = diff[0] hunk = patch.hunks[0] - self.assertEqual(hunk.origin, '+') self.assertEqual(hunk.old_start, 1) self.assertEqual(hunk.old_lines, 1) self.assertEqual(hunk.new_start, 1) @@ -216,7 +214,8 @@ def test_hunk_content(self): commit_b = self.repo[COMMIT_SHA1_2] patch = commit_a.tree.diff(commit_b.tree)[0] hunk = patch.hunks[0] - self.assertEqual(HUNK_EXPECTED, ''.join(hunk.lines)) + lines = ('{0} {1}'.format(*x) for x in hunk.lines) + self.assertEqual(HUNK_EXPECTED, ''.join(lines)) def test_find_similar(self): commit_a = self.repo[COMMIT_SHA1_6] From ae6cd8a0ca378c3911dc86b949d90402bfdbe1be Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 7 May 2013 14:06:20 +0200 Subject: [PATCH 0493/2237] refactoring Tree.diff() into seperate methods * Tree.diff_to_tree() * Tree.diff_to_workdir() * Tree.diff-to_index() --- src/diff.c | 15 +++++++ src/diff.h | 2 + src/tree.c | 112 +++++++++++++++++++++++++++------------------- test/test_diff.py | 30 +++++++------ 4 files changed, 99 insertions(+), 60 deletions(-) diff --git a/src/diff.c b/src/diff.c index 099420994..fd78f4985 100644 --- a/src/diff.c +++ b/src/diff.c @@ -42,6 +42,21 @@ extern PyTypeObject HunkType; PyTypeObject PatchType; +PyObject* +wrap_diff(git_diff_list *diff, Repository *repo) +{ + Diff *py_diff; + + py_diff = PyObject_New(Diff, &DiffType); + if (py_diff) { + Py_INCREF(repo); + py_diff->repo = repo; + py_diff->list = diff; + } + + return (PyObject*) py_diff; +} + PyObject* diff_get_patch_byindex(git_diff_list* list, size_t idx) { diff --git a/src/diff.h b/src/diff.h index b04559349..75e633334 100644 --- a/src/diff.h +++ b/src/diff.h @@ -41,4 +41,6 @@ PyObject* Diff_changes(Diff *self); PyObject* Diff_patch(Diff *self); +PyObject* wrap_diff(git_diff_list *diff, Repository *repo); + #endif diff --git a/src/tree.c b/src/tree.c index f6a01cf1b..13309be56 100644 --- a/src/tree.c +++ b/src/tree.c @@ -33,6 +33,7 @@ #include "repository.h" #include "oid.h" #include "tree.h" +#include "diff.h" extern PyTypeObject TreeType; extern PyTypeObject DiffType; @@ -270,70 +271,87 @@ Tree_getitem(Tree *self, PyObject *value) } -PyDoc_STRVAR(Tree_diff__doc__, - "diff([obj, flags]) -> Diff\n" - "\n" - "Get changes between current tree instance with another tree, an index or\n" - "the working dir.\n" - "\n" - "Arguments:\n" - "\n" - "obj\n" - " If not given compare diff against working dir. Possible valid\n" - " arguments are instances of Tree or Index.\n" - "\n" - "flags\n" - " TODO"); +PyDoc_STRVAR(Tree_diff_to_workdir__doc__, "\n"); PyObject * -Tree_diff(Tree *self, PyObject *args, PyObject *kwds) +Tree_diff_to_workdir(Tree *self, PyObject *args) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; - git_tree* tree = NULL; - git_index* index; git_repository* repo; - int err, empty_tree = 0; - char *keywords[] = {"obj", "flags", "empty_tree", NULL}; + int err; Diff *py_diff; PyObject *py_obj = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oii", keywords, - &py_obj, &opts.flags, &empty_tree)) + if (!PyArg_ParseTuple(args, "|i", &opts.flags)) return NULL; repo = self->repo->repo; - if (py_obj == NULL) { - if (empty_tree > 0) - err = git_diff_tree_to_tree(&diff, repo, self->tree, NULL, &opts); - else - err = git_diff_tree_to_workdir(&diff, repo, self->tree, &opts); - - } else if (PyObject_TypeCheck(py_obj, &TreeType)) { - tree = ((Tree *)py_obj)->tree; - err = git_diff_tree_to_tree(&diff, repo, self->tree, tree, &opts); - - } else if (PyObject_TypeCheck(py_obj, &IndexType)) { - index = ((Index *)py_obj)->index; - err = git_diff_tree_to_index(&diff, repo, self->tree, index, &opts); - - } else { - PyErr_SetObject(PyExc_TypeError, py_obj); + err = git_diff_tree_to_workdir(&diff, repo, self->tree, &opts); + + if (err < 0) + return Error_set(err); + + return wrap_diff(diff, self->repo); +} + + +PyDoc_STRVAR(Tree_diff_to_index__doc__, "\n"); + +PyObject * +Tree_diff_to_index(Tree *self, PyObject *args) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_list *diff; + git_index* index; + git_repository* repo; + int err; + char *keywords[] = {"obj", "flags", NULL}; + + Diff *py_diff; + Index *py_idx = NULL; + + if (!PyArg_ParseTuple(args, "O!|i", &IndexType, &py_idx, &opts.flags)) return NULL; - } + + repo = self->repo->repo; + err = git_diff_tree_to_index(&diff, repo, self->tree, py_idx->index, &opts); if (err < 0) return Error_set(err); - py_diff = PyObject_New(Diff, &DiffType); - if (py_diff) { - Py_INCREF(self->repo); - py_diff->repo = self->repo; - py_diff->list = diff; - } + return wrap_diff(diff, self->repo); +} + + +PyDoc_STRVAR(Tree_diff_to_tree__doc__, "\n"); + +PyObject * +Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_list *diff; + git_tree* tree; + git_repository* repo; + int err; + char *keywords[] = {"obj", "flags", NULL}; + + Diff *py_diff; + Tree *py_tree = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!i", keywords, + &TreeType, &py_tree, &opts.flags)) + return NULL; + + repo = self->repo->repo; + tree = (py_tree == NULL) ? NULL : py_tree->tree; + err = git_diff_tree_to_tree(&diff, repo, self->tree, tree, &opts); + + if (err < 0) + return Error_set(err); - return (PyObject*)py_diff; + return wrap_diff(diff, self->repo); } @@ -355,7 +373,9 @@ PyMappingMethods Tree_as_mapping = { }; PyMethodDef Tree_methods[] = { - METHOD(Tree, diff, METH_VARARGS | METH_KEYWORDS), + METHOD(Tree, diff_to_tree, METH_VARARGS | METH_KEYWORDS), + METHOD(Tree, diff_to_workdir, METH_VARARGS), + METHOD(Tree, diff_to_index, METH_VARARGS), {NULL} }; diff --git a/test/test_diff.py b/test/test_diff.py index 7dd5a0175..d68d7ed8b 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -91,7 +91,7 @@ class DiffDirtyTest(utils.DirtyRepoTestCase): def test_diff_empty_index(self): repo = self.repo head = repo[repo.lookup_reference('HEAD').resolve().target] - diff = head.tree.diff(repo.index) + diff = head.tree.diff_to_index(repo.index) files = [patch.new_file_path for patch in diff] self.assertEqual(DIFF_INDEX_EXPECTED, files) @@ -99,7 +99,7 @@ def test_diff_empty_index(self): def test_workdir_to_tree(self): repo = self.repo head = repo[repo.lookup_reference('HEAD').resolve().target] - diff = head.tree.diff() + diff = head.tree.diff_to_workdir() files = [patch.new_file_path for patch in diff] self.assertEqual(DIFF_WORKDIR_EXPECTED, files) @@ -110,12 +110,13 @@ class DiffTest(utils.BareRepoTestCase): def test_diff_invalid(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] - self.assertRaises(TypeError, commit_a.tree.diff, commit_b) + self.assertRaises(TypeError, commit_a.tree.diff_to_tree, commit_b) + self.assertRaises(TypeError, commit_a.tree.diff_to_index, commit_b) def test_diff_empty_index(self): repo = self.repo head = repo[repo.lookup_reference('HEAD').resolve().target] - diff = head.tree.diff(repo.index) + diff = head.tree.diff_to_index(repo.index) files = [patch.new_file_path.split('/')[0] for patch in diff] self.assertEqual([x.name for x in head.tree], files) @@ -124,7 +125,7 @@ def test_diff_tree(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] - diff = commit_a.tree.diff(commit_b.tree) + diff = commit_a.tree.diff_to_tree(commit_b.tree) # self.assertIsNotNone is 2.7 only self.assertTrue(diff is not None) @@ -143,7 +144,7 @@ def test_diff_tree(self): def test_diff_empty_tree(self): commit_a = self.repo[COMMIT_SHA1_1] - diff = commit_a.tree.diff(empty_tree=True) + diff = commit_a.tree.diff_to_tree() entries = [p.new_file_path for p in diff] self.assertAll(lambda x: commit_a.tree[x], entries) @@ -153,11 +154,11 @@ def test_diff_tree_opts(self): for opt in [pygit2.GIT_DIFF_IGNORE_WHITESPACE, pygit2.GIT_DIFF_IGNORE_WHITESPACE_EOL]: - diff = commit_c.tree.diff(commit_d.tree, opt) + diff = commit_c.tree.diff_to_tree(commit_d.tree, opt) self.assertTrue(diff is not None) self.assertEqual(0, len(diff[0].hunks)) - diff = commit_c.tree.diff(commit_d.tree) + diff = commit_c.tree.diff_to_tree(commit_d.tree) self.assertTrue(diff is not None) self.assertEqual(1, len(diff[0].hunks)) @@ -166,11 +167,11 @@ def test_diff_merge(self): commit_b = self.repo[COMMIT_SHA1_2] commit_c = self.repo[COMMIT_SHA1_3] - diff_b = commit_a.tree.diff(commit_b.tree) + diff_b = commit_a.tree.diff_to_tree(commit_b.tree) # self.assertIsNotNone is 2.7 only self.assertTrue(diff_b is not None) - diff_c = commit_b.tree.diff(commit_c.tree) + diff_c = commit_b.tree.diff_to_tree(commit_c.tree) # self.assertIsNotNone is 2.7 only self.assertTrue(diff_c is not None) @@ -197,13 +198,13 @@ def test_diff_patch(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] - diff = commit_a.tree.diff(commit_b.tree) + diff = commit_a.tree.diff_to_tree(commit_b.tree) self.assertEqual(diff.patch, PATCH) def test_diff_oids(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] - patch = commit_a.tree.diff(commit_b.tree)[0] + patch = commit_a.tree.diff_to_tree(commit_b.tree)[0] self.assertEqual(patch.old_oid, '7f129fd57e31e935c6d60a0c794efe4e6927664b') self.assertEqual(patch.new_oid, @@ -212,7 +213,7 @@ def test_diff_oids(self): def test_hunk_content(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] - patch = commit_a.tree.diff(commit_b.tree)[0] + patch = commit_a.tree.diff_to_tree(commit_b.tree)[0] hunk = patch.hunks[0] lines = ('{0} {1}'.format(*x) for x in hunk.lines) self.assertEqual(HUNK_EXPECTED, ''.join(lines)) @@ -223,7 +224,8 @@ def test_find_similar(self): #~ Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate #~ --find-copies-harder during rename transformion... - diff = commit_a.tree.diff(commit_b.tree, GIT_DIFF_INCLUDE_UNMODIFIED) + diff = commit_a.tree.diff_to_tree(commit_b.tree, + GIT_DIFF_INCLUDE_UNMODIFIED) self.assertAll(lambda x: x.status != 'R', diff) diff.find_similar() self.assertAny(lambda x: x.status == 'R', diff) From 101715bf37440d32291bde4f58c3142bcf7d8adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 9 May 2013 14:13:30 +0200 Subject: [PATCH 0494/2237] docs: review, in progress --- docs/general.rst | 24 ++++ docs/index.rst | 15 +-- docs/{errors.rst => merge.rst} | 7 +- docs/objects.rst | 216 ++++++++++++++------------------- docs/oid.rst | 84 +++++++++++++ docs/repository.rst | 39 +++--- docs/utils.rst | 14 --- pygit2/__init__.py | 11 +- src/object.c | 11 +- src/oid.c | 4 +- src/pygit2.c | 16 +-- src/repository.c | 10 +- 12 files changed, 256 insertions(+), 195 deletions(-) create mode 100644 docs/general.rst rename docs/{errors.rst => merge.rst} (59%) create mode 100644 docs/oid.rst delete mode 100644 docs/utils.rst diff --git a/docs/general.rst b/docs/general.rst new file mode 100644 index 000000000..bf0b3c164 --- /dev/null +++ b/docs/general.rst @@ -0,0 +1,24 @@ +********************************************************************** +General +********************************************************************** + +.. contents:: Contents + :local: + + +Constants +========= + +.. py:data:: LIBGIT2_VER_MAJOR +.. py:data:: LIBGIT2_VER_MINOR +.. py:data:: LIBGIT2_VER_REVISION +.. py:data:: LIBGIT2_VER_VERSION + + +Errors +====== + +.. autoexception:: pygit2.GitError + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/index.rst b/docs/index.rst index daba596a4..dcd3ed63c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -22,32 +22,27 @@ Pygit2 links: Start: .. toctree:: - :maxdepth: 2 + :maxdepth: 1 install Usage guide: .. toctree:: - :maxdepth: 2 + :maxdepth: 1 + general repository + oid objects references revparse log working-copy diff + merge config remotes - errors - -More: - -.. toctree:: - :maxdepth: 1 - - utils Indices and tables diff --git a/docs/errors.rst b/docs/merge.rst similarity index 59% rename from docs/errors.rst rename to docs/merge.rst index 6a3715a2e..55e411ac1 100644 --- a/docs/errors.rst +++ b/docs/merge.rst @@ -1,8 +1,5 @@ ********************************************************************** -Errors +Merge ********************************************************************** -.. autoexception:: pygit2.GitError - :members: - :show-inheritance: - :undoc-members: +.. automethod:: pygit2.Repository.merge_base diff --git a/docs/objects.rst b/docs/objects.rst index dd849bb76..a277a3ad9 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -2,91 +2,46 @@ Git objects ********************************************************************** +There are four types of Git objects: blobs, trees, commits and tags. For each +one pygit2 has a type, and all four types inherit from the base ``Object`` +type. + + .. contents:: Contents :local: -In the first place Git is a key-value storage system. The keys are called -OIDs, for Object id, and the values stored are called Objects. -Oids +Objects ================= -The oid is the `SHA-1 `_ hash of an -object. It is 20 bytes long: - -- When we represent an oid as a 20 bytes Python string, we say it is a raw - oid. - -- When we represent an oid as a 40 chars Python string, we sayt it is a hex - oid. +The Object type is a base type, it is not possible to make instances of it, in +any way. -However, most of the time we will use the Oid type. We can explicetly create -an Oid object from its raw or hexadecimal form:: +It is the base type of the ``Blob``, ``Tree``, ``Commit`` and ``Tag`` types, so +it is possible to check whether a Python value is an Object or not:: - >>> hex = "cff3ceaefc955f0dbe1957017db181bc49913781" - >>> oid1 = Oid(hex=hex) - - >>> from binascii import unhexlify - >>> raw = unhexlify(hex) - >>> oid2 = Oid(raw=raw) - - >>> print oid1 == oid2 + >>> from pygit2 import Object + >>> commit = repository.revparse_single('HEAD') + >>> print isinstance(commit, Object) True -And in the opposite direction, we can get the raw or hexadecimal form from -an Oid object: - -.. autoattribute:: pygit2.Oid.raw -.. autoattribute:: pygit2.Oid.hex - -The Oid type supports: - -- rich comparisons, not just for equality, also: lesser-than, lesser-or-equal, - etc. - -- hashing, so Oid objects can be used as keys in a dictionary - - -Python 2 and Python 3 ---------------------- - -There is a difference on how the library handles hex oids, depending on -whether we are using Python 2 or 3. - -- In Python 2, we can represent an hexadecimal oid using a bytes string - (``str``) or a text string (``unicode``) - -- In Python 3, hexadecimal oids can only be represented using unicode - strings. - +All Objects are immutable, they cannot be modified once they are created:: -Objects -================= - -There are four types (commits, trees, blobs and tags), for each type pygit2 -has a Python class:: - - >>> # Show commits and trees - >>> commit - - >>> commit.tree - - -These four classes (``Commit``, ``Tree``, ``Blob`` and ``Tag``) inherit from -the ``Object`` base class, which provides shared behaviour. A Git object is -identified by a unique *object id*, which is a binary byte string; this is -often represented as an hexadecimal text string:: + >>> commit.message = u"foobar" + Traceback (most recent call last): + File "", line 1, in + AttributeError: attribute 'message' of '_pygit2.Commit' objects is not writable - >>> commit.oid - b'x\xde\xb5W\x8d\x01<\xdb\xdf\x08o\xa1\xd1\xa3\xe7\xd9\x82\xe8\x88\x8f' - >>> commit.hex - '78deb5578d013cdbdf086fa1d1a3e7d982e8888f' +Derived types (blobs, trees, etc.) don't have a constructor, this means they +cannot be created with the common idiom:: -The API of pygit2 accepts both the raw object id and its hexadecimal -representation, the difference is done based on its type (a byte or a text -string). + >>> from pygit2 import Blob + >>> blob = Blob("data") + Traceback (most recent call last): + File "", line 1, in + TypeError: cannot create '_pygit2.Blob' instances -Objects can not be modified once they have been created. +New objects are created using an specific API we will see later. This is the common interface for all Git objects: @@ -96,55 +51,38 @@ This is the common interface for all Git objects: .. automethod:: pygit2.Object.read_raw -Commits -================= -A commit is a snapshot of the working dir with meta informations like author, -committer and others. -.. autoattribute:: pygit2.Commit.author -.. autoattribute:: pygit2.Commit.committer -.. autoattribute:: pygit2.Commit.message -.. autoattribute:: pygit2.Commit.message_encoding -.. autoattribute:: pygit2.Commit.tree -.. autoattribute:: pygit2.Commit.parents -.. autoattribute:: pygit2.Commit.commit_time -.. autoattribute:: pygit2.Commit.commit_time_offset -Signatures -------------- - -The author and committer attributes of commit objects are ``Signature`` -objects:: - >>> commit.author - +Blobs +================= -.. autoattribute:: pygit2.Signature.name -.. autoattribute:: pygit2.Signature.email -.. autoattribute:: pygit2.Signature.time -.. autoattribute:: pygit2.Signature.offset +A blob is equivalent to a file in a file system.:: + >>> # create a blob out of memory + >>> oid = repo.create_blob('foo bar') + >>> blob = repo[oid] + >>> blob.data + 'foo bar' + >>> oid + '\x96\xc9\x06um{\x91\xc4S"a|\x92\x95\xe4\xa8\rR\xd1\xc5' -Creating commits ----------------- +.. autoattribute:: pygit2.Blob.data +.. autoattribute:: pygit2.Blob.size -.. automethod:: pygit2.Repository.create_commit +To create new blobs use the Repository API: -Commits can be created by calling the ``create_commit`` method of the -repository with the following parameters:: +.. automethod:: pygit2.Repository.create_blob +.. automethod:: pygit2.Repository.create_blob_fromworkdir +.. automethod:: pygit2.Repository.create_blob_fromdisk - >>> author = Signature('Alice Author', 'alice@authors.tld') - >>> committer = Signature('Cecil Committer', 'cecil@committers.tld') - >>> tree = repo.TreeBuilder().write() - >>> repo.create_commit( - ... 'refs/heads/master', # the name of the reference to update - ... author, committer, 'one line commit message\n\ndetailed commit message', - ... tree, # binary string representing the tree object ID - ... [] # list of binary strings representing parents of the new commit - ... ) - '#\xe4>> # create a blob out of memory - >>> oid = repo.create_blob('foo bar') - >>> blob = repo[oid] - >>> blob.data - 'foo bar' - >>> oid - '\x96\xc9\x06um{\x91\xc4S"a|\x92\x95\xe4\xa8\rR\xd1\xc5' +.. autoattribute:: pygit2.Commit.author +.. autoattribute:: pygit2.Commit.committer +.. autoattribute:: pygit2.Commit.message +.. autoattribute:: pygit2.Commit.message_encoding +.. autoattribute:: pygit2.Commit.tree +.. autoattribute:: pygit2.Commit.parents +.. autoattribute:: pygit2.Commit.commit_time +.. autoattribute:: pygit2.Commit.commit_time_offset -.. autoattribute:: pygit2.Blob.data -.. autoattribute:: pygit2.Blob.size -Creating blobs --------------------- +Signatures +------------- + +The author and committer attributes of commit objects are ``Signature`` +objects:: + + >>> commit.author + + +.. autoattribute:: pygit2.Signature.name +.. autoattribute:: pygit2.Signature.email +.. autoattribute:: pygit2.Signature.time +.. autoattribute:: pygit2.Signature.offset + + +Creating commits +---------------- + +.. automethod:: pygit2.Repository.create_commit + +Commits can be created by calling the ``create_commit`` method of the +repository with the following parameters:: + + >>> author = Signature('Alice Author', 'alice@authors.tld') + >>> committer = Signature('Cecil Committer', 'cecil@committers.tld') + >>> tree = repo.TreeBuilder().write() + >>> repo.create_commit( + ... 'refs/heads/master', # the name of the reference to update + ... author, committer, 'one line commit message\n\ndetailed commit message', + ... tree, # binary string representing the tree object ID + ... [] # list of binary strings representing parents of the new commit + ... ) + '#\xe4`_ hash of an +object. It is 20 bytes long. + +These are the three forms of an oid in pygit2: + +Raw oid + A raw oid is represented as a Python byte string of 20 bytes length. + This form can only be used to create an Oid object. + +Hex oid + A hex oid is represented as a Python string of 40 hexadecimal chars. This + form can be used to create Oid objects, just like raw oids. Also, the pygit2 + API directly accepts hex oids everywhere. + + .. note:: + + In Python 3 hexadecimal oids are represented using the ``str`` type. + In Python 2 both ``str`` and ``unicode`` are accepted. + +Oid object + An ``Oid`` object can be built from the raw or hexadecimal representations + (see below). The pygit2 API always returns, and accepts, ``Oid`` objects. + + This is the preferred way to represent an Oid, with the hexadecimal form + being used for interaction with the user. + + +The Oid type +============ + +.. c:type:: pygit2.Oid(raw=None, hex=None) + + The constructor expects either a raw or a hex oid, but not both. + + An Oid object is created from the hexadecimal form this way:: + + >>> from pygit2 import Oid + + >>> hex = "cff3ceaefc955f0dbe1957017db181bc49913781" + >>> oid1 = Oid(hex=hex) + + An Oid object is created from the raw form this way:: + + >>> from binascii import unhexlify + >>> from pygit2 import Oid + + >>> raw = unhexlify("cff3ceaefc955f0dbe1957017db181bc49913781") + >>> oid2 = Oid(raw=raw) + +An the other way around, from an Oid object we can get the hexadecimal and raw +forms. + +.. autoattribute:: pygit2.Oid.hex +.. autoattribute:: pygit2.Oid.raw + +The Oid type supports: + +- rich comparisons, not just for equality, also: lesser-than, lesser-or-equal, + etc. + +- hashing, so Oid objects can be used as keys in a dictionary. + + +Constants +========= + +.. py:data:: GIT_OID_RAWSZ +.. py:data:: GIT_OID_HEXSZ +.. py:data:: GIT_OID_HEX_ZERO +.. py:data:: GIT_OID_MINPREFIXLEN diff --git a/docs/repository.rst b/docs/repository.rst index e04ca2d61..2f34c147e 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -5,35 +5,41 @@ The repository Everything starts either by creating a new repository, or by opening an existing one. +.. contents:: Contents + :local: + Creating a repository =================================== .. autofunction:: pygit2.init_repository -This is how to create non-bare repository:: +Example:: >>> from pygit2 import init_repository - >>> repo = init_repository('test') + >>> repo = init_repository('test') # Creates a non-bare repository + >>> repo = init_repository('test', bare=True) # Creates a bare repository -And this is how to create a bare repository:: - >>> from pygit2 import init_repository - >>> repo = init_repository('test', bare=True) +The Repository class +=================================== -But one can also do:: +.. py:class:: pygit2.Repository(path) - >>> from pygit2 import init_repository - >>> repo = init_repository('test', True) + The Repository constructor only takes one argument, the path of the + repository to open. + Example:: -The Repository class -=================================== + >>> from pygit2 import Repository + >>> repo = Repository('pygit2/.git') -To open an existing repository:: +The API of the Repository class is quite large. Since this documentation is +orgaized by features, the related bits are explained in the related chapters, +for instance the :py:meth:`pygit2.Repository.checkout` method are explained in +the Checkout section. - >>> from pygit2 import Repository - >>> repo = Repository('pygit2/.git') +Below there are some general attributes and methods: .. autoattribute:: pygit2.Repository.path .. autoattribute:: pygit2.Repository.workdir @@ -41,4 +47,9 @@ To open an existing repository:: .. autoattribute:: pygit2.Repository.is_empty .. automethod:: pygit2.Repository.read .. automethod:: pygit2.Repository.write -.. automethod:: pygit2.Repository.merge_base + + +Utilities +========= + +.. autofunction:: pygit2.discover_repository diff --git a/docs/utils.rst b/docs/utils.rst deleted file mode 100644 index c68301228..000000000 --- a/docs/utils.rst +++ /dev/null @@ -1,14 +0,0 @@ -********************************************************************** -Utilities -********************************************************************** - -.. autofunction:: pygit2.discover_repository - -.. autofunction:: pygit2.hash - -.. autofunction:: pygit2.hashfile - -.. automodule:: pygit2.utils - :members: - :show-inheritance: - :undoc-members: diff --git a/pygit2/__init__.py b/pygit2/__init__.py index fbaae96cb..aeb7fe5be 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -40,15 +40,10 @@ def init_repository(path, bare=False): """ - Creates a new Git repository in the given path. + Creates a new Git repository in the given *path*. - Arguments: - - path - Path where to create the repository. - - bare - Whether the repository will be bare or not. + If *bare* is True the repository will be bare, i.e. it will not have a + working copy. """ _pygit2.init_repository(path, bare) return Repository(path) diff --git a/src/object.c b/src/object.c index 67bff887a..e602da185 100644 --- a/src/object.c +++ b/src/object.c @@ -50,7 +50,7 @@ Object_dealloc(Object* self) PyDoc_STRVAR(Object_oid__doc__, - "The object id, a byte string 20 bytes long."); + "The object id, an instance of the Oid type."); PyObject * Object_oid__get__(Object *self) @@ -65,7 +65,8 @@ Object_oid__get__(Object *self) PyDoc_STRVAR(Object_hex__doc__, - "Hexadecimal representation of the object id, a text string 40 chars long."); + "Hexadecimal representation of the object id. This is a shortcut for\n" + "Object.oid.hex"); PyObject * Object_hex__get__(Object *self) @@ -80,8 +81,8 @@ Object_hex__get__(Object *self) PyDoc_STRVAR(Object_type__doc__, - "One of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_TAG\n" - "constants."); + "One of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_TAG\n" + "constants."); PyObject * Object_type__get__(Object *self) @@ -91,6 +92,8 @@ Object_type__get__(Object *self) PyDoc_STRVAR(Object_read_raw__doc__, + "read_raw()\n" + "\n" "Returns the byte string with the raw contents of the of the object."); PyObject * diff --git a/src/oid.c b/src/oid.c index b9a50b842..d1751ceb7 100644 --- a/src/oid.c +++ b/src/oid.c @@ -253,7 +253,7 @@ Oid_richcompare(PyObject *o1, PyObject *o2, int op) } -PyDoc_STRVAR(Oid_raw__doc__, "Raw oid."); +PyDoc_STRVAR(Oid_raw__doc__, "Raw oid, a 20 bytes string."); PyObject * Oid_raw__get__(Oid *self) @@ -262,7 +262,7 @@ Oid_raw__get__(Oid *self) } -PyDoc_STRVAR(Oid_hex__doc__, "Hex oid."); +PyDoc_STRVAR(Oid_hex__doc__, "Hex oid, a 40 chars long string (type str)."); PyObject * Oid_hex__get__(Oid *self) diff --git a/src/pygit2.c b/src/pygit2.c index 0533b2aca..c68e530d6 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -124,10 +124,10 @@ discover_repository(PyObject *self, PyObject *args) }; PyDoc_STRVAR(hashfile__doc__, - "hashfile(path) -> bytes\n" - "\n" - "Returns the oid of a new blob from a file path without actually writing \n" - "to the odb."); + "hashfile(path) -> Oid\n" + "\n" + "Returns the oid of a new blob from a file path without actually writing\n" + "to the odb."); PyObject * hashfile(PyObject *self, PyObject *args) { @@ -146,10 +146,10 @@ hashfile(PyObject *self, PyObject *args) } PyDoc_STRVAR(hash__doc__, - "hash(data) -> bytes\n" - "\n" - "Returns the oid of a new blob from a string without actually writing to \n" - "the odb."); + "hash(data) -> Oid\n" + "\n" + "Returns the oid of a new blob from a string without actually writing to\n" + "the odb."); PyObject * hash(PyObject *self, PyObject *args) { diff --git a/src/repository.c b/src/repository.c index eb9e2f646..fe63f6701 100644 --- a/src/repository.c +++ b/src/repository.c @@ -366,11 +366,11 @@ Repository_read(Repository *self, PyObject *py_hex) PyDoc_STRVAR(Repository_write__doc__, - "write(type, data) -> oid\n" - "\n" - "Write raw object data into the repository. First arg is the object type,\n" - "the second one a buffer with data. Return the object id (sha) of of the\n" - "created object."); + "write(type, data) -> Oid\n" + "\n" + "Write raw object data into the repository. First arg is the object\n" + "type, the second one a buffer with data. Return the Oid of the created\n" + "object."); PyObject * Repository_write(Repository *self, PyObject *args) From 8c76a14a99a2e840427e2199c42f9f6545e8b01b Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Thu, 9 May 2013 14:40:45 +0200 Subject: [PATCH 0495/2237] added reorder trees possibility for diff_to_tree() --- src/tree.c | 22 +++++++++++++++------- test/test_diff.py | 13 +++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/tree.c b/src/tree.c index 13309be56..4ffa5c998 100644 --- a/src/tree.c +++ b/src/tree.c @@ -332,21 +332,29 @@ Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; - git_tree* tree; + git_tree *from, *to, *tmp; git_repository* repo; - int err; - char *keywords[] = {"obj", "flags", NULL}; + int err, swap = 0; + char *keywords[] = {"obj", "flags", "swap", NULL}; Diff *py_diff; Tree *py_tree = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!i", keywords, - &TreeType, &py_tree, &opts.flags)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!ii", keywords, + &TreeType, &py_tree, &opts.flags, + &swap)) return NULL; repo = self->repo->repo; - tree = (py_tree == NULL) ? NULL : py_tree->tree; - err = git_diff_tree_to_tree(&diff, repo, self->tree, tree, &opts); + to = (py_tree == NULL) ? NULL : py_tree->tree; + from = self->tree; + if (swap > 0) { + tmp = from; + from = to; + to = tmp; + } + + err = git_diff_tree_to_tree(&diff, repo, from, to, &opts); if (err < 0) return Error_set(err); diff --git a/test/test_diff.py b/test/test_diff.py index d68d7ed8b..b3366240a 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -33,6 +33,7 @@ import pygit2 from pygit2 import GIT_DIFF_INCLUDE_UNMODIFIED from . import utils +from itertools import chain COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10' @@ -145,8 +146,20 @@ def test_diff_tree(self): def test_diff_empty_tree(self): commit_a = self.repo[COMMIT_SHA1_1] diff = commit_a.tree.diff_to_tree() + + def get_context_for_lines(diff): + hunks = chain(*map(lambda x: x.hunks, [p for p in diff])) + lines = chain(*map(lambda x: x.lines, hunks)) + return map(lambda x: x[0], lines) + entries = [p.new_file_path for p in diff] self.assertAll(lambda x: commit_a.tree[x], entries) + self.assertAll(lambda x: '-' == x, get_context_for_lines(diff)) + + diff_swaped = commit_a.tree.diff_to_tree(swap=True) + entries = [p.new_file_path for p in diff_swaped] + self.assertAll(lambda x: commit_a.tree[x], entries) + self.assertAll(lambda x: '+' == x, get_context_for_lines(diff_swaped)) def test_diff_tree_opts(self): commit_c = self.repo[COMMIT_SHA1_3] From 7b7bd5edb3080c7b6e47dd4b5bcd4498927a9459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 11 May 2013 08:18:16 +0200 Subject: [PATCH 0496/2237] docs: a little more --- docs/objects.rst | 86 ++++++++++++++++++++++++++++++++++++--------- docs/repository.rst | 19 +++++----- src/blob.c | 3 +- src/repository.c | 21 ++++++----- 4 files changed, 91 insertions(+), 38 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index a277a3ad9..6f778e9f8 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -1,5 +1,5 @@ ********************************************************************** -Git objects +Git Objects ********************************************************************** There are four types of Git objects: blobs, trees, commits and tags. For each @@ -11,9 +11,42 @@ type. :local: -Objects +Object lookup ================= +In the previous chapter we learnt about Object IDs. With an oid we can ask the +repository to get the associated object. To do that the ``Repository`` class +implementes a subset of the mapping interface. + +.. method:: Repository.get(oid, default=None) + + Return the Git object for the given *oid*, returns the *default* value if + there's no object in the repository with that oid. The oid can be an Oid + object, or an hexadecimal string. + + Example:: + + >>> from pygit2 import Repository + >>> repo = Repository('path/to/pygit2') + >>> obj = repo.get("101715bf37440d32291bde4f58c3142bcf7d8adb") + >>> obj + <_pygit2.Commit object at 0x7ff27a6b60f0> + +.. method:: Repository[oid] + + Return the Git object for the given oid, raise ``KeyError`` if there's no + object in the repository with that oid. The oid can be an Oid object, or + an hexadecimal string. + +.. method:: oid in Repository + + Returns True if there is an object in the Repository with that oid, False + if there is not. The oid can be an Oid object, or an hexadecimal string. + + +The Object base type +==================== + The Object type is a base type, it is not possible to make instances of it, in any way. @@ -51,40 +84,59 @@ This is the common interface for all Git objects: .. automethod:: pygit2.Object.read_raw +Blobs +================= + +A blob is just a raw byte string. They are the Git equivalent to files in +a filesytem. +This is their API: +.. autoattribute:: pygit2.Blob.data + Example, print the contents of the ``.gitignore`` file:: + >>> blob = repo["d8022420bf6db02e906175f64f66676df539f2fd"] + >>> print blob.data + MANIFEST + build + dist -Blobs -================= +.. autoattribute:: pygit2.Blob.size -A blob is equivalent to a file in a file system.:: + Example:: - >>> # create a blob out of memory - >>> oid = repo.create_blob('foo bar') - >>> blob = repo[oid] - >>> blob.data - 'foo bar' - >>> oid - '\x96\xc9\x06um{\x91\xc4S"a|\x92\x95\xe4\xa8\rR\xd1\xc5' + >>> print blob.size + 130 -.. autoattribute:: pygit2.Blob.data -.. autoattribute:: pygit2.Blob.size +Creating blobs +-------------- -To create new blobs use the Repository API: +There are a number of methods in the repository to create new blobs, and add +them to the Git object database: .. automethod:: pygit2.Repository.create_blob + + Example: + + >>> oid = repo.create_blob('foo bar') # Creates blob from bytes string + >>> blob = repo[oid] + >>> blob.data + 'foo bar' + .. automethod:: pygit2.Repository.create_blob_fromworkdir .. automethod:: pygit2.Repository.create_blob_fromdisk -It is also possible to get the oid for a blob without really adding it to -the Git object database: +There are also some functions to calculate the oid for a byte string without +creating the blob object: .. autofunction:: pygit2.hash .. autofunction:: pygit2.hashfile + + + Trees ================= diff --git a/docs/repository.rst b/docs/repository.rst index 2f34c147e..24f0674e3 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -9,16 +9,19 @@ existing one. :local: -Creating a repository +Functions =================================== .. autofunction:: pygit2.init_repository -Example:: + Example:: + + >>> from pygit2 import init_repository + >>> repo = init_repository('test') # Creates a non-bare repository + >>> repo = init_repository('test', bare=True) # Creates a bare repository + +.. autofunction:: pygit2.discover_repository - >>> from pygit2 import init_repository - >>> repo = init_repository('test') # Creates a non-bare repository - >>> repo = init_repository('test', bare=True) # Creates a bare repository The Repository class @@ -47,9 +50,3 @@ Below there are some general attributes and methods: .. autoattribute:: pygit2.Repository.is_empty .. automethod:: pygit2.Repository.read .. automethod:: pygit2.Repository.write - - -Utilities -========= - -.. autofunction:: pygit2.discover_repository diff --git a/src/blob.c b/src/blob.c index 289160ca5..465d8d651 100644 --- a/src/blob.c +++ b/src/blob.c @@ -42,7 +42,8 @@ Blob_size__get__(Blob *self) PyDoc_STRVAR(Blob_data__doc__, - "Raw data. This is the same as Blob.read_raw()"); + "The contents of the blob, a bytes string. This is the same as\n" + "Blob.read_raw()"); PyGetSetDef Blob_getseters[] = { GETTER(Blob, size), diff --git a/src/repository.c b/src/repository.c index fe63f6701..59c444357 100644 --- a/src/repository.c +++ b/src/repository.c @@ -592,9 +592,10 @@ Repository_walk(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_blob__doc__, - "create_blob(data) -> bytes\n" - "\n" - "Create a new blob from memory."); + "create_blob(data) -> Oid\n" + "\n" + "Create a new blob from a bytes string. The blob is added to the Git\n" + "object database. Returns the oid of the blob."); PyObject * Repository_create_blob(Repository *self, PyObject *args) @@ -616,9 +617,11 @@ Repository_create_blob(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_blob_fromworkdir__doc__, - "create_blob_fromworkdir(path) -> bytes\n" - "\n" - "Create a new blob from a file within the working directory (raise an error otherwise)."); + "create_blob_fromworkdir(path) -> Oid\n" + "\n" + "Create a new blob from a file within the working directory. The given\n" + "path must be relative to the working directory, if it is not an error\n" + "is raised."); PyObject * Repository_create_blob_fromworkdir(Repository *self, PyObject *args) @@ -639,9 +642,9 @@ Repository_create_blob_fromworkdir(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_blob_fromdisk__doc__, - "create_blob_fromdisk(path) -> bytes\n" - "\n" - "Create a new blob from a file anywhere (no working directory check)."); + "create_blob_fromdisk(path) -> Oid\n" + "\n" + "Create a new blob from a file anywhere (no working directory check)."); PyObject * Repository_create_blob_fromdisk(Repository *self, PyObject *args) From d44f7aa9a9f9528afde6280ceb6eff365aa3d09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 11 May 2013 15:22:28 +0200 Subject: [PATCH 0497/2237] docs: a little more --- docs/objects.rst | 54 +++++++++++++++++++++++++++++------------------ src/index.c | 2 +- src/repository.c | 13 ++++++------ src/treebuilder.c | 2 +- 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index 6f778e9f8..04f466e0e 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -134,24 +134,48 @@ creating the blob object: .. autofunction:: pygit2.hashfile - - - Trees ================= A tree is a sorted collection of tree entries. It is similar to a folder or directory in a file system. Each entry points to another tree or a blob. A tree can be iterated, and partially implements the sequence and mapping -interfaces:: +interfaces. + +.. method:: Tree[name] + + Return the TreeEntry object for the given *name*. Raise ``KeyError`` if + there is not a tree entry with that name. + +.. method:: name in Tree + + Return True if there is a tree entry with the given name, False otherwise. + +.. method:: len(Tree) + + Return the number of entries in the tree. + +.. method:: iter(Tree) + + Return an iterator over the entries of the tree. + +.. automethod:: pygit2.Tree.diff + +Tree entries +------------ + +.. autoattribute:: pygit2.TreeEntry.name +.. autoattribute:: pygit2.TreeEntry.oid +.. autoattribute:: pygit2.TreeEntry.hex +.. autoattribute:: pygit2.TreeEntry.filemode + +Example:: - >>> # Number of entries >>> tree = commit.tree - >>> len(tree) + >>> len(tree) # Number of entries 6 - >>> # Iteration - >>> for entry in tree: + >>> for entry in tree: # Iteration ... print(entry.hex, entry.name) ... 7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa .gitignore @@ -161,24 +185,14 @@ interfaces:: 85a67270a49ef16cdd3d328f06a3e4b459f09b27 setup.py 3d8985bbec338eb4d47c5b01b863ee89d044bd53 test - >>> # Get an entry by name - >>> entry = tree['pygit2.c'] + >>> entry = tree['pygit2.c'] # Get an entry by name >>> entry - >>> # Get the object the entry points to - >>> blob = repo[entry.oid] + >>> blob = repo[entry.oid] # Get the object the entry points to >>> blob -.. automethod:: pygit2.Tree.diff - -.. autoattribute:: pygit2.TreeEntry.name -.. autoattribute:: pygit2.TreeEntry.oid -.. autoattribute:: pygit2.TreeEntry.hex -.. autoattribute:: pygit2.TreeEntry.filemode - - Creating trees -------------------- diff --git a/src/index.c b/src/index.c index 55fe01a33..3b8447cf5 100644 --- a/src/index.c +++ b/src/index.c @@ -372,7 +372,7 @@ Index_read_tree(Index *self, PyObject *value) PyDoc_STRVAR(Index_write_tree__doc__, - "write_tree() -> str\n" + "write_tree() -> Oid\n" "\n" "Create a tree object from the index file, return its oid."); diff --git a/src/repository.c b/src/repository.c index 59c444357..3468ffca4 100644 --- a/src/repository.c +++ b/src/repository.c @@ -505,7 +505,7 @@ Repository_config__get__(Repository *self) } PyDoc_STRVAR(Repository_merge_base__doc__, - "merge_base(oid, oid) -> commit\n" + "merge_base(oid, oid) -> Oid\n" "\n" "Find as good common ancestors as possible for a merge."); @@ -665,9 +665,9 @@ Repository_create_blob_fromdisk(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_commit__doc__, - "create_commit(reference, author, committer, message, tree, parents[, encoding]) -> bytes\n" + "create_commit(reference, author, committer, message, tree, parents[, encoding]) -> Oid\n" "\n" - "Create a new commit object, return its SHA."); + "Create a new commit object, return its oid."); PyObject * Repository_create_commit(Repository *self, PyObject *args) @@ -749,9 +749,9 @@ Repository_create_commit(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_tag__doc__, - "create_tag(name, oid, type, tagger, message) -> bytes\n" + "create_tag(name, oid, type, tagger, message) -> Oid\n" "\n" - "Create a new tag object, return its SHA."); + "Create a new tag object, return its oid."); PyObject * Repository_create_tag(Repository *self, PyObject *args) @@ -1209,12 +1209,11 @@ Repository_notes(Repository *self, PyObject *args) } return Error_set(err); - } PyDoc_STRVAR(Repository_create_note__doc__, - "create_note(message, author, committer, annotated_id [,ref, force]) -> ID\n" + "create_note(message, author, committer, annotated_id [,ref, force]) -> Oid\n" "\n" "Create a new note for an object, return its SHA-ID." "If no ref is given 'refs/notes/commits' will be used."); diff --git a/src/treebuilder.c b/src/treebuilder.c index 55601e58f..8f5259603 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -74,7 +74,7 @@ TreeBuilder_insert(TreeBuilder *self, PyObject *args) PyDoc_STRVAR(TreeBuilder_write__doc__, - "write() -> bytes\n" + "write() -> Oid\n" "\n" "Write the tree to the given repository."); From 405d5ccdfb5fab99474158c419cedc009df6ae4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 11 May 2013 19:38:17 +0200 Subject: [PATCH 0498/2237] Rename and review py_str_to_git_oid* --- src/index.c | 6 ++-- src/oid.c | 70 ++++++++++++++++++++++++----------------------- src/oid.h | 6 ++-- src/reference.c | 9 ++---- src/repository.c | 61 ++++++++++++++++++++--------------------- src/treebuilder.c | 6 ++-- src/walker.c | 14 ++++------ 7 files changed, 84 insertions(+), 88 deletions(-) diff --git a/src/index.c b/src/index.c index 3b8447cf5..c62e5f235 100644 --- a/src/index.c +++ b/src/index.c @@ -352,10 +352,10 @@ Index_read_tree(Index *self, PyObject *value) git_oid oid; git_tree *tree; int err; - Py_ssize_t len; + size_t len; - len = py_str_to_git_oid(value, &oid); - if (len < 0) + len = py_oid_to_git_oid(value, &oid); + if (len == 0) return NULL; err = git_tree_lookup_prefix(&tree, self->repo->repo, &oid, len); diff --git a/src/oid.c b/src/oid.c index d1751ceb7..f96fd1eed 100644 --- a/src/oid.c +++ b/src/oid.c @@ -45,8 +45,8 @@ git_oid_to_python(const git_oid *oid) return (PyObject*)py_oid; } -Py_ssize_t -_oid_from_hex(PyObject *py_oid, git_oid *oid) +size_t +py_hex_to_git_oid (PyObject *py_oid, git_oid *oid) { PyObject *py_hex; int err; @@ -58,15 +58,15 @@ _oid_from_hex(PyObject *py_oid, git_oid *oid) if (PyBytes_Check(py_oid)) { err = PyBytes_AsStringAndSize(py_oid, &hex, &len); if (err) - return -1; + return 0; err = git_oid_fromstrn(oid, hex, len); if (err < 0) { PyErr_SetObject(Error_type(err), py_oid); - return -1; + return 0; } - return len; + return (size_t)len; } #endif @@ -74,31 +74,31 @@ _oid_from_hex(PyObject *py_oid, git_oid *oid) if (PyUnicode_Check(py_oid)) { py_hex = PyUnicode_AsASCIIString(py_oid); if (py_hex == NULL) - return -1; + return 0; err = PyBytes_AsStringAndSize(py_hex, &hex, &len); if (err) { Py_DECREF(py_hex); - return -1; + return 0; } err = git_oid_fromstrn(oid, hex, len); Py_DECREF(py_hex); if (err < 0) { PyErr_SetObject(Error_type(err), py_oid); - return -1; + return 0; } - return len; + return (size_t)len; } /* Type error */ PyErr_SetObject(PyExc_TypeError, py_oid); - return -1; + return 0; } -Py_ssize_t -py_str_to_git_oid(PyObject *py_oid, git_oid *oid) +size_t +py_oid_to_git_oid(PyObject *py_oid, git_oid *oid) { /* Oid */ if (PyObject_TypeCheck(py_oid, (PyTypeObject*)&OidType)) { @@ -107,41 +107,43 @@ py_str_to_git_oid(PyObject *py_oid, git_oid *oid) } /* Hex */ - return _oid_from_hex(py_oid, oid); + return py_hex_to_git_oid(py_oid, oid); } -Py_ssize_t -py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, git_oid *oid) +int +py_oid_to_git_oid_expand(git_repository *repo, PyObject *py_str, git_oid *oid) { int err; - Py_ssize_t len; - git_odb *odb; - git_odb_object *obj; + size_t len; + git_odb *odb = NULL; + git_odb_object *obj = NULL; - len = py_str_to_git_oid(py_str, oid); + len = py_oid_to_git_oid(py_str, oid); + if (len == 0) + return -1; - if (len == GIT_OID_HEXSZ || len < 0) - return len; + if (len == GIT_OID_HEXSZ) + return 0; + /* Short oid */ err = git_repository_odb(&odb, repo); - if (err < 0) { - Error_set(err); - return -1; - } + if (err < 0) + goto error; err = git_odb_read_prefix(&obj, odb, oid, len); - if (err < 0) { - git_odb_free(odb); - Error_set(err); - return err; - } + if (err < 0) + goto error; git_oid_cpy(oid, git_odb_object_id(obj)); - git_odb_object_free(obj); git_odb_free(odb); - return 0; + +error: + git_odb_object_free(obj); + git_odb_free(odb); + Error_set(err); + return -1; } PyObject * @@ -197,8 +199,8 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw) } /* Case 2: hex */ - len = _oid_from_hex(hex, &self->oid); - if (len < 0) + len = py_hex_to_git_oid(hex, &self->oid); + if (len == 0) return -1; return 0; diff --git a/src/oid.h b/src/oid.h index ca6567c1a..871f26a47 100644 --- a/src/oid.h +++ b/src/oid.h @@ -32,9 +32,9 @@ #include #include -Py_ssize_t py_str_to_git_oid(PyObject *py_str, git_oid *oid); -Py_ssize_t py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, - git_oid *oid); +size_t py_oid_to_git_oid(PyObject *py_str, git_oid *oid); +int py_oid_to_git_oid_expand(git_repository *repo, PyObject *py_str, + git_oid *oid); PyObject* git_oid_to_python(const git_oid *oid); PyObject* git_oid_to_py_str(const git_oid *oid); diff --git a/src/reference.c b/src/reference.c index 17537e73e..1e302c728 100644 --- a/src/reference.c +++ b/src/reference.c @@ -226,7 +226,6 @@ Reference_target__set__(Reference *self, PyObject *py_target) git_oid oid; char *c_name; int err; - Py_ssize_t len; git_reference *new_ref; git_repository *repo; @@ -235,11 +234,9 @@ Reference_target__set__(Reference *self, PyObject *py_target) /* Case 1: Direct */ if (GIT_REF_OID == git_reference_type(self->reference)) { repo = git_reference_owner(self->reference); - len = py_str_to_git_oid_expand(repo, py_target, &oid); - if (len < 0) { - err = (int)len; - goto error; - } + err = py_oid_to_git_oid_expand(repo, py_target, &oid); + if (err < 0) + return err; err = git_reference_set_target(&new_ref, self->reference, &oid); if (err < 0) diff --git a/src/repository.c b/src/repository.c index 3468ffca4..0cc7c3b4e 100644 --- a/src/repository.c +++ b/src/repository.c @@ -254,12 +254,12 @@ PyObject * Repository_git_object_lookup_prefix(Repository *self, PyObject *key) { int err; - Py_ssize_t len; + size_t len; git_oid oid; git_object *obj; - len = py_str_to_git_oid(key, &oid); - if (len < 0) + len = py_oid_to_git_oid(key, &oid); + if (len == 0) return NULL; err = git_object_lookup_prefix(&obj, self->repo, &oid, len, GIT_OBJ_ANY); @@ -339,11 +339,11 @@ Repository_read(Repository *self, PyObject *py_hex) { git_oid oid; git_odb_object *obj; - Py_ssize_t len; + size_t len; PyObject* tuple; - len = py_str_to_git_oid(py_hex, &oid); - if (len < 0) + len = py_oid_to_git_oid(py_hex, &oid); + if (len == 0) return NULL; obj = Repository_read_raw(self->repo, &oid, len); @@ -522,11 +522,11 @@ Repository_merge_base(Repository *self, PyObject *args) if (!PyArg_ParseTuple(args, "OO", &value1, &value2)) return NULL; - err = py_str_to_git_oid_expand(self->repo, value1, &oid1); + err = py_oid_to_git_oid_expand(self->repo, value1, &oid1); if (err < 0) return NULL; - err = py_str_to_git_oid_expand(self->repo, value2, &oid2); + err = py_oid_to_git_oid_expand(self->repo, value2, &oid2); if (err < 0) return NULL; @@ -548,7 +548,6 @@ Repository_walk(Repository *self, PyObject *args) PyObject *value; unsigned int sort; int err; - Py_ssize_t len; git_oid oid; git_revwalk *walk; Walker *py_walker; @@ -565,10 +564,10 @@ Repository_walk(Repository *self, PyObject *args) /* Push */ if (value != Py_None) { - len = py_str_to_git_oid_expand(self->repo, value, &oid); - if (len < 0) { + err = py_oid_to_git_oid_expand(self->repo, value, &oid); + if (err < 0) { git_revwalk_free(walk); - return Error_set((int)len); + return NULL; } err = git_revwalk_push(walk, &oid); @@ -683,7 +682,7 @@ Repository_create_commit(Repository *self, PyObject *args) int parent_count; git_commit **parents = NULL; int err = 0, i = 0; - Py_ssize_t len; + size_t len; if (!PyArg_ParseTuple(args, "zO!O!OOO!|s", &update_ref, @@ -695,8 +694,8 @@ Repository_create_commit(Repository *self, PyObject *args) &encoding)) return NULL; - len = py_str_to_git_oid(py_oid, &oid); - if (len < 0) + len = py_oid_to_git_oid(py_oid, &oid); + if (len == 0) goto out; message = py_str_to_c_str(py_message, encoding); @@ -717,12 +716,14 @@ Repository_create_commit(Repository *self, PyObject *args) } for (; i < parent_count; i++) { py_parent = PyList_GET_ITEM(py_parents, i); - len = py_str_to_git_oid(py_parent, &oid); - if (len < 0) + len = py_oid_to_git_oid(py_parent, &oid); + if (len == 0) goto out; - if (git_commit_lookup_prefix(&parents[i], self->repo, &oid, - (unsigned int)len)) + err = git_commit_lookup_prefix(&parents[i], self->repo, &oid, len); + if (err < 0) { + Error_set(err); goto out; + } } err = git_commit_create(&oid, self->repo, update_ref, @@ -762,7 +763,7 @@ Repository_create_tag(Repository *self, PyObject *args) git_oid oid; git_object *target = NULL; int err, target_type; - Py_ssize_t len; + size_t len; if (!PyArg_ParseTuple(args, "sOiO!s", &tag_name, @@ -772,12 +773,12 @@ Repository_create_tag(Repository *self, PyObject *args) &message)) return NULL; - len = py_str_to_git_oid(py_oid, &oid); - if (len < 0) + len = py_oid_to_git_oid(py_oid, &oid); + if (len == 0) return NULL; - err = git_object_lookup_prefix(&target, self->repo, &oid, - (unsigned int)len, target_type); + err = git_object_lookup_prefix(&target, self->repo, &oid, len, + target_type); err = err < 0 ? err : git_tag_create(&oid, self->repo, tag_name, target, py_tagger->signature, message, 0); git_object_free(target); @@ -885,14 +886,13 @@ Repository_create_reference_direct(Repository *self, PyObject *args, char *c_name; git_oid oid; int err, force; - Py_ssize_t len; if (!PyArg_ParseTuple(args, "sOi", &c_name, &py_obj, &force)) return NULL; - len = py_str_to_git_oid_expand(self->repo, py_obj, &oid); - if (len < 0) - return Error_set((int)len); + err = py_oid_to_git_oid_expand(self->repo, py_obj, &oid); + if (err < 0) + return NULL; err = git_reference_create(&c_reference, self->repo, c_name, &oid, force); if (err < 0) @@ -1013,7 +1013,6 @@ Repository_TreeBuilder(Repository *self, PyObject *args) git_tree *tree = NULL; git_tree *must_free = NULL; int err; - Py_ssize_t len; if (!PyArg_ParseTuple(args, "|O", &py_src)) return NULL; @@ -1027,8 +1026,8 @@ Repository_TreeBuilder(Repository *self, PyObject *args) } tree = py_tree->tree; } else { - len = py_str_to_git_oid_expand(self->repo, py_src, &oid); - if (len < 0) + err = py_oid_to_git_oid_expand(self->repo, py_src, &oid); + if (err < 0) return NULL; err = git_tree_lookup(&tree, self->repo, &oid); diff --git a/src/treebuilder.c b/src/treebuilder.c index 8f5259603..1724f14ee 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -53,7 +53,7 @@ PyObject * TreeBuilder_insert(TreeBuilder *self, PyObject *args) { PyObject *py_oid; - Py_ssize_t len; + size_t len; int err, attr; git_oid oid; const char *fname; @@ -61,8 +61,8 @@ TreeBuilder_insert(TreeBuilder *self, PyObject *args) if (!PyArg_ParseTuple(args, "sOi", &fname, &py_oid, &attr)) return NULL; - len = py_str_to_git_oid(py_oid, &oid); - if (len < 0) + len = py_oid_to_git_oid(py_oid, &oid); + if (len == 0) return NULL; err = git_treebuilder_insert(NULL, self->bld, fname, &oid, attr); diff --git a/src/walker.c b/src/walker.c index 1952fbe32..a02b2f45c 100644 --- a/src/walker.c +++ b/src/walker.c @@ -53,12 +53,11 @@ PyObject * Walker_hide(Walker *self, PyObject *py_hex) { int err; - Py_ssize_t len; git_oid oid; - len = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); - if (len < 0) - return Error_set((int)len); + err = py_oid_to_git_oid_expand(self->repo->repo, py_hex, &oid); + if (err < 0) + return NULL; err = git_revwalk_hide(self->walk, &oid); if (err < 0) @@ -77,12 +76,11 @@ PyObject * Walker_push(Walker *self, PyObject *py_hex) { int err; - Py_ssize_t len; git_oid oid; - len = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); - if (len < 0) - return Error_set((int)len); + err = py_oid_to_git_oid_expand(self->repo->repo, py_hex, &oid); + if (err < 0) + return NULL; err = git_revwalk_push(self->walk, &oid); if (err < 0) From 8c69751d4fbae6b70015d045632bb65ceae31e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 11 May 2013 21:55:28 +0200 Subject: [PATCH 0499/2237] Fix compilation warnings --- src/oid.c | 3 +++ src/utils.h | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/oid.c b/src/oid.c index f96fd1eed..d1db630ef 100644 --- a/src/oid.c +++ b/src/oid.c @@ -248,6 +248,9 @@ Oid_richcompare(PyObject *o1, PyObject *o2, int op) case Py_GE: res = (cmp >= 0) ? Py_True: Py_False; break; + default: + PyErr_Format(PyExc_RuntimeError, "Unexpected '%d' op", op); + return NULL; } Py_INCREF(res); diff --git a/src/utils.h b/src/utils.h index 1fb246699..99a89db1d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -79,7 +79,8 @@ /* Utilities */ -#define to_unicode(x, encoding, errors) to_unicode_n(x, strlen(x), encoding, errors) +#define to_unicode(x, encoding, errors)\ + to_unicode_n(x, strlen(x), encoding, errors) PYGIT2_FN_UNUSED Py_LOCAL_INLINE(PyObject*) @@ -150,8 +151,8 @@ char * py_str_to_c_str(PyObject *value, const char *encoding); /* Helpers to make type init shorter. */ #define INIT_TYPE(type, base, new) \ - if (base != NULL) type.tp_base = base; \ - if (new != NULL) type.tp_new = new; \ + type.tp_base = base; \ + type.tp_new = new; \ if (PyType_Ready(&type) < 0) return NULL; #define ADD_TYPE(module, type) \ From 3dd998c061f769081e385ecc4065e05b1d362cd3 Mon Sep 17 00:00:00 2001 From: Bernardo Heynemann Date: Wed, 15 May 2013 18:15:00 -0300 Subject: [PATCH 0500/2237] Supporting clone in pygit2 --- pygit2/__init__.py | 19 ++++++++++++++ src/pygit2.c | 56 +++++++++++++++++++++++++++++++++++++++++ test/test_repository.py | 42 ++++++++++++++++++++++++++++++- 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index aeb7fe5be..44b31e75d 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -47,3 +47,22 @@ def init_repository(path, bare=False): """ _pygit2.init_repository(path, bare) return Repository(path) + + +def clone_repository( + url, path, bare=False, remote_name="origin", push_url=None, fetch_spec=None, + push_spec=None, checkout_branch=None): + """ + Clones a new Git repository from *url* in the given *path*. + + Parameters: + * If 'bare' is True, then a bare git repository will be created. + * 'remote_name' is the name given to the "origin" remote. The default is "origin". + * 'push_url' is a URL to be used for pushing. None means use the fetch url. + * 'fetch_spec' is the fetch specification to be used for fetching. None results in the same behavior as GIT_REMOTE_DEFAULT_FETCH. + * 'push_spec' is the fetch specification to be used for pushing. None means use the same spec as for 'fetch_spec'. + * 'checkout_branch' gives the name of the branch to checkout. None means use the remote's HEAD + """ + + _pygit2.clone_repository(url, path, bare, remote_name, push_url, fetch_spec, push_spec, checkout_branch) + return Repository(path) diff --git a/src/pygit2.c b/src/pygit2.c index c68e530d6..ed1ad9d0b 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -97,6 +97,61 @@ init_repository(PyObject *self, PyObject *args) { Py_RETURN_NONE; }; +PyDoc_STRVAR(clone_repository__doc__, + "clone_repository(url, path, bare, remote_name, push_url, fetch_spec, push_spec, checkout_branch)\n" + "\n" + "Clones a Git repository in the given url to the given path with the specified options.\n" + "\n" + "Arguments:\n" + "\n" + "url\n" + " Git repository remote url.\n" + "path\n" + " Path where to create the repository.\n" + "bare\n" + " If 'bare' is not 0, then a bare git repository will be created.\n" + "remote_name\n" + " The name given to the 'origin' remote. The default is 'origin'.\n" + "push_url\n" + " URL to be used for pushing.\n" + "fetch_spec\n" + " The fetch specification to be used for fetching. None results in the same behavior as GIT_REMOTE_DEFAULT_FETCH.\n" + "push_spec\n" + " The fetch specification to be used for pushing. None means use the same spec as for 'fetch_spec'\n" + "checkout_branch\n" + " The name of the branch to checkout. None means use the remote's HEAD.\n"); + + +PyObject * +clone_repository(PyObject *self, PyObject *args) { + git_repository *repo; + const char *url; + const char *path; + unsigned int bare; + const char *remote_name, *push_url, *fetch_spec, *push_spec, *checkout_branch; + int err; + + if (!PyArg_ParseTuple(args, "zzIzzzzz", &url, &path, &bare, &remote_name, &push_url, &fetch_spec, &push_spec, &checkout_branch)) + return NULL; + + git_clone_options opts = { + .version=1, + .bare=bare, + .remote_name=remote_name, + .pushurl=push_url, + .fetch_spec=fetch_spec, + .push_spec=push_spec, + .checkout_branch=checkout_branch + }; + + err = git_clone(&repo, url, path, &opts); + if (err < 0) + return Error_set_str(err, path); + + git_repository_free(repo); + Py_RETURN_NONE; +}; + PyDoc_STRVAR(discover_repository__doc__, "discover_repository(path[, across_fs[, ceiling_dirs]]) -> str\n" @@ -172,6 +227,7 @@ hash(PyObject *self, PyObject *args) PyMethodDef module_methods[] = { {"init_repository", init_repository, METH_VARARGS, init_repository__doc__}, + {"clone_repository", clone_repository, METH_VARARGS, clone_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, diff --git a/test/test_repository.py b/test/test_repository.py index b11c804ad..f1771e9ec 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -40,7 +40,7 @@ # Import from pygit2 from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT -from pygit2 import init_repository, discover_repository, Reference, hashfile +from pygit2 import init_repository, clone_repository, discover_repository, Reference, hashfile from pygit2 import Oid import pygit2 from . import utils @@ -291,6 +291,46 @@ def test_head(self): self.assertTrue(self.repo.head_is_orphaned) self.assertFalse(self.repo.head_is_detached) +class CloneRepositoryTest(utils.NoRepoTestCase): + def test_clone_repository(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir) + self.assertFalse(repo.is_empty) + self.assertFalse(repo.is_bare) + + def test_clone_bare_repository(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, bare=True) + self.assertFalse(repo.is_empty) + self.assertTrue(repo.is_bare) + + def test_clone_remote_name(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, remote_name="custom_remote") + self.assertFalse(repo.is_empty) + self.assertEqual(repo.remotes[0].name, "custom_remote") + + def test_clone_push_url(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, push_url="custom_push_url") + self.assertFalse(repo.is_empty) + # not sure how to test this... couldn't find pushurl + # self.assertEqual(repo.remotes[0].pushurl, "custom_push_url") + + def test_clone_fetch_spec(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, fetch_spec="refs/heads/test") + self.assertFalse(repo.is_empty) + # not sure how to test this either... fetchspec seems to be going through, but repo is not getting it. + # self.assertEqual(repo.remotes[0].fetchspec, "refs/heads/test") + + def test_clone_push_spec(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, push_spec="refs/heads/test") + self.assertFalse(repo.is_empty) + # not sure how to test this either... couldn't find pushspec + # self.assertEqual(repo.remotes[0].fetchspec, "refs/heads/test") + + def test_clone_checkout_branch(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, checkout_branch="test") + self.assertFalse(repo.is_empty) + # not sure how to test this either... couldn't find current branch + # self.assertEqual(repo.remotes[0].current_branch, "test") + if __name__ == '__main__': unittest.main() From 4070c583f14b40eec0f0bbdbb419e388ae09502a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 16 May 2013 10:25:30 +0200 Subject: [PATCH 0501/2237] Explain what "latest libgit2" means better pygit2 targets the master branch, and "latest" isn't well-defined in this case, as libgit2's default branch is development. --- docs/install.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index 6cde5b587..b18d90806 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -6,8 +6,10 @@ How to Install .. contents:: -First you need to install the latest version of libgit2. You can find -platform-specific instructions to build the library in the libgit2 website: +First you need to install the latest release of libgit2. If you clone +the repository, make sure to use the ``master`` branch. You can find +platform-specific instructions to build the library in the libgit2 +website: http://libgit2.github.com @@ -55,7 +57,7 @@ instructions in the libgit2 ``README.md``): .. code-block:: sh - $ git clone git://github.com/libgit2/libgit2.git + $ git clone -b master git://github.com/libgit2/libgit2.git $ mkdir libgit2/build $ cd libgit2/build $ cmake .. @@ -105,7 +107,7 @@ from a bash shell: .. code-block:: sh $ export LIBGIT2=C:/Dev/libgit2 - $ git clone git://github.com/libgit2/libgit2.git + $ git clone -b master git://github.com/libgit2/libgit2.git $ cd libgit2 $ mkdir build $ cd build From a831b8b6f6c588a0a7e172e681523aad3f9a6cf8 Mon Sep 17 00:00:00 2001 From: Bernardo Heynemann Date: Thu, 16 May 2013 10:04:17 -0300 Subject: [PATCH 0502/2237] Since I was PEP8-ing my tests, I decided to do it for the whole file. --- test/test_repository.py | 64 ++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/test/test_repository.py b/test/test_repository.py index f1771e9ec..d6e762e4e 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -40,7 +40,10 @@ # Import from pygit2 from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT -from pygit2 import init_repository, clone_repository, discover_repository, Reference, hashfile +from pygit2 import ( + init_repository, clone_repository, discover_repository, + Reference, hashfile +) from pygit2 import Oid import pygit2 from . import utils @@ -101,7 +104,7 @@ def test_contains(self): self.assertFalse('a' * 20 in self.repo) def test_iterable(self): - l = [ obj for obj in self.repo ] + l = [obj for obj in self.repo] oid = Oid(hex=BLOB_HEX) self.assertTrue(oid in l) @@ -135,9 +138,10 @@ def test_lookup_commit_prefix(self): commit = self.repo[commit_sha_prefix] self.assertEqual(commit_sha, commit.hex) self.assertEqual(GIT_OBJ_COMMIT, commit.type) - self.assertEqual(('Second test data commit.\n\n' - 'This commit has some additional text.\n'), - commit.message) + self.assertEqual( + ('Second test data commit.\n\n' + 'This commit has some additional text.\n'), + commit.message) self.assertRaises(ValueError, self.repo.__getitem__, too_short_prefix) def test_get_path(self): @@ -208,7 +212,7 @@ def test_checkout_ref(self): def test_checkout_index(self): # some changes to working dir with open(os.path.join(self.repo.workdir, 'hello.txt'), 'w') as f: - f.write('new content') + f.write('new content') # checkout index self.assertTrue('hello.txt' in self.repo.status()) @@ -218,7 +222,7 @@ def test_checkout_index(self): def test_checkout_head(self): # some changes to the index with open(os.path.join(self.repo.workdir, 'bye.txt'), 'w') as f: - f.write('new content') + f.write('new content') self.repo.index.add('bye.txt') # checkout from index should not change anything @@ -272,6 +276,7 @@ def test_keyword_arg_true(self): repo = init_repository(self._temp_dir, bare=True) self.assertTrue(repo.is_bare) + class DiscoverRepositoryTest(utils.NoRepoTestCase): def test_discover_repo(self): repo = init_repository(self._temp_dir, False) @@ -279,6 +284,7 @@ def test_discover_repo(self): os.makedirs(subdir) self.assertEqual(repo.path, discover_repository(subdir)) + class EmptyRepositoryTest(utils.EmptyRepoTestCase): def test_is_empty(self): @@ -291,44 +297,68 @@ def test_head(self): self.assertTrue(self.repo.head_is_orphaned) self.assertFalse(self.repo.head_is_detached) + class CloneRepositoryTest(utils.NoRepoTestCase): def test_clone_repository(self): - repo = clone_repository("./test/data/testrepo.git/", self._temp_dir) + repo_path = "./test/data/testrepo.git/" + repo = clone_repository(repo_path, self._temp_dir) self.assertFalse(repo.is_empty) self.assertFalse(repo.is_bare) def test_clone_bare_repository(self): - repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, bare=True) + repo_path = "./test/data/testrepo.git/" + repo = clone_repository(repo_path, self._temp_dir, bare=True) self.assertFalse(repo.is_empty) self.assertTrue(repo.is_bare) def test_clone_remote_name(self): - repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, remote_name="custom_remote") + repo_path = "./test/data/testrepo.git/" + repo = clone_repository( + repo_path, self._temp_dir, remote_name="custom_remote" + ) self.assertFalse(repo.is_empty) self.assertEqual(repo.remotes[0].name, "custom_remote") def test_clone_push_url(self): - repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, push_url="custom_push_url") + repo_path = "./test/data/testrepo.git/" + repo = clone_repository( + repo_path, self._temp_dir, push_url="custom_push_url" + ) self.assertFalse(repo.is_empty) - # not sure how to test this... couldn't find pushurl + # FIXME: When pygit2 supports retrieving the pushurl parameter, + # enable this test # self.assertEqual(repo.remotes[0].pushurl, "custom_push_url") def test_clone_fetch_spec(self): - repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, fetch_spec="refs/heads/test") + repo_path = "./test/data/testrepo.git/" + repo = clone_repository( + repo_path, self._temp_dir, fetch_spec="refs/heads/test" + ) self.assertFalse(repo.is_empty) - # not sure how to test this either... fetchspec seems to be going through, but repo is not getting it. + # FIXME: When pygit2 retrieve the fetchspec we passed to git clone. + # fetchspec seems to be going through, but the Repository class is + # not getting it. # self.assertEqual(repo.remotes[0].fetchspec, "refs/heads/test") def test_clone_push_spec(self): - repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, push_spec="refs/heads/test") + repo_path = "./test/data/testrepo.git/" + repo = clone_repository( + repo_path, self._temp_dir, push_spec="refs/heads/test" + ) self.assertFalse(repo.is_empty) + # FIXME: When pygit2 supports retrieving the pushspec parameter, + # enable this test # not sure how to test this either... couldn't find pushspec # self.assertEqual(repo.remotes[0].fetchspec, "refs/heads/test") def test_clone_checkout_branch(self): - repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, checkout_branch="test") + repo_path = "./test/data/testrepo.git/" + repo = clone_repository( + repo_path, self._temp_dir, checkout_branch="test" + ) self.assertFalse(repo.is_empty) - # not sure how to test this either... couldn't find current branch + # FIXME: When pygit2 supports retrieving the current branch, + # enable this test # self.assertEqual(repo.remotes[0].current_branch, "test") From 57ea19e90589f506243a5e9128ca37a063b33a4f Mon Sep 17 00:00:00 2001 From: Bernardo Heynemann Date: Thu, 16 May 2013 10:06:46 -0300 Subject: [PATCH 0503/2237] Now __init__ is pep8-compliant --- pygit2/__init__.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 44b31e75d..babce1af5 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -50,19 +50,27 @@ def init_repository(path, bare=False): def clone_repository( - url, path, bare=False, remote_name="origin", push_url=None, fetch_spec=None, + url, path, bare=False, remote_name="origin", + push_url=None, fetch_spec=None, push_spec=None, checkout_branch=None): """ Clones a new Git repository from *url* in the given *path*. Parameters: - * If 'bare' is True, then a bare git repository will be created. - * 'remote_name' is the name given to the "origin" remote. The default is "origin". - * 'push_url' is a URL to be used for pushing. None means use the fetch url. - * 'fetch_spec' is the fetch specification to be used for fetching. None results in the same behavior as GIT_REMOTE_DEFAULT_FETCH. - * 'push_spec' is the fetch specification to be used for pushing. None means use the same spec as for 'fetch_spec'. - * 'checkout_branch' gives the name of the branch to checkout. None means use the remote's HEAD + * 'bare' indicates whether a bare git repository should be created. + * 'remote_name' is the name given to the "origin" remote. + The default is "origin". + * 'push_url' is a URL to be used for pushing. + None means use the fetch url. + * 'fetch_spec' is the fetch specification to be used for fetching. + None results in the same behavior as GIT_REMOTE_DEFAULT_FETCH. + * 'push_spec' is the fetch specification to be used for pushing. + None means use the same spec as for 'fetch_spec'. + * 'checkout_branch' gives the name of the branch to checkout. + None means use the remote's HEAD """ - _pygit2.clone_repository(url, path, bare, remote_name, push_url, fetch_spec, push_spec, checkout_branch) + _pygit2.clone_repository( + url, path, bare, remote_name, push_url, + fetch_spec, push_spec, checkout_branch) return Repository(path) From a377b32b603b82aaa9878679b9ae0ac583ecce34 Mon Sep 17 00:00:00 2001 From: Bernardo Heynemann Date: Thu, 16 May 2013 10:25:47 -0300 Subject: [PATCH 0504/2237] Documentation for the new clone method. Even though I am aware that the comment lines in pygit2/__init__.py are longer than 79 characters, there's a reason for that. If I break them, they'll show poorly in the documentation, and in my opinion better docs trump the 80 char requirement. If you think it's still better to have less than 80 characters in the comments, I'll gladly resubmit the changes at the expense of the documentation. --- docs/repository.rst | 11 +++++++++++ pygit2/__init__.py | 26 ++++++++++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/docs/repository.rst b/docs/repository.rst index 24f0674e3..034ed5692 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -20,6 +20,17 @@ Functions >>> repo = init_repository('test') # Creates a non-bare repository >>> repo = init_repository('test', bare=True) # Creates a bare repository +.. autofunction:: pygit2.clone_repository + + Example:: + + >>> from pygit2 import clone_repository + >>> repo_url = 'git://github.com/libgit2/pygit2.git' + >>> repo_path = '/path/to/create/repository' + >>> repo = clone_repository(repo_url, repo_path) # Clones a non-bare repository + >>> repo = clone_repository(repo_url, repo_path, bare=True) # Clones a bare repository + + .. autofunction:: pygit2.discover_repository diff --git a/pygit2/__init__.py b/pygit2/__init__.py index babce1af5..be87a49ad 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -56,18 +56,20 @@ def clone_repository( """ Clones a new Git repository from *url* in the given *path*. - Parameters: - * 'bare' indicates whether a bare git repository should be created. - * 'remote_name' is the name given to the "origin" remote. - The default is "origin". - * 'push_url' is a URL to be used for pushing. - None means use the fetch url. - * 'fetch_spec' is the fetch specification to be used for fetching. - None results in the same behavior as GIT_REMOTE_DEFAULT_FETCH. - * 'push_spec' is the fetch specification to be used for pushing. - None means use the same spec as for 'fetch_spec'. - * 'checkout_branch' gives the name of the branch to checkout. - None means use the remote's HEAD + * **bare** indicates whether a bare git repository should be created. + * **remote_name** is the name given to the "origin" remote. The default is "origin". + * **push_url** is a URL to be used for pushing. None means use the *url* parameter. + * **fetch_spec** defines the the default fetch spec. None results in the same behavior as *GIT_REMOTE_DEFAULT_FETCH*. + * **push_spec** is the fetch specification to be used for pushing. None means use the same spec as for *fetch_spec*. + * **checkout_branch** gives the name of the branch to checkout. None means use the remote's *HEAD*. + + Returns a Repository class pointing to the newly cloned repository. + + If you wish to use the repo, you need to do a checkout for one of the available branches, like this: + + >>> repo = repo.clone_repository("url", "path") + >>> repo.checkout(branch) # i.e.: refs/heads/master + """ _pygit2.clone_repository( From 032faf7017d9b5ac60c37f126697539e0dc8f63b Mon Sep 17 00:00:00 2001 From: Bernardo Heynemann Date: Thu, 16 May 2013 16:09:39 -0300 Subject: [PATCH 0505/2237] Fixing 80-column width for both the python and C code. --- pygit2/__init__.py | 25 ++++++++++++++++++------- src/pygit2.c | 19 +++++++++++++------ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index be87a49ad..e246ff837 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -56,16 +56,27 @@ def clone_repository( """ Clones a new Git repository from *url* in the given *path*. - * **bare** indicates whether a bare git repository should be created. - * **remote_name** is the name given to the "origin" remote. The default is "origin". - * **push_url** is a URL to be used for pushing. None means use the *url* parameter. - * **fetch_spec** defines the the default fetch spec. None results in the same behavior as *GIT_REMOTE_DEFAULT_FETCH*. - * **push_spec** is the fetch specification to be used for pushing. None means use the same spec as for *fetch_spec*. - * **checkout_branch** gives the name of the branch to checkout. None means use the remote's *HEAD*. + **bare** indicates whether a bare git repository should be created. + + **remote_name** is the name given to the "origin" remote. + The default is "origin". + + **push_url** is a URL to be used for pushing. + None means use the *url* parameter. + + **fetch_spec** defines the the default fetch spec. + None results in the same behavior as *GIT_REMOTE_DEFAULT_FETCH*. + + **push_spec** is the fetch specification to be used for pushing. + None means use the same spec as for *fetch_spec*. + + **checkout_branch** gives the name of the branch to checkout. + None means use the remote's *HEAD*. Returns a Repository class pointing to the newly cloned repository. - If you wish to use the repo, you need to do a checkout for one of the available branches, like this: + If you wish to use the repo, you need to do a checkout for one of + the available branches, like this: >>> repo = repo.clone_repository("url", "path") >>> repo.checkout(branch) # i.e.: refs/heads/master diff --git a/src/pygit2.c b/src/pygit2.c index ed1ad9d0b..d062a9ef5 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -98,9 +98,11 @@ init_repository(PyObject *self, PyObject *args) { }; PyDoc_STRVAR(clone_repository__doc__, - "clone_repository(url, path, bare, remote_name, push_url, fetch_spec, push_spec, checkout_branch)\n" + "clone_repository(url, path, bare, remote_name, push_url," + "fetch_spec, push_spec, checkout_branch)\n" "\n" - "Clones a Git repository in the given url to the given path with the specified options.\n" + "Clones a Git repository in the given url to the given path " + "with the specified options.\n" "\n" "Arguments:\n" "\n" @@ -115,11 +117,14 @@ PyDoc_STRVAR(clone_repository__doc__, "push_url\n" " URL to be used for pushing.\n" "fetch_spec\n" - " The fetch specification to be used for fetching. None results in the same behavior as GIT_REMOTE_DEFAULT_FETCH.\n" + " The fetch specification to be used for fetching. None results in " + "the same behavior as GIT_REMOTE_DEFAULT_FETCH.\n" "push_spec\n" - " The fetch specification to be used for pushing. None means use the same spec as for 'fetch_spec'\n" + " The fetch specification to be used for pushing. None means use the " + "same spec as for 'fetch_spec'\n" "checkout_branch\n" - " The name of the branch to checkout. None means use the remote's HEAD.\n"); + " The name of the branch to checkout. None means use the remote's " + "HEAD.\n"); PyObject * @@ -131,7 +136,9 @@ clone_repository(PyObject *self, PyObject *args) { const char *remote_name, *push_url, *fetch_spec, *push_spec, *checkout_branch; int err; - if (!PyArg_ParseTuple(args, "zzIzzzzz", &url, &path, &bare, &remote_name, &push_url, &fetch_spec, &push_spec, &checkout_branch)) + if (!PyArg_ParseTuple(args, "zzIzzzzz", + &url, &path, &bare, &remote_name, &push_url, + &fetch_spec, &push_spec, &checkout_branch)) return NULL; git_clone_options opts = { From 68e3e06a88bc7b781bc029f7241cd9125a370326 Mon Sep 17 00:00:00 2001 From: Bernardo Heynemann Date: Thu, 16 May 2013 16:13:23 -0300 Subject: [PATCH 0506/2237] Missed a couple lines in the C code --- src/pygit2.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index d062a9ef5..836289106 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -133,7 +133,8 @@ clone_repository(PyObject *self, PyObject *args) { const char *url; const char *path; unsigned int bare; - const char *remote_name, *push_url, *fetch_spec, *push_spec, *checkout_branch; + const char *remote_name, *push_url, *fetch_spec; + const char *push_spec, *checkout_branch; int err; if (!PyArg_ParseTuple(args, "zzIzzzzz", @@ -234,7 +235,8 @@ hash(PyObject *self, PyObject *args) PyMethodDef module_methods[] = { {"init_repository", init_repository, METH_VARARGS, init_repository__doc__}, - {"clone_repository", clone_repository, METH_VARARGS, clone_repository__doc__}, + {"clone_repository", clone_repository, METH_VARARGS, + clone_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, From c5606d94f21465d9ad0ee3d26bf835d0233d39d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Thu, 16 May 2013 23:23:57 +0200 Subject: [PATCH 0507/2237] Add Repository pointer to Reference. As asked in #221. --- src/reference.c | 12 +++++++++--- src/reference.h | 2 +- src/repository.c | 8 ++++---- src/types.h | 5 +---- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/reference.c b/src/reference.c index 1e302c728..0ed45aa3e 100644 --- a/src/reference.c +++ b/src/reference.c @@ -107,6 +107,7 @@ PyTypeObject RefLogIterType = { void Reference_dealloc(Reference *self) { + Py_CLEAR(self->repo); git_reference_free(self->reference); PyObject_Del(self); } @@ -191,7 +192,7 @@ Reference_resolve(Reference *self, PyObject *args) if (err < 0) return Error_set(err); - return wrap_reference(c_reference); + return wrap_reference(c_reference, self->repo); } @@ -461,13 +462,18 @@ PyTypeObject ReferenceType = { PyObject * -wrap_reference(git_reference * c_reference) +wrap_reference(git_reference * c_reference, Repository *repo) { Reference *py_reference=NULL; py_reference = PyObject_New(Reference, &ReferenceType); - if (py_reference) + if (py_reference) { py_reference->reference = c_reference; + if (repo) { + py_reference->repo = repo; + Py_INCREF(repo); + } + } return (PyObject *)py_reference; } diff --git a/src/reference.h b/src/reference.h index ff63bbf1e..5cbd13453 100644 --- a/src/reference.h +++ b/src/reference.h @@ -41,6 +41,6 @@ PyObject* Reference_get_name(Reference *self); PyObject* Reference_get_oid(Reference *self); PyObject* Reference_get_hex(Reference *self); PyObject* Reference_get_type(Reference *self); -PyObject* wrap_reference(git_reference * c_reference); +PyObject* wrap_reference(git_reference *c_reference, Repository *repo); #endif diff --git a/src/repository.c b/src/repository.c index 0cc7c3b4e..af388f47f 100644 --- a/src/repository.c +++ b/src/repository.c @@ -168,7 +168,7 @@ Repository_head__get__(Repository *self) return NULL; } - return wrap_reference(head); + return wrap_reference(head, self); } int @@ -859,7 +859,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) free(c_name); /* 3- Make an instance of Reference and return it */ - return wrap_reference(c_reference); + return wrap_reference(c_reference, self); } PyDoc_STRVAR(Repository_create_reference_direct__doc__, @@ -898,7 +898,7 @@ Repository_create_reference_direct(Repository *self, PyObject *args, if (err < 0) return Error_set(err); - return wrap_reference(c_reference); + return wrap_reference(c_reference, self); } PyDoc_STRVAR(Repository_create_reference_symbolic__doc__, @@ -932,7 +932,7 @@ Repository_create_reference_symbolic(Repository *self, PyObject *args, if (err < 0) return Error_set(err); - return wrap_reference(c_reference); + return wrap_reference(c_reference, self); } diff --git a/src/types.h b/src/types.h index fdc451e95..a4ac5eca1 100644 --- a/src/types.h +++ b/src/types.h @@ -159,10 +159,7 @@ typedef struct { /* git_reference, git_reflog */ SIMPLE_TYPE(Walker, git_revwalk, walk) -typedef struct { - PyObject_HEAD - git_reference *reference; -} Reference; +SIMPLE_TYPE(Reference, git_reference, reference) typedef struct { PyObject_HEAD From e0f4bb27a9dd4429486f32a464ae0ddad5427cb6 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Fri, 17 May 2013 17:44:32 +1000 Subject: [PATCH 0508/2237] README: fix typo --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 86790494d..7f5550dce 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,7 @@ Pygit2 links: Quick install guide =================== -1. Checkout libgi2 v0.18.0:: +1. Checkout libgit2 v0.18.0:: $ git clone git://github.com/libgit2/libgit2.git -b v0.18.0 From 4f83209a9ad08bb31c496d8493ce60f858e79736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 17 May 2013 10:04:25 +0200 Subject: [PATCH 0509/2237] Install notes, checkout from libgit2 master Some older versions of Git do not support to clone and checkout from a tag. --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 7f5550dce..b0bce5741 100644 --- a/README.rst +++ b/README.rst @@ -20,9 +20,9 @@ Pygit2 links: Quick install guide =================== -1. Checkout libgit2 v0.18.0:: +1. Checkout the libgit2 stable branch:: - $ git clone git://github.com/libgit2/libgit2.git -b v0.18.0 + $ git clone git://github.com/libgit2/libgit2.git -b master 2. Build and install libgit2 https://github.com/libgit2/libgit2/#building-libgit2---using-cmake From 785b5112abfb67294e6fd4ee47a081ba157478f0 Mon Sep 17 00:00:00 2001 From: Andrey Devyatkin Date: Fri, 17 May 2013 22:20:44 +0200 Subject: [PATCH 0510/2237] Add example for repo.walk pydoc --- src/repository.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/repository.c b/src/repository.c index af388f47f..9409bad3d 100644 --- a/src/repository.c +++ b/src/repository.c @@ -540,7 +540,14 @@ Repository_merge_base(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_walk__doc__, "walk(oid, sort_mode) -> iterator\n" "\n" - "Generator that traverses the history starting from the given commit."); + "Generator that traverses the history starting from the given commit.\n" + "\n" + "Example:\n" + "\n" + "import pygit2" + "repo = pygit2.Repository(.git)\n" + "for object in repo.walk(repo.head.oid, pygit2.GIT_SORT_TOPOLOGICAL):\n" + " print object.message"); PyObject * Repository_walk(Repository *self, PyObject *args) From 80574613f45164a47a22d076d9b604c5697f4d25 Mon Sep 17 00:00:00 2001 From: Andrey Devyatkin Date: Sat, 18 May 2013 01:05:59 +0200 Subject: [PATCH 0511/2237] Fix document generation and move example to pydoc - Fix issues introduced in prev commit - Move all documentation for Repository.walk from log.rst to pydoc in repository.c to make it available in both html and pydoc - Extend documentation with sort types description --- docs/log.rst | 6 ------ src/repository.c | 23 +++++++++++++++++++---- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/docs/log.rst b/docs/log.rst index 260b9d0cb..c63bc41c6 100644 --- a/docs/log.rst +++ b/docs/log.rst @@ -3,9 +3,3 @@ Commit log ********************************************************************** .. automethod:: pygit2.Repository.walk - -You can iterate through the revision history with repo.walk:: - - >>> from pygit2 import GIT_SORT_TIME - >>> for commit in repo.walk(oid, GIT_SORT_TIME): - ... print(commit.hex) diff --git a/src/repository.c b/src/repository.c index 9409bad3d..9f9893a2b 100644 --- a/src/repository.c +++ b/src/repository.c @@ -541,13 +541,28 @@ PyDoc_STRVAR(Repository_walk__doc__, "walk(oid, sort_mode) -> iterator\n" "\n" "Generator that traverses the history starting from the given commit.\n" + "The following types of sorting could be used to control traversing\n" + "direction:\n" + "\n" + "* GIT_SORT_NONE. This is the default sorting for new walkers\n" + " Sort the repository contents in no particular ordering\n" + "* GIT_SORT_TOPOLOGICAL. Sort the repository contents in topological order\n" + " (parents before children); this sorting mode can be combined with\n" + " time sorting.\n" + "* GIT_SORT_TIME. Sort the repository contents by commit time\n" + "* GIT_SORT_REVERSE. Iterate through the repository contents in reverse\n" + " order; this sorting mode can be combined with any of the above.\n" "\n" "Example:\n" "\n" - "import pygit2" - "repo = pygit2.Repository(.git)\n" - "for object in repo.walk(repo.head.oid, pygit2.GIT_SORT_TOPOLOGICAL):\n" - " print object.message"); + " >>> from pygit2 import Repository\n" + " >>> from pygit2 import GIT_SORT_TOPOLOGICAL, GIT_SORT_REVERSE\n" + " >>> repo = Repository('.git')\n" + " >>> for commit in repo.walk(repo.head.oid, GIT_SORT_TOPOLOGICAL):\n" + " ... print commit.message\n" + " >>> for commit in repo.walk(repo.head.oid, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE):\n" + " ... print commit.message\n" + " >>>\n"); PyObject * Repository_walk(Repository *self, PyObject *args) From 196d0595b0ce42fe17c0feffbd7c1989c281d4c4 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 18 May 2013 14:42:31 +0200 Subject: [PATCH 0512/2237] Added: Repository.diff() - porcelain method similiar to --- pygit2/repository.py | 68 +++++++++++++++++++++++++++++++++++++++++++- pygit2/utils.py | 10 +++++++ src/tree.c | 4 +-- test/test_diff.py | 41 +++++++++++++++++--------- 4 files changed, 107 insertions(+), 16 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index 93f8f5447..c233de3b1 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -29,10 +29,11 @@ from string import hexdigits # Import from pygit2 +from utils import text_type from _pygit2 import Repository as _Repository from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN from _pygit2 import GIT_CHECKOUT_SAFE_CREATE -from _pygit2 import Reference +from _pygit2 import Reference, Tree, Commit, Blob class Repository(_Repository): @@ -128,3 +129,68 @@ def checkout(self, refname=None, strategy=GIT_CHECKOUT_SAFE_CREATE): treeish = self[oid] self.checkout_tree(treeish, strategy) self.head = refname + + + # + # Diff + # + def diff(self, a=None, b=None, cached=False, flags=0): + """ + Show changes between the working tree and the index or a tree, + changes between the index and a tree, changes between two trees, or + changes between two blobs. + + Examples:: + + # Changes in the working tree not yet staged for the next commit + >>> diff() + + # Changes between the index and your last commit + >>> diff(cached=True) + + # Changes in the working tree since your last commit + >>> diff('HEAD') + + # Changes between commits + >>> t0 = revparse_single('HEAD') + >>> t1 = revparse_single('HEAD^') + >>> diff(t0, t1) + >>> diff('HEAD', 'HEAD^') # equivalent + + If you want to diff a tree against an empty tree, use the low level + API (Tree.diff_to_tree()) directly. + """ + + def treeish_to_tree(obj): + if isinstance(obj, text_type): + obj = self.revparse_single(obj) + + if isinstance(obj, Commit): + return obj.tree + elif isinstance(obj, Reference): + oid = obj.resolve().target + return self[oid] + + a = treeish_to_tree(a) or a + b = treeish_to_tree(b) or b + + # Case 1: Diff tree to tree + if isinstance(a, Tree) and isinstance(b, Tree): + return a.diff_to_tree(b, flags=flags) + + # Case 2: Index to workdir + elif a is None and b is None: + return self.index.diff() + + # Case 3: Diff tree to index or workdir + elif isinstance(a, Tree) and b is None: + if cached: + return a.diff_to_index(self.index, flags=flags) + else: + return a.diff_to_workdir(flags) + + # Case 4: Diff blob to blob + if isinstance(a, Blob) and isinstance(b, Blob): + raise NotImplementedError('git_diff_blob_to_blob()') + + raise ValueError("Only blobs and treeish can be diffed") diff --git a/pygit2/utils.py b/pygit2/utils.py index 79e60407c..2f6645a3b 100644 --- a/pygit2/utils.py +++ b/pygit2/utils.py @@ -27,3 +27,13 @@ # feel free to add utils functions here + +import sys + +# python2/3 compability types for isinstance() +if sys.version < '3': + text_type = unicode + binary_type = str +else: + text_type = str + binary_type = bytes diff --git a/src/tree.c b/src/tree.c index 4ffa5c998..d311b88e7 100644 --- a/src/tree.c +++ b/src/tree.c @@ -300,7 +300,7 @@ Tree_diff_to_workdir(Tree *self, PyObject *args) PyDoc_STRVAR(Tree_diff_to_index__doc__, "\n"); PyObject * -Tree_diff_to_index(Tree *self, PyObject *args) +Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; @@ -383,7 +383,7 @@ PyMappingMethods Tree_as_mapping = { PyMethodDef Tree_methods[] = { METHOD(Tree, diff_to_tree, METH_VARARGS | METH_KEYWORDS), METHOD(Tree, diff_to_workdir, METH_VARARGS), - METHOD(Tree, diff_to_index, METH_VARARGS), + METHOD(Tree, diff_to_index, METH_VARARGS | METH_KEYWORDS), {NULL} }; diff --git a/test/test_diff.py b/test/test_diff.py index b3366240a..6de34af32 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -91,17 +91,25 @@ class DiffDirtyTest(utils.DirtyRepoTestCase): def test_diff_empty_index(self): repo = self.repo + head = repo[repo.lookup_reference('HEAD').resolve().target] diff = head.tree.diff_to_index(repo.index) + files = [patch.new_file_path for patch in diff] + self.assertEqual(DIFF_INDEX_EXPECTED, files) + diff = repo.diff('HEAD', cached=True) files = [patch.new_file_path for patch in diff] self.assertEqual(DIFF_INDEX_EXPECTED, files) def test_workdir_to_tree(self): repo = self.repo head = repo[repo.lookup_reference('HEAD').resolve().target] + diff = head.tree.diff_to_workdir() + files = [patch.new_file_path for patch in diff] + self.assertEqual(DIFF_WORKDIR_EXPECTED, files) + diff = repo.diff('HEAD') files = [patch.new_file_path for patch in diff] self.assertEqual(DIFF_WORKDIR_EXPECTED, files) @@ -117,8 +125,12 @@ def test_diff_invalid(self): def test_diff_empty_index(self): repo = self.repo head = repo[repo.lookup_reference('HEAD').resolve().target] + diff = head.tree.diff_to_index(repo.index) + files = [patch.new_file_path.split('/')[0] for patch in diff] + self.assertEqual([x.name for x in head.tree], files) + diff = repo.diff('HEAD', cached=True) files = [patch.new_file_path.split('/')[0] for patch in diff] self.assertEqual([x.name for x in head.tree], files) @@ -126,22 +138,25 @@ def test_diff_tree(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] - diff = commit_a.tree.diff_to_tree(commit_b.tree) + def _test(diff): + # self.assertIsNotNone is 2.7 only + self.assertTrue(diff is not None) + # self.assertIn is 2.7 only + self.assertEqual(2, sum(map(lambda x: len(x.hunks), diff))) - # self.assertIsNotNone is 2.7 only - self.assertTrue(diff is not None) - # self.assertIn is 2.7 only - self.assertEqual(2, sum(map(lambda x: len(x.hunks), diff))) + patch = diff[0] + hunk = patch.hunks[0] + self.assertEqual(hunk.old_start, 1) + self.assertEqual(hunk.old_lines, 1) + self.assertEqual(hunk.new_start, 1) + self.assertEqual(hunk.new_lines, 1) - patch = diff[0] - hunk = patch.hunks[0] - self.assertEqual(hunk.old_start, 1) - self.assertEqual(hunk.old_lines, 1) - self.assertEqual(hunk.new_start, 1) - self.assertEqual(hunk.new_lines, 1) + self.assertEqual(patch.old_file_path, 'a') + self.assertEqual(patch.new_file_path, 'a') + + _test(commit_a.tree.diff_to_tree(commit_b.tree)) + _test(self.repo.diff(COMMIT_SHA1_1, COMMIT_SHA1_2)) - self.assertEqual(patch.old_file_path, 'a') - self.assertEqual(patch.new_file_path, 'a') def test_diff_empty_tree(self): commit_a = self.repo[COMMIT_SHA1_1] From 0fc7a4fbad68a2bd4a70c4ec6cc7c7811136abea Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 18 May 2013 15:29:28 +0200 Subject: [PATCH 0513/2237] Refactored Index.diff() into Index.diff_to_tree()/diff_to_workdir() --- pygit2/repository.py | 2 +- src/diff.c | 3 +- src/index.c | 75 ++++++++++++++++++++++++-------------------- src/tree.c | 1 - test/test_diff.py | 32 +++++++++++++++---- 5 files changed, 70 insertions(+), 43 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index c233de3b1..542746254 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -180,7 +180,7 @@ def treeish_to_tree(obj): # Case 2: Index to workdir elif a is None and b is None: - return self.index.diff() + return self.index.diff_to_workdir() # Case 3: Diff tree to index or workdir elif isinstance(a, Tree) and b is None: diff --git a/src/diff.c b/src/diff.c index fd78f4985..876cddb81 100644 --- a/src/diff.c +++ b/src/diff.c @@ -109,7 +109,8 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) goto cleanup; PyList_SetItem(py_hunk->lines, j, - Py_BuildValue("cO", line_origin, + Py_BuildValue("OO", + to_unicode_n(&line_origin, 1, NULL, NULL), to_unicode_n(line, line_len, NULL, NULL) ) ); diff --git a/src/index.c b/src/index.c index 55fe01a33..9bf1fe1fa 100644 --- a/src/index.c +++ b/src/index.c @@ -31,6 +31,7 @@ #include "types.h" #include "utils.h" #include "oid.h" +#include "diff.h" #include "index.h" extern PyTypeObject IndexType; @@ -114,54 +115,59 @@ Index_clear(Index *self) } -PyDoc_STRVAR(Index_diff__doc__, - "diff([tree]) -> Diff\n" +PyDoc_STRVAR(Index_diff_to_workdir__doc__, + "diff_to_workdir() -> Diff\n" "\n" "Return a :py:class:`~pygit2.Diff` object with the differences between the\n" - "index and the working copy. If a :py:class:`~pygit2.Tree` object is\n" - "passed, return the diferences between the index and the given tree."); + "index and the working copy.\n"); PyObject * -Index_diff(Index *self, PyObject *args) +Index_diff_to_workdir(Index *self, PyObject *args) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; int err; - Diff *py_diff; - PyObject *py_obj = NULL; - - if (!PyArg_ParseTuple(args, "|O", &py_obj)) + if (!PyArg_ParseTuple(args, "|i", &opts.flags)) return NULL; - if (py_obj == NULL) { - err = git_diff_index_to_workdir( - &diff, - self->repo->repo, - self->index, - &opts); - } else if (PyObject_TypeCheck(py_obj, &TreeType)) { - err = git_diff_tree_to_index( - &diff, - self->repo->repo, - ((Tree *)py_obj)->tree, - self->index, - &opts); - } else { - PyErr_SetObject(PyExc_TypeError, py_obj); - return NULL; - } + err = git_diff_index_to_workdir( + &diff, + self->repo->repo, + self->index, + &opts); + if (err < 0) return Error_set(err); - py_diff = PyObject_New(Diff, &DiffType); - if (py_diff) { - Py_INCREF(self->repo); - py_diff->repo = self->repo; - py_diff->list = diff; - } + return wrap_diff(diff, self->repo); +} + +PyDoc_STRVAR(Index_diff_to_tree__doc__, + "diff_to_tree(tree) -> Diff\n" + "\n" + "Return a :py:class:`~pygit2.Diff` object with the differences between the\n" + "index and the given tree.\n"); + +PyObject * +Index_diff_to_tree(Index *self, PyObject *args) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_list *diff; + git_repository* repo; + int err; + + Tree *py_tree = NULL; + + if (!PyArg_ParseTuple(args, "O!|i", &TreeType, &py_tree, &opts.flags)) + return NULL; + + repo = self->repo->repo; + err = git_diff_tree_to_index(&diff, repo, py_tree->tree, self->index, &opts); + if (err < 0) + return Error_set(err); - return (PyObject*)py_diff; + return wrap_diff(diff, self->repo); } @@ -393,7 +399,8 @@ PyMethodDef Index_methods[] = { METHOD(Index, add, METH_VARARGS), METHOD(Index, remove, METH_VARARGS), METHOD(Index, clear, METH_NOARGS), - METHOD(Index, diff, METH_VARARGS), + METHOD(Index, diff_to_workdir, METH_VARARGS), + METHOD(Index, diff_to_tree, METH_VARARGS), METHOD(Index, _find, METH_O), METHOD(Index, read, METH_NOARGS), METHOD(Index, write, METH_NOARGS), diff --git a/src/tree.c b/src/tree.c index d311b88e7..ede8d7132 100644 --- a/src/tree.c +++ b/src/tree.c @@ -317,7 +317,6 @@ Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) repo = self->repo->repo; err = git_diff_tree_to_index(&diff, repo, self->tree, py_idx->index, &opts); - if (err < 0) return Error_set(err); diff --git a/test/test_diff.py b/test/test_diff.py index 6de34af32..d69026593 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -61,7 +61,7 @@ -c/d contents """ -DIFF_INDEX_EXPECTED = [ +DIFF_HEAD_TO_INDEX_EXPECTED = [ 'staged_changes', 'staged_changes_file_deleted', 'staged_changes_file_modified', @@ -72,7 +72,7 @@ 'staged_new_file_modified' ] -DIFF_WORKDIR_EXPECTED = [ +DIFF_HEAD_TO_WORKDIR_EXPECTED = [ 'file_deleted', 'modified_file', 'staged_changes', @@ -84,6 +84,17 @@ 'subdir/modified_file' ] +DIFF_INDEX_TO_WORK_EXPECTED = [ + 'file_deleted', + 'modified_file', + 'staged_changes_file_deleted', + 'staged_changes_file_modified', + 'staged_new_file_deleted', + 'staged_new_file_modified', + 'subdir/deleted_file', + 'subdir/modified_file' +] + HUNK_EXPECTED = """- a contents 2 + a contents """ @@ -95,11 +106,11 @@ def test_diff_empty_index(self): head = repo[repo.lookup_reference('HEAD').resolve().target] diff = head.tree.diff_to_index(repo.index) files = [patch.new_file_path for patch in diff] - self.assertEqual(DIFF_INDEX_EXPECTED, files) + self.assertEqual(DIFF_HEAD_TO_INDEX_EXPECTED, files) diff = repo.diff('HEAD', cached=True) files = [patch.new_file_path for patch in diff] - self.assertEqual(DIFF_INDEX_EXPECTED, files) + self.assertEqual(DIFF_HEAD_TO_INDEX_EXPECTED, files) def test_workdir_to_tree(self): repo = self.repo @@ -107,11 +118,16 @@ def test_workdir_to_tree(self): diff = head.tree.diff_to_workdir() files = [patch.new_file_path for patch in diff] - self.assertEqual(DIFF_WORKDIR_EXPECTED, files) + self.assertEqual(DIFF_HEAD_TO_WORKDIR_EXPECTED, files) diff = repo.diff('HEAD') files = [patch.new_file_path for patch in diff] - self.assertEqual(DIFF_WORKDIR_EXPECTED, files) + self.assertEqual(DIFF_HEAD_TO_WORKDIR_EXPECTED, files) + + def test_index_to_workdir(self): + diff = self.repo.diff() + files = [patch.new_file_path for patch in diff] + self.assertEqual(DIFF_INDEX_TO_WORK_EXPECTED, files) class DiffTest(utils.BareRepoTestCase): @@ -126,6 +142,10 @@ def test_diff_empty_index(self): repo = self.repo head = repo[repo.lookup_reference('HEAD').resolve().target] + diff = self.repo.index.diff_to_tree(head.tree) + files = [patch.new_file_path.split('/')[0] for patch in diff] + self.assertEqual([x.name for x in head.tree], files) + diff = head.tree.diff_to_index(repo.index) files = [patch.new_file_path.split('/')[0] for patch in diff] self.assertEqual([x.name for x in head.tree], files) From bc4c32b65cdaf57a95b220c787d460d926021472 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 18 May 2013 15:37:04 +0200 Subject: [PATCH 0514/2237] Added: documentation for Tree.diff_*-Methods --- docs/diff.rst | 33 +++++++++++++++++++++------------ src/tree.c | 16 +++++++++++++--- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/docs/diff.rst b/docs/diff.rst index 4d50ed81b..f604696be 100644 --- a/docs/diff.rst +++ b/docs/diff.rst @@ -4,25 +4,34 @@ Diff A diff shows the changes between trees, an index or the working dir:: - >>> head = repo.revparse_single('HEAD') + # Changes in the working tree not yet staged for the next commit + >>> repo.diff() - # Diff two trees - >>> t0 = head.tree - >>> t1 = head.parents[0].tree - >>> diff = t1.diff(t0) + # Changes between the index and your last commit + >>> self.diff(cached=True) - # Diff a tree with the index - >>> diff = head.tree.diff(repo.index) + # Changes in the working tree since your last commit + >>> self.diff('HEAD') - # Diff a tree with the current working dir - >>> diff = head.tree.diff() + # Changes between commits + >>> t0 = revparse_single('HEAD') + >>> t1 = revparse_single('HEAD^') + >>> repo.diff(t0, t1) + >>> t0.diff(t1) # equivalent + >>> repo.diff('HEAD', 'HEAD^') # equivalent # Get all patches for a diff - >>> t0 = repo.revparse_single('HEAD^').tree - >>> t1 = repo.revparse_single('HEAD~3').tree - >>> diff = t0.diff(t1) + >>> diff = repo.diff('HEAD^', 'HEAD~3') >>> patches = [p for p in diff] + # Diffing the empty tree + >>> tree = revparse_single('HEAD').tree + >>> tree.diff_to_tree() + + # Diff empty tree to a tree + >>> tree = revparse_single('HEAD').tree + >>> tree.diff_to_tree(swap=True) + The Diff type ==================== diff --git a/src/tree.c b/src/tree.c index ede8d7132..c6edaab04 100644 --- a/src/tree.c +++ b/src/tree.c @@ -271,7 +271,10 @@ Tree_getitem(Tree *self, PyObject *value) } -PyDoc_STRVAR(Tree_diff_to_workdir__doc__, "\n"); +PyDoc_STRVAR(Tree_diff_to_workdir__doc__, + "diff_to_workdir([flags]) -> Diff\n" + "\n" + "Show the changes between the tree and the workdir.\n"); PyObject * Tree_diff_to_workdir(Tree *self, PyObject *args) @@ -297,7 +300,10 @@ Tree_diff_to_workdir(Tree *self, PyObject *args) } -PyDoc_STRVAR(Tree_diff_to_index__doc__, "\n"); +PyDoc_STRVAR(Tree_diff_to_index__doc__, + "diff_to_index(index, [flags]) -> Diff\n" + "\n" + "Show the changes between the index and a given tree.\n"); PyObject * Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) @@ -324,7 +330,11 @@ Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) } -PyDoc_STRVAR(Tree_diff_to_tree__doc__, "\n"); +PyDoc_STRVAR(Tree_diff_to_tree__doc__, + "diff_to_tree([tree, flags, swap]) -> Diff\n" + "\n" + "Show the changes between two trees. If no tree is given the empty tree will" + "be used instead.\n"); PyObject * Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds) From 99263af164ce6cb1b4a34c87de98e5b6eb1637d3 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 18 May 2013 15:49:30 +0200 Subject: [PATCH 0515/2237] use git_tree_owner in Index_diff_to_tree() --- src/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.c b/src/index.c index dccd2a154..b756a0938 100644 --- a/src/index.c +++ b/src/index.c @@ -162,7 +162,7 @@ Index_diff_to_tree(Index *self, PyObject *args) if (!PyArg_ParseTuple(args, "O!|i", &TreeType, &py_tree, &opts.flags)) return NULL; - repo = self->repo->repo; + repo = git_tree_owner(py_tree->tree); err = git_diff_tree_to_index(&diff, repo, py_tree->tree, self->index, &opts); if (err < 0) return Error_set(err); From 60c37bd7bf4a0b6254b6bdc1a7a7cbd33f8efbbe Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sat, 18 May 2013 16:19:44 +0200 Subject: [PATCH 0516/2237] Fixed: documentation for diff_*()-Methods --- docs/diff.rst | 14 ++++++-------- docs/objects.rst | 4 +++- docs/working-copy.rst | 3 ++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/diff.rst b/docs/diff.rst index f604696be..d81fa9c9c 100644 --- a/docs/diff.rst +++ b/docs/diff.rst @@ -2,16 +2,15 @@ Diff ********************************************************************** -A diff shows the changes between trees, an index or the working dir:: +.. contents:: - # Changes in the working tree not yet staged for the next commit - >>> repo.diff() +A diff shows the changes between trees, an index or the working dir. - # Changes between the index and your last commit - >>> self.diff(cached=True) +.. automethod:: pygit2.Repository.diff - # Changes in the working tree since your last commit - >>> self.diff('HEAD') +Examples + +.. code-block:: python # Changes between commits >>> t0 = revparse_single('HEAD') @@ -32,7 +31,6 @@ A diff shows the changes between trees, an index or the working dir:: >>> tree = revparse_single('HEAD').tree >>> tree.diff_to_tree(swap=True) - The Diff type ==================== diff --git a/docs/objects.rst b/docs/objects.rst index 04f466e0e..970a0c3e5 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -159,7 +159,9 @@ interfaces. Return an iterator over the entries of the tree. -.. automethod:: pygit2.Tree.diff +.. automethod:: pygit2.Tree.diff_to_tree +.. automethod:: pygit2.Tree.diff_to_workdir +.. automethod:: pygit2.Tree.diff_to_index Tree entries ------------ diff --git a/docs/working-copy.rst b/docs/working-copy.rst index 349155245..bb10f7156 100644 --- a/docs/working-copy.rst +++ b/docs/working-copy.rst @@ -33,7 +33,8 @@ The Index type .. automethod:: pygit2.Index.write .. automethod:: pygit2.Index.read_tree .. automethod:: pygit2.Index.write_tree -.. automethod:: pygit2.Index.diff +.. automethod:: pygit2.Index.diff_to_tree +.. automethod:: pygit2.Index.diff_to_workdir The IndexEntry type From 35d8bc4fc4f136d627e640c067d4d64dc23a52b2 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 20 May 2013 12:55:28 +0200 Subject: [PATCH 0517/2237] Added: Missing CONSTANTS for GIT_DIFF_* --- src/pygit2.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pygit2.c b/src/pygit2.c index 836289106..94bb06db7 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -383,6 +383,14 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNTRACKED) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNMODIFIED) ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_UNTRACKED_DIRS) + ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_UNTRACKED_DIRS) + ADD_CONSTANT_INT(m, GIT_DIFF_DISABLE_PATHSPEC_MATCH) + ADD_CONSTANT_INT(m, GIT_DIFF_DELTAS_ARE_ICASE) + ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) + ADD_CONSTANT_INT(m, GIT_DIFF_SKIP_BINARY_CHECK) + ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE) + ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) + ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_IGNORED_DIRS) /* Flags for diff find similar */ /* --find-renames */ ADD_CONSTANT_INT(m, GIT_DIFF_FIND_RENAMES) From 6ce71a279961fa17316bf581c1507306689be6bb Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 20 May 2013 13:57:40 +0200 Subject: [PATCH 0518/2237] Added: support for context_lines and interhunk_lines for diffs --- pygit2/repository.py | 34 +++++++++++++++++++---- src/index.c | 36 ++++++++++++++++++++---- src/tree.c | 65 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 112 insertions(+), 23 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index 542746254..d62d08a74 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -27,6 +27,7 @@ # Import from the Standard Library from string import hexdigits +from collections import OrderedDict # Import from pygit2 from utils import text_type @@ -134,12 +135,29 @@ def checkout(self, refname=None, strategy=GIT_CHECKOUT_SAFE_CREATE): # # Diff # - def diff(self, a=None, b=None, cached=False, flags=0): + def diff(self, a=None, b=None, cached=False, flags=0, context_lines=3, + interhunk_lines=0): """ Show changes between the working tree and the index or a tree, changes between the index and a tree, changes between two trees, or changes between two blobs. + Keyword arguments: + + cached + use staged changes instead of workdir + + flag + a GIT_DIFF_* constant + + context_lines + the number of unchanged lines that define the boundary + of a hunk (and to display before and after)\n" + + interhunk_lines + the maximum number of unchanged lines between hunk + boundaries before the hunks will be merged into a one + Examples:: # Changes in the working tree not yet staged for the next commit @@ -174,20 +192,26 @@ def treeish_to_tree(obj): a = treeish_to_tree(a) or a b = treeish_to_tree(b) or b + opts = OrderedDict([ + ('flags', flags), + ('context_lines', context_lines), + ('interhunk_lines', interhunk_lines) + ]) + # Case 1: Diff tree to tree if isinstance(a, Tree) and isinstance(b, Tree): - return a.diff_to_tree(b, flags=flags) + return a.diff_to_tree(b, **opts) # Case 2: Index to workdir elif a is None and b is None: - return self.index.diff_to_workdir() + return self.index.diff_to_workdir(*opts.values()) # Case 3: Diff tree to index or workdir elif isinstance(a, Tree) and b is None: if cached: - return a.diff_to_index(self.index, flags=flags) + return a.diff_to_index(self.index, **opts) else: - return a.diff_to_workdir(flags) + return a.diff_to_workdir(*opts.values()) # Case 4: Diff blob to blob if isinstance(a, Blob) and isinstance(b, Blob): diff --git a/src/index.c b/src/index.c index b756a0938..3b637a852 100644 --- a/src/index.c +++ b/src/index.c @@ -116,10 +116,20 @@ Index_clear(Index *self) PyDoc_STRVAR(Index_diff_to_workdir__doc__, - "diff_to_workdir() -> Diff\n" + "diff_to_workdir([flag, context_lines, interhunk_lines]) -> Diff\n" "\n" "Return a :py:class:`~pygit2.Diff` object with the differences between the\n" - "index and the working copy.\n"); + "index and the working copy.\n" + "\n" + "Arguments:\n" + "\n" + "flag: a GIT_DIFF_* constant.\n" + "\n" + "context_lines: the number of unchanged lines that define the boundary\n" + " of a hunk (and to display before and after)\n" + "\n" + "interhunk_lines: the maximum number of unchanged lines between hunk\n" + " boundaries before the hunks will be merged into a one.\n"); PyObject * Index_diff_to_workdir(Index *self, PyObject *args) @@ -128,7 +138,8 @@ Index_diff_to_workdir(Index *self, PyObject *args) git_diff_list *diff; int err; - if (!PyArg_ParseTuple(args, "|i", &opts.flags)) + if (!PyArg_ParseTuple(args, "|IHH", &opts.flags, &opts.context_lines, + &opts.interhunk_lines)) return NULL; err = git_diff_index_to_workdir( @@ -144,10 +155,22 @@ Index_diff_to_workdir(Index *self, PyObject *args) } PyDoc_STRVAR(Index_diff_to_tree__doc__, - "diff_to_tree(tree) -> Diff\n" + "diff_to_tree(tree [, flag, context_lines, interhunk_lines]) -> Diff\n" "\n" "Return a :py:class:`~pygit2.Diff` object with the differences between the\n" - "index and the given tree.\n"); + "index and the given tree.\n" + "\n" + "Arguments:\n" + "\n" + "tree: the tree to diff.\n" + "\n" + "flag: a GIT_DIFF_* constant.\n" + "\n" + "context_lines: the number of unchanged lines that define the boundary\n" + " of a hunk (and to display before and after)\n" + "\n" + "interhunk_lines: the maximum number of unchanged lines between hunk\n" + " boundaries before the hunks will be merged into a one.\n"); PyObject * Index_diff_to_tree(Index *self, PyObject *args) @@ -159,7 +182,8 @@ Index_diff_to_tree(Index *self, PyObject *args) Tree *py_tree = NULL; - if (!PyArg_ParseTuple(args, "O!|i", &TreeType, &py_tree, &opts.flags)) + if (!PyArg_ParseTuple(args, "O!|IHH", &TreeType, &py_tree, &opts.flags, + &opts.context_lines, &opts.interhunk_lines)) return NULL; repo = git_tree_owner(py_tree->tree); diff --git a/src/tree.c b/src/tree.c index b8dee9b0a..58e27a159 100644 --- a/src/tree.c +++ b/src/tree.c @@ -272,9 +272,19 @@ Tree_getitem(Tree *self, PyObject *value) PyDoc_STRVAR(Tree_diff_to_workdir__doc__, - "diff_to_workdir([flags]) -> Diff\n" + "diff_to_workdir([flags, context_lines, interhunk_lines]) -> Diff\n" "\n" - "Show the changes between the tree and the workdir.\n"); + "Show the changes between the :py:class:`~pygit2.Tree` and the workdir.\n" + "\n" + "Arguments:\n" + "\n" + "flag: a GIT_DIFF_* constant.\n" + "\n" + "context_lines: the number of unchanged lines that define the boundary\n" + " of a hunk (and to display before and after)\n" + "\n" + "interhunk_lines: the maximum number of unchanged lines between hunk\n" + " boundaries before the hunks will be merged into a one.\n"); PyObject * Tree_diff_to_workdir(Tree *self, PyObject *args) @@ -287,7 +297,8 @@ Tree_diff_to_workdir(Tree *self, PyObject *args) Diff *py_diff; PyObject *py_obj = NULL; - if (!PyArg_ParseTuple(args, "|i", &opts.flags)) + if (!PyArg_ParseTuple(args, "|IHH", &opts.flags, &opts.context_lines, + &opts.interhunk_lines)) return NULL; repo = git_tree_owner(self->tree); @@ -301,9 +312,21 @@ Tree_diff_to_workdir(Tree *self, PyObject *args) PyDoc_STRVAR(Tree_diff_to_index__doc__, - "diff_to_index(index, [flags]) -> Diff\n" + "diff_to_index(index, [flags, context_lines, interhunk_lines]) -> Diff\n" + "\n" + "Show the changes between the index and a given :py:class:`~pygit2.Tree`.\n" + "\n" + "Arguments:\n" + "\n" + "tree: the :py:class:`~pygit2.Tree` to diff.\n" + "\n" + "flag: a GIT_DIFF_* constant.\n" "\n" - "Show the changes between the index and a given tree.\n"); + "context_lines: the number of unchanged lines that define the boundary\n" + " of a hunk (and to display before and after)\n" + "\n" + "interhunk_lines: the maximum number of unchanged lines between hunk\n" + " boundaries before the hunks will be merged into a one.\n"); PyObject * Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) @@ -318,7 +341,9 @@ Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) Diff *py_diff; Index *py_idx = NULL; - if (!PyArg_ParseTuple(args, "O!|i", &IndexType, &py_idx, &opts.flags)) + if (!PyArg_ParseTuple(args, "O!|IHH", &IndexType, &py_idx, &opts.flags, + &opts.context_lines, + &opts.interhunk_lines)) return NULL; repo = git_tree_owner(self->tree); @@ -331,10 +356,24 @@ Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) PyDoc_STRVAR(Tree_diff_to_tree__doc__, - "diff_to_tree([tree, flags, swap]) -> Diff\n" + "diff_to_tree([tree, flags, context_lines, interhunk_lines, swap]) -> Diff\n" + "\n" + "Show the changes between two trees\n" + "\n" + "Arguments:\n" + "\n" + "tree: the :py:class:`~pygit2.Tree` to diff. If no tree is given the empty\n" + " tree will be used instead.\n" + "\n" + "flag: a GIT_DIFF_* constant.\n" + "\n" + "context_lines: the number of unchanged lines that define the boundary\n" + " of a hunk (and to display before and after)\n" + "\n" + "interhunk_lines: the maximum number of unchanged lines between hunk\n" + " boundaries before the hunks will be merged into a one.\n" "\n" - "Show the changes between two trees. If no tree is given the empty tree will" - "be used instead.\n"); + "swap: instead of diffing a to b. Diff b to a.\n"); PyObject * Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds) @@ -344,14 +383,16 @@ Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds) git_tree *from, *to, *tmp; git_repository* repo; int err, swap = 0; - char *keywords[] = {"obj", "flags", "swap", NULL}; + char *keywords[] = {"obj", "flags", "context_lines", "interhunk_lines", + "swap", NULL}; Diff *py_diff; Tree *py_tree = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!ii", keywords, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!IHHi", keywords, &TreeType, &py_tree, &opts.flags, - &swap)) + &opts.context_lines, + &opts.interhunk_lines, &swap)) return NULL; repo = git_tree_owner(self->tree); From 77b5cdce5b50b9edcde3a1aed4d7117e6e47bc24 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 20 May 2013 14:58:54 +0200 Subject: [PATCH 0519/2237] Refactored: get rid of type check for treeish_to_tree in Diff --- pygit2/repository.py | 5 +++-- pygit2/utils.py | 10 ---------- test/test_diff.py | 8 ++++++-- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index d62d08a74..6a5ec386d 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -30,7 +30,6 @@ from collections import OrderedDict # Import from pygit2 -from utils import text_type from _pygit2 import Repository as _Repository from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN from _pygit2 import GIT_CHECKOUT_SAFE_CREATE @@ -180,8 +179,10 @@ def diff(self, a=None, b=None, cached=False, flags=0, context_lines=3, """ def treeish_to_tree(obj): - if isinstance(obj, text_type): + try: obj = self.revparse_single(obj) + except: + pass if isinstance(obj, Commit): return obj.tree diff --git a/pygit2/utils.py b/pygit2/utils.py index 2f6645a3b..79e60407c 100644 --- a/pygit2/utils.py +++ b/pygit2/utils.py @@ -27,13 +27,3 @@ # feel free to add utils functions here - -import sys - -# python2/3 compability types for isinstance() -if sys.version < '3': - text_type = unicode - binary_type = str -else: - text_type = str - binary_type = bytes diff --git a/test/test_diff.py b/test/test_diff.py index d69026593..7ff994682 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -196,13 +196,17 @@ def get_context_for_lines(diff): self.assertAll(lambda x: commit_a.tree[x], entries) self.assertAll(lambda x: '+' == x, get_context_for_lines(diff_swaped)) + def test_diff_revparse(self): + diff = self.repo.diff('HEAD','HEAD~6') + self.assertEqual(type(diff), pygit2.Diff) + def test_diff_tree_opts(self): commit_c = self.repo[COMMIT_SHA1_3] commit_d = self.repo[COMMIT_SHA1_4] - for opt in [pygit2.GIT_DIFF_IGNORE_WHITESPACE, + for flag in [pygit2.GIT_DIFF_IGNORE_WHITESPACE, pygit2.GIT_DIFF_IGNORE_WHITESPACE_EOL]: - diff = commit_c.tree.diff_to_tree(commit_d.tree, opt) + diff = commit_c.tree.diff_to_tree(commit_d.tree, flag) self.assertTrue(diff is not None) self.assertEqual(0, len(diff[0].hunks)) From 86aafebb6deabbf5183906f4c0c79dade410ed95 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 20 May 2013 15:13:04 +0200 Subject: [PATCH 0520/2237] Fixed: remove OrderedDict for compability reasons (python2.6) --- pygit2/repository.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index 6a5ec386d..bf0e835a5 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -27,7 +27,6 @@ # Import from the Standard Library from string import hexdigits -from collections import OrderedDict # Import from pygit2 from _pygit2 import Repository as _Repository @@ -193,26 +192,23 @@ def treeish_to_tree(obj): a = treeish_to_tree(a) or a b = treeish_to_tree(b) or b - opts = OrderedDict([ - ('flags', flags), - ('context_lines', context_lines), - ('interhunk_lines', interhunk_lines) - ]) + opt_keys = ['flags', 'context_lines', 'interhunk_lines'] + opt_values = [flags, context_lines, interhunk_lines] # Case 1: Diff tree to tree if isinstance(a, Tree) and isinstance(b, Tree): - return a.diff_to_tree(b, **opts) + return a.diff_to_tree(b, **dict(zip(opt_keys, opt_values))) # Case 2: Index to workdir elif a is None and b is None: - return self.index.diff_to_workdir(*opts.values()) + return self.index.diff_to_workdir(*opt_values) # Case 3: Diff tree to index or workdir elif isinstance(a, Tree) and b is None: if cached: - return a.diff_to_index(self.index, **opts) + return a.diff_to_index(self.index, *opt_values) else: - return a.diff_to_workdir(*opts.values()) + return a.diff_to_workdir(*opt_values) # Case 4: Diff blob to blob if isinstance(a, Blob) and isinstance(b, Blob): From 6dd14d799e4ecfe542f3b41a3e905e33429efd24 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 20 May 2013 15:38:49 +0200 Subject: [PATCH 0521/2237] Fixed: Use GIT_DIFF_NORMAL as default flag for Diff --- pygit2/repository.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index bf0e835a5..3d8521bb8 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -31,7 +31,7 @@ # Import from pygit2 from _pygit2 import Repository as _Repository from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN -from _pygit2 import GIT_CHECKOUT_SAFE_CREATE +from _pygit2 import GIT_CHECKOUT_SAFE_CREATE, GIT_DIFF_NORMAL from _pygit2 import Reference, Tree, Commit, Blob @@ -133,8 +133,8 @@ def checkout(self, refname=None, strategy=GIT_CHECKOUT_SAFE_CREATE): # # Diff # - def diff(self, a=None, b=None, cached=False, flags=0, context_lines=3, - interhunk_lines=0): + def diff(self, a=None, b=None, cached=False, flags=GIT_DIFF_NORMAL, + context_lines=3, interhunk_lines=0): """ Show changes between the working tree and the index or a tree, changes between the index and a tree, changes between two trees, or From bdff1c3145992364c2088c86415c7281616dc7f9 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 20 May 2013 15:39:17 +0200 Subject: [PATCH 0522/2237] Removed: unnecessary PyObject in tree.c --- src/tree.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tree.c b/src/tree.c index 58e27a159..b7177f352 100644 --- a/src/tree.c +++ b/src/tree.c @@ -295,7 +295,6 @@ Tree_diff_to_workdir(Tree *self, PyObject *args) int err; Diff *py_diff; - PyObject *py_obj = NULL; if (!PyArg_ParseTuple(args, "|IHH", &opts.flags, &opts.context_lines, &opts.interhunk_lines)) From 7bccfc8d26bd20e48e88a9c3e1222e8493e96870 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 21 May 2013 13:46:58 +0200 Subject: [PATCH 0523/2237] Fixed: Memleak in diff_get_patch_byindex() There was a memleak because of a missing Py_DECREF. Thanks to @delanne --- src/diff.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/diff.c b/src/diff.c index 876cddb81..f46d4bdb7 100644 --- a/src/diff.c +++ b/src/diff.c @@ -69,6 +69,7 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) int err; Hunk *py_hunk = NULL; Patch *py_patch = NULL; + PyObject *py_line_origin=NULL, *py_line=NULL; err = git_diff_get_patch(&patch, &delta, list, idx); if (err < 0) @@ -108,12 +109,16 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) if (err < 0) goto cleanup; + py_line_origin = to_unicode_n(&line_origin, 1, NULL, NULL); + py_line = to_unicode_n(line, line_len, NULL, NULL); PyList_SetItem(py_hunk->lines, j, Py_BuildValue("OO", - to_unicode_n(&line_origin, 1, NULL, NULL), - to_unicode_n(line, line_len, NULL, NULL) + py_line_origin, + py_line ) ); + Py_DECREF(py_line_origin); + Py_DECREF(py_line); } PyList_SetItem((PyObject*) py_patch->hunks, i, From b50a8d05fe8784fc21db8f839a2c79b21a7bfcb6 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 21 May 2013 14:06:44 +0200 Subject: [PATCH 0524/2237] Fixed: Memleak in Repository_config__get__() There was a memleak because of a missing Py_INCREF() for self->config. We need 2 Py_INCREF() here because one is returned and one is kept internally. Thanks @delanne for reporting and providing the patch. --- src/repository.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/repository.c b/src/repository.c index 9f9893a2b..c2c8b94a1 100644 --- a/src/repository.c +++ b/src/repository.c @@ -497,6 +497,9 @@ Repository_config__get__(Repository *self) py_config->config = config; self->config = (PyObject*)py_config; + // We need 2 refs here. + // One is returned, one is keep internally. + Py_INCREF(self->config); } else { Py_INCREF(self->config); } From d59343730665624451c47451b0d89cdd6f4a39fc Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 21 May 2013 14:11:37 +0200 Subject: [PATCH 0525/2237] Removed: unused variables in tree.c (thanks @delanne) --- src/tree.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tree.c b/src/tree.c index b7177f352..378792df6 100644 --- a/src/tree.c +++ b/src/tree.c @@ -294,8 +294,6 @@ Tree_diff_to_workdir(Tree *self, PyObject *args) git_repository* repo; int err; - Diff *py_diff; - if (!PyArg_ParseTuple(args, "|IHH", &opts.flags, &opts.context_lines, &opts.interhunk_lines)) return NULL; @@ -332,12 +330,9 @@ Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; - git_index* index; git_repository* repo; int err; - char *keywords[] = {"obj", "flags", NULL}; - Diff *py_diff; Index *py_idx = NULL; if (!PyArg_ParseTuple(args, "O!|IHH", &IndexType, &py_idx, &opts.flags, @@ -385,7 +380,6 @@ Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds) char *keywords[] = {"obj", "flags", "context_lines", "interhunk_lines", "swap", NULL}; - Diff *py_diff; Tree *py_tree = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!IHHi", keywords, From f075aa36658fb9ccd143cb249ff8be22d99306a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 24 May 2013 21:34:10 +0200 Subject: [PATCH 0526/2237] Fix some "errors" found by pyflakes --- pygit2/__init__.py | 1 - pygit2/utils.py | 29 ----------------------------- test/utils.py | 1 - 3 files changed, 31 deletions(-) delete mode 100644 pygit2/utils.py diff --git a/pygit2/__init__.py b/pygit2/__init__.py index e246ff837..e59d3c31d 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -35,7 +35,6 @@ # High level API from .repository import Repository from .version import __version__ -import pygit2.utils def init_repository(path, bare=False): diff --git a/pygit2/utils.py b/pygit2/utils.py deleted file mode 100644 index 79e60407c..000000000 --- a/pygit2/utils.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2010-2013 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. - - -# feel free to add utils functions here diff --git a/test/utils.py b/test/utils.py index 27da92acd..10a4e1719 100644 --- a/test/utils.py +++ b/test/utils.py @@ -27,7 +27,6 @@ """Test utilities for libgit2.""" -from binascii import b2a_hex import sys import os import shutil From 7db21d132ace5713dda819b225415a0f1a2bc53a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 24 May 2013 23:04:16 +0200 Subject: [PATCH 0527/2237] Revert to use "self->repo" (#228) --- src/commit.c | 8 ++++---- src/index.c | 9 +++++---- src/object.c | 4 +--- src/reference.c | 4 +--- src/tree.c | 35 +++++++++++++++++------------------ 5 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/commit.c b/src/commit.c index b8e8fbfe8..74b1ecc16 100644 --- a/src/commit.c +++ b/src/commit.c @@ -152,7 +152,7 @@ PyDoc_STRVAR(Commit_parents__doc__, "The list of parent commits."); PyObject * Commit_parents__get__(Commit *self) { - git_repository *repo; + Repository *py_repo; unsigned int i, parent_count; const git_oid *parent_oid; git_commit *parent; @@ -165,7 +165,7 @@ Commit_parents__get__(Commit *self) if (!list) return NULL; - repo = git_object_owner((git_object*)self->commit); + py_repo = self->repo; for (i=0; i < parent_count; i++) { parent_oid = git_commit_parent_id(self->commit, i); if (parent_oid == NULL) { @@ -174,13 +174,13 @@ Commit_parents__get__(Commit *self) return NULL; } - err = git_commit_lookup(&parent, repo, parent_oid); + err = git_commit_lookup(&parent, py_repo->repo, parent_oid); if (err < 0) { Py_DECREF(list); return Error_set_oid(err, parent_oid, GIT_OID_HEXSZ); } - py_parent = wrap_object((git_object*)parent, self->repo); + py_parent = wrap_object((git_object*)parent, py_repo); if (py_parent == NULL) { Py_DECREF(list); return NULL; diff --git a/src/index.c b/src/index.c index 3b637a852..3c73f2178 100644 --- a/src/index.c +++ b/src/index.c @@ -175,9 +175,9 @@ PyDoc_STRVAR(Index_diff_to_tree__doc__, PyObject * Index_diff_to_tree(Index *self, PyObject *args) { + Repository *py_repo; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; - git_repository* repo; int err; Tree *py_tree = NULL; @@ -186,12 +186,13 @@ Index_diff_to_tree(Index *self, PyObject *args) &opts.context_lines, &opts.interhunk_lines)) return NULL; - repo = git_tree_owner(py_tree->tree); - err = git_diff_tree_to_index(&diff, repo, py_tree->tree, self->index, &opts); + py_repo = py_tree->repo; + err = git_diff_tree_to_index(&diff, py_repo->repo, py_tree->tree, + self->index, &opts); if (err < 0) return Error_set(err); - return wrap_diff(diff, self->repo); + return wrap_diff(diff, py_repo); } diff --git a/src/object.c b/src/object.c index e602da185..193efdc04 100644 --- a/src/object.c +++ b/src/object.c @@ -99,15 +99,13 @@ PyDoc_STRVAR(Object_read_raw__doc__, PyObject * Object_read_raw(Object *self) { - git_repository *repo; const git_oid *oid; git_odb_object *obj; PyObject *aux; - repo = git_object_owner(self->obj); oid = git_object_id(self->obj); - obj = Repository_read_raw(repo, oid, GIT_OID_HEXSZ); + obj = Repository_read_raw(self->repo->repo, oid, GIT_OID_HEXSZ); if (obj == NULL) return NULL; diff --git a/src/reference.c b/src/reference.c index 0ed45aa3e..e01eb47a0 100644 --- a/src/reference.c +++ b/src/reference.c @@ -228,14 +228,12 @@ Reference_target__set__(Reference *self, PyObject *py_target) char *c_name; int err; git_reference *new_ref; - git_repository *repo; CHECK_REFERENCE_INT(self); /* Case 1: Direct */ if (GIT_REF_OID == git_reference_type(self->reference)) { - repo = git_reference_owner(self->reference); - err = py_oid_to_git_oid_expand(repo, py_target, &oid); + err = py_oid_to_git_oid_expand(self->repo->repo, py_target, &oid); if (err < 0) return err; diff --git a/src/tree.c b/src/tree.c index 378792df6..e0abbbd57 100644 --- a/src/tree.c +++ b/src/tree.c @@ -291,20 +291,19 @@ Tree_diff_to_workdir(Tree *self, PyObject *args) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; - git_repository* repo; + Repository *py_repo; int err; if (!PyArg_ParseTuple(args, "|IHH", &opts.flags, &opts.context_lines, &opts.interhunk_lines)) return NULL; - repo = git_tree_owner(self->tree); - err = git_diff_tree_to_workdir(&diff, repo, self->tree, &opts); - + py_repo = self->repo; + err = git_diff_tree_to_workdir(&diff, py_repo->repo, self->tree, &opts); if (err < 0) return Error_set(err); - return wrap_diff(diff, self->repo); + return wrap_diff(diff, py_repo); } @@ -330,7 +329,7 @@ Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; - git_repository* repo; + Repository *py_repo; int err; Index *py_idx = NULL; @@ -340,12 +339,13 @@ Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) &opts.interhunk_lines)) return NULL; - repo = git_tree_owner(self->tree); - err = git_diff_tree_to_index(&diff, repo, self->tree, py_idx->index, &opts); + py_repo = self->repo; + err = git_diff_tree_to_index(&diff, py_repo->repo, self->tree, + py_idx->index, &opts); if (err < 0) return Error_set(err); - return wrap_diff(diff, self->repo); + return wrap_diff(diff, py_repo); } @@ -375,10 +375,10 @@ Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds) git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; git_tree *from, *to, *tmp; - git_repository* repo; + Repository *py_repo; int err, swap = 0; char *keywords[] = {"obj", "flags", "context_lines", "interhunk_lines", - "swap", NULL}; + "swap", NULL}; Tree *py_tree = NULL; @@ -388,21 +388,20 @@ Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds) &opts.interhunk_lines, &swap)) return NULL; - repo = git_tree_owner(self->tree); + py_repo = self->repo; to = (py_tree == NULL) ? NULL : py_tree->tree; from = self->tree; if (swap > 0) { - tmp = from; - from = to; - to = tmp; + tmp = from; + from = to; + to = tmp; } - err = git_diff_tree_to_tree(&diff, repo, from, to, &opts); - + err = git_diff_tree_to_tree(&diff, py_repo->repo, from, to, &opts); if (err < 0) return Error_set(err); - return wrap_diff(diff, self->repo); + return wrap_diff(diff, py_repo); } From 5e9c42908318317251925a07d3d70d0331c622cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 24 May 2013 23:08:08 +0200 Subject: [PATCH 0528/2237] Remove trailing whitespace --- docs/objects.rst | 2 +- docs/oid.rst | 10 +++++----- src/pygit2.c | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index 970a0c3e5..36edf958f 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -129,7 +129,7 @@ them to the Git object database: There are also some functions to calculate the oid for a byte string without creating the blob object: - + .. autofunction:: pygit2.hash .. autofunction:: pygit2.hashfile diff --git a/docs/oid.rst b/docs/oid.rst index 3df5d3afd..1ffc08c61 100644 --- a/docs/oid.rst +++ b/docs/oid.rst @@ -47,17 +47,17 @@ The Oid type The constructor expects either a raw or a hex oid, but not both. An Oid object is created from the hexadecimal form this way:: - + >>> from pygit2 import Oid - + >>> hex = "cff3ceaefc955f0dbe1957017db181bc49913781" >>> oid1 = Oid(hex=hex) - + An Oid object is created from the raw form this way:: - + >>> from binascii import unhexlify >>> from pygit2 import Oid - + >>> raw = unhexlify("cff3ceaefc955f0dbe1957017db181bc49913781") >>> oid2 = Oid(raw=raw) diff --git a/src/pygit2.c b/src/pygit2.c index 94bb06db7..758a96c4c 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -137,8 +137,8 @@ clone_repository(PyObject *self, PyObject *args) { const char *push_spec, *checkout_branch; int err; - if (!PyArg_ParseTuple(args, "zzIzzzzz", - &url, &path, &bare, &remote_name, &push_url, + if (!PyArg_ParseTuple(args, "zzIzzzzz", + &url, &path, &bare, &remote_name, &push_url, &fetch_spec, &push_spec, &checkout_branch)) return NULL; From 5904a8752cc2b45b826ffb3d464600c46a1b7a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 24 May 2013 23:10:55 +0200 Subject: [PATCH 0529/2237] docs: fix warning --- pygit2/repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index 3d8521bb8..ba40b2abc 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -150,7 +150,7 @@ def diff(self, a=None, b=None, cached=False, flags=GIT_DIFF_NORMAL, context_lines the number of unchanged lines that define the boundary - of a hunk (and to display before and after)\n" + of a hunk (and to display before and after) interhunk_lines the maximum number of unchanged lines between hunk From 98551e3e415dc5b5db615681b5812ecb1fce774b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 24 May 2013 23:30:27 +0200 Subject: [PATCH 0530/2237] Updae list of authors --- .mailmap | 1 + README.rst | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index f08f7b8db..da003e239 100644 --- a/.mailmap +++ b/.mailmap @@ -3,3 +3,4 @@ Richo Healey Xavier Delannoy Christian Boos Martin Lenders +Xu Tao diff --git a/README.rst b/README.rst index b0bce5741..f421b76cf 100644 --- a/README.rst +++ b/README.rst @@ -53,13 +53,14 @@ This is the list of authors of pygit2, sorted by number of commits (as shown by - W Trevor King - Dave Borowitz - Carlos Martín Nieto +- Richo Healey - Christian Boos - Julien Miotte -- Richo Healey - Martin Lenders - Xavier Delannoy - Yonggang Luo - Valentin Haenel +- Bernardo Heynemann - John Szakmeister - David Versmisse - Petr Hosek @@ -68,7 +69,10 @@ This is the list of authors of pygit2, sorted by number of commits (as shown by - Petr Viktorin - Alex Chamberlain - Amit Bakshi +- Andrey Devyatkin - Ben Davis +- Daniel Rodríguez Troitiño +- Hervé Cauwelier - Jared Flatow - Sarath Lakshman - Vicent Marti @@ -82,11 +86,13 @@ This is the list of authors of pygit2, sorted by number of commits (as shown by - Eric Schrijver - Erik van Zijst - Ferengee +- Fraser Tweedale - Hugh Cole-Baker - Josh Bleecher Snyder - Jun Omae - Ridge Kennedy - Rui Abreu Ferreira +- Xu Tao - pistacchio From a626b3c2974778f8ab2db958d7d240e607f7275d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Sun, 26 May 2013 01:00:02 +0200 Subject: [PATCH 0531/2237] Fix memory leak in Repository_head__set__ --- src/repository.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/repository.c b/src/repository.c index c2c8b94a1..77327b0fe 100644 --- a/src/repository.c +++ b/src/repository.c @@ -175,13 +175,14 @@ int Repository_head__set__(Repository *self, PyObject *py_refname) { int err; - const char *refname; + char *refname; refname = py_str_to_c_str(py_refname, NULL); if (refname == NULL) return -1; err = git_repository_set_head(self->repo, refname); + free(refname); if (err < 0) { Error_set_str(err, refname); return -1; From d388d1ab2168cac517d1d3e29c441fab6ef8a3b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Sun, 26 May 2013 01:00:33 +0200 Subject: [PATCH 0532/2237] Fix memory leak in TreeBuilder_get --- src/treebuilder.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/treebuilder.c b/src/treebuilder.c index 1724f14ee..94036a82f 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -108,6 +108,7 @@ TreeBuilder_get(TreeBuilder *self, PyObject *py_filename) return NULL; entry = git_treebuilder_get(self->bld, filename); + free(filename); if (entry == NULL) Py_RETURN_NONE; From c3335f92eeefa533d254991f99b229e56a4d1eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 26 May 2013 10:26:10 +0200 Subject: [PATCH 0533/2237] tests: get nice error when test module is broken --- test/__init__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/__init__.py b/test/__init__.py index 024476b75..b22439347 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -41,10 +41,14 @@ def test_suite(): # Sometimes importing pygit2 fails, we try this first to get an - # informative traceback + # informative traceback. import pygit2 - # Go + # Check the test modules import correctly, to get a nice error if one + # does not. modules = ['test.test_%s' % n for n in names] + for module in modules: + __import__(module) + # Go return unittest.defaultTestLoader.loadTestsFromNames(modules) From 74a1fdab60962079a03ef945e081ef5381d63868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 26 May 2013 10:32:05 +0200 Subject: [PATCH 0534/2237] Fixing coding style with the help of pep8 (wip) --- .pep8 | 3 +++ setup.py | 12 ++++++------ test/test_blob.py | 10 +++++----- test/test_diff.py | 13 +++++++------ test/test_index.py | 2 +- test/test_refs.py | 11 ++++++----- test/test_remote.py | 4 ++-- test/test_status.py | 28 ---------------------------- 8 files changed, 30 insertions(+), 53 deletions(-) create mode 100644 .pep8 diff --git a/.pep8 b/.pep8 new file mode 100644 index 000000000..92c073b46 --- /dev/null +++ b/.pep8 @@ -0,0 +1,3 @@ +[pep8] +exclude = .git,build,docs +ignore = diff --git a/setup.py b/setup.py index e38ebec7a..0aad84dc1 100644 --- a/setup.py +++ b/setup.py @@ -31,13 +31,15 @@ from __future__ import print_function import codecs -import os -from subprocess import Popen, PIPE -import sys from distutils.core import setup, Extension, Command from distutils.command.build import build from distutils.command.sdist import sdist from distutils import log +import os +import shlex +from subprocess import Popen, PIPE +import sys +import unittest # Read version from local pygit2/version.py without pulling in # pygit2/__init__.py @@ -85,11 +87,9 @@ def finalize_options(self): def run(self): self.run_command('build') bld = self.distribution.get_command_obj('build') - #Add build_lib in to sys.path so that unittest can found DLLs and libs + # Add build_lib in to sys.path so that unittest can found DLLs and libs sys.path = [os.path.abspath(bld.build_lib)] + sys.path - import shlex - import unittest test_argv0 = [sys.argv[0] + ' test --args='] # For transfering args to unittest, we have to split args by ourself, # so that command like: diff --git a/test/test_blob.py b/test/test_blob.py index a5659a2d8..8105455f7 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -29,7 +29,7 @@ from __future__ import absolute_import from __future__ import unicode_literals -import os +from os.path import dirname, join import unittest import pygit2 @@ -91,14 +91,14 @@ def test_create_blob_fromworkdir(self): self.assertEqual(len(BLOB_FILE_CONTENT), blob.size) self.assertEqual(BLOB_FILE_CONTENT, blob.read_raw()) - def test_create_blob_outside_workdir(self): - path = os.path.join(os.path.dirname(__file__), 'data', self.repo_dir + '.tar') + def test_create_blob_outside_workdir(self): + path = join(dirname(__file__), 'data', self.repo_dir + '.tar') self.assertRaises(KeyError, self.repo.create_blob_fromworkdir, path) - def test_create_blob_fromdisk(self): - path = os.path.join(os.path.dirname(__file__), 'data', self.repo_dir + '.tar') + def test_create_blob_fromdisk(self): + path = join(dirname(__file__), 'data', self.repo_dir + '.tar') blob_oid = self.repo.create_blob_fromdisk(path) blob = self.repo[blob_oid] diff --git a/test/test_diff.py b/test/test_diff.py index 7ff994682..09ce99a1b 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -32,6 +32,7 @@ import unittest import pygit2 from pygit2 import GIT_DIFF_INCLUDE_UNMODIFIED +from pygit2 import GIT_DIFF_IGNORE_WHITESPACE, GIT_DIFF_IGNORE_WHITESPACE_EOL from . import utils from itertools import chain @@ -183,9 +184,9 @@ def test_diff_empty_tree(self): diff = commit_a.tree.diff_to_tree() def get_context_for_lines(diff): - hunks = chain(*map(lambda x: x.hunks, [p for p in diff])) - lines = chain(*map(lambda x: x.lines, hunks)) - return map(lambda x: x[0], lines) + hunks = chain(*map(lambda x: x.hunks, [p for p in diff])) + lines = chain(*map(lambda x: x.lines, hunks)) + return map(lambda x: x[0], lines) entries = [p.new_file_path for p in diff] self.assertAll(lambda x: commit_a.tree[x], entries) @@ -204,8 +205,8 @@ def test_diff_tree_opts(self): commit_c = self.repo[COMMIT_SHA1_3] commit_d = self.repo[COMMIT_SHA1_4] - for flag in [pygit2.GIT_DIFF_IGNORE_WHITESPACE, - pygit2.GIT_DIFF_IGNORE_WHITESPACE_EOL]: + for flag in [GIT_DIFF_IGNORE_WHITESPACE, + GIT_DIFF_IGNORE_WHITESPACE_EOL]: diff = commit_c.tree.diff_to_tree(commit_d.tree, flag) self.assertTrue(diff is not None) self.assertEqual(0, len(diff[0].hunks)) @@ -277,7 +278,7 @@ def test_find_similar(self): #~ Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate #~ --find-copies-harder during rename transformion... diff = commit_a.tree.diff_to_tree(commit_b.tree, - GIT_DIFF_INCLUDE_UNMODIFIED) + GIT_DIFF_INCLUDE_UNMODIFIED) self.assertAll(lambda x: x.status != 'R', diff) diff.find_similar() self.assertAny(lambda x: x.status == 'R', diff) diff --git a/test/test_index.py b/test/test_index.py index 39f41b8e8..8613020e4 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -129,7 +129,7 @@ def test_mode(self): def test_bare_index(self): index = pygit2.Index(os.path.join(self.repo.path, 'index')) self.assertEqual([x.hex for x in index], - [x.hex for x in self.repo.index]) + [x.hex for x in self.repo.index]) self.assertRaises(pygit2.GitError, lambda: index.add('bye.txt')) diff --git a/test/test_refs.py b/test/test_refs.py index 3c2af0c21..ecad5f632 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -180,26 +180,27 @@ def test_create_reference(self): # try to create existing reference with force reference = self.repo.create_reference('refs/tags/version1', - LAST_COMMIT, force=True) + LAST_COMMIT, force=True) self.assertEqual(reference.target.hex, LAST_COMMIT) def test_create_symbolic_reference(self): + repo = self.repo # We add a tag as a new symbolic reference that always points to # "refs/heads/master" - reference = self.repo.create_reference('refs/tags/beta', + reference = repo.create_reference('refs/tags/beta', 'refs/heads/master') self.assertEqual(reference.type, GIT_REF_SYMBOLIC) self.assertEqual(reference.target, 'refs/heads/master') # try to create existing symbolic reference - self.assertRaises(ValueError, self.repo.create_reference, + self.assertRaises(ValueError, repo.create_reference, 'refs/tags/beta', 'refs/heads/master') # try to create existing symbolic reference with force - reference = self.repo.create_reference('refs/tags/beta', - 'refs/heads/master', force=True) + reference = repo.create_reference('refs/tags/beta', + 'refs/heads/master', force=True) self.assertEqual(reference.type, GIT_REF_SYMBOLIC) self.assertEqual(reference.target, 'refs/heads/master') diff --git a/test/test_remote.py b/test/test_remote.py index 6e5a150af..279c8110a 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -44,7 +44,7 @@ def test_remote_create(self): name = 'upstream' url = 'git://github.com/libgit2/pygit2.git' - remote = self.repo.create_remote(name, url); + remote = self.repo.create_remote(name, url) self.assertEqual(type(remote), pygit2.Remote) self.assertEqual(name, remote.name) @@ -94,7 +94,7 @@ def test_remote_list(self): name = 'upstream' url = 'git://github.com/libgit2/pygit2.git' - remote = self.repo.create_remote(name, url); + remote = self.repo.create_remote(name, url) self.assertTrue(remote.name in [x.name for x in self.repo.remotes]) diff --git a/test/test_status.py b/test/test_status.py index c1b350f06..98a7a7099 100644 --- a/test/test_status.py +++ b/test/test_status.py @@ -35,34 +35,6 @@ from . import utils -EXPECTED = { - "current_file": pygit2.GIT_STATUS_CURRENT, - "file_deleted": pygit2.GIT_STATUS_WT_DELETED, - "modified_file": pygit2.GIT_STATUS_WT_MODIFIED, - "new_file": pygit2.GIT_STATUS_WT_NEW, - - "staged_changes": pygit2.GIT_STATUS_INDEX_MODIFIED, - "staged_changes_file_deleted": pygit2.GIT_STATUS_INDEX_MODIFIED | - pygit2.GIT_STATUS_WT_DELETED, - "staged_changes_file_modified": pygit2.GIT_STATUS_INDEX_MODIFIED | - pygit2.GIT_STATUS_WT_MODIFIED, - - "staged_delete": pygit2.GIT_STATUS_INDEX_DELETED, - "staged_delete_file_modified": pygit2.GIT_STATUS_INDEX_DELETED | - pygit2.GIT_STATUS_WT_NEW, - "staged_new": pygit2.GIT_STATUS_INDEX_NEW, - - "staged_new_file_deleted": pygit2.GIT_STATUS_INDEX_NEW | - pygit2.GIT_STATUS_WT_DELETED, - "staged_new_file_modified": pygit2.GIT_STATUS_INDEX_NEW | - pygit2.GIT_STATUS_WT_MODIFIED, - - "subdir/current_file": pygit2.GIT_STATUS_CURRENT, - "subdir/deleted_file": pygit2.GIT_STATUS_WT_DELETED, - "subdir/modified_file": pygit2.GIT_STATUS_WT_MODIFIED, - "subdir/new_file": pygit2.GIT_STATUS_WT_NEW, -} - class StatusTest(utils.DirtyRepoTestCase): def test_status(self): From 19c9a895d54306aaf2ecd5dd6147f0b7989eb04c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 26 May 2013 13:49:08 +0200 Subject: [PATCH 0535/2237] tests: find out test modules automatically --- test/__init__.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/test/__init__.py b/test/__init__.py index b22439347..1cbb37d02 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -31,23 +31,26 @@ manually. """ +from os import listdir +from os.path import dirname import sys import unittest -names = ['blob', 'commit', 'config', 'diff', 'index', 'note', 'oid', 'refs', - 'remote', 'repository', 'revwalk', 'signature', 'status', 'tag', - 'tree', 'treebuilder'] - def test_suite(): # Sometimes importing pygit2 fails, we try this first to get an # informative traceback. import pygit2 - # Check the test modules import correctly, to get a nice error if one - # does not. - modules = ['test.test_%s' % n for n in names] - for module in modules: - __import__(module) + + # Build the list of modules + modules = [] + for name in listdir(dirname(__file__)): + if name.startswith('test_') and name.endswith('.py'): + module = 'test.%s' % name[:-3] + # Check the module imports correctly, have a nice error otherwise + __import__(module) + modules.append(module) + # Go return unittest.defaultTestLoader.loadTestsFromNames(modules) From 0c86307eb53dcea7942e11be0710d62eda54a48d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 26 May 2013 14:39:12 +0200 Subject: [PATCH 0536/2237] Coding style: Silent pep8 --- .pep8 | 2 +- pygit2/repository.py | 3 +-- test/test_config.py | 39 ++++++++++++++++++++++----------------- test/test_diff.py | 3 ++- test/test_note.py | 4 ++-- test/test_oid.py | 1 + test/test_refs.py | 4 ++-- test/test_remote.py | 1 + test/test_revwalk.py | 19 +++++++------------ test/test_treebuilder.py | 1 + 10 files changed, 40 insertions(+), 37 deletions(-) diff --git a/.pep8 b/.pep8 index 92c073b46..a5a00a5d7 100644 --- a/.pep8 +++ b/.pep8 @@ -1,3 +1,3 @@ [pep8] exclude = .git,build,docs -ignore = +ignore = E303 diff --git a/pygit2/repository.py b/pygit2/repository.py index ba40b2abc..d235b8dc8 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -83,8 +83,7 @@ def create_reference(self, name, target, force=False): type(target) is Oid or ( all(c in hexdigits for c in target) - and GIT_OID_MINPREFIXLEN <= len(target) <= GIT_OID_HEXSZ) - ) + and GIT_OID_MINPREFIXLEN <= len(target) <= GIT_OID_HEXSZ)) if direct: return self.create_reference_direct(name, target, force) diff --git a/test/test_config.py b/test/test_config.py index 9c7e13d3d..a8afe86cb 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -30,22 +30,24 @@ import os import unittest -import pygit2 +from pygit2 import Config from . import utils -config_filename = "test_config" +CONFIG_FILENAME = "test_config" + def foreach_test_wrapper(key, name, lst): lst[key] = name return 0 foreach_test_wrapper.__test__ = False + class ConfigTest(utils.RepoTestCase): def tearDown(self): try: - os.remove(config_filename) + os.remove(CONFIG_FILENAME) except OSError: pass @@ -54,40 +56,43 @@ def test_config(self): def test_global_config(self): try: - self.assertNotEqual(None, pygit2.Config.get_global_config()) - except IOError: # there is no user config + self.assertNotEqual(None, Config.get_global_config()) + except IOError: + # There is no user config pass def test_system_config(self): try: - self.assertNotEqual(None, pygit2.Config.get_system_config()) - except IOError: # there is no system config + self.assertNotEqual(None, Config.get_system_config()) + except IOError: + # There is no system config pass def test_new(self): - open(config_filename, 'w').close() # touch file - config_write = pygit2.Config(config_filename) + # Touch file + open(CONFIG_FILENAME, 'w').close() + config_write = Config(CONFIG_FILENAME) self.assertNotEqual(config_write, None) config_write['core.bare'] = False config_write['core.editor'] = 'ed' - config_read = pygit2.Config(config_filename) + config_read = Config(CONFIG_FILENAME) self.assertTrue('core.bare' in config_read) self.assertFalse(config_read['core.bare']) self.assertTrue('core.editor' in config_read) self.assertEqual(config_read['core.editor'], 'ed') def test_add(self): - config = pygit2.Config() + config = Config() - new_file = open(config_filename, "w") + new_file = open(CONFIG_FILENAME, "w") new_file.write("[this]\n\tthat = true\n") new_file.write("[something \"other\"]\n\there = false") new_file.close() - config.add_file(config_filename, 0) + config.add_file(CONFIG_FILENAME, 0) self.assertTrue('this.that' in config) self.assertTrue(config['this.that']) self.assertTrue('something.other.here' in config) @@ -110,11 +115,11 @@ def test_read(self): self.assertTrue('core.repositoryformatversion' in config) self.assertEqual(config['core.repositoryformatversion'], 0) - new_file = open(config_filename, "w") + new_file = open(CONFIG_FILENAME, "w") new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n") new_file.close() - config.add_file(config_filename, 0) + config.add_file(CONFIG_FILENAME, 0) self.assertTrue('this.that' in config) self.assertEqual(len(config.get_multivar('this.that')), 2) l = config.get_multivar('this.that', 'bar') @@ -149,11 +154,11 @@ def test_write(self): del config['core.dummy3'] self.assertFalse('core.dummy3' in config) - new_file = open(config_filename, "w") + new_file = open(CONFIG_FILENAME, "w") new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n") new_file.close() - config.add_file(config_filename, 5) + config.add_file(CONFIG_FILENAME, 5) self.assertTrue('this.that' in config) l = config.get_multivar('this.that', 'foo.*') self.assertEqual(len(l), 2) diff --git a/test/test_diff.py b/test/test_diff.py index 09ce99a1b..697b8be19 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -100,6 +100,7 @@ + a contents """ + class DiffDirtyTest(utils.DirtyRepoTestCase): def test_diff_empty_index(self): repo = self.repo @@ -198,7 +199,7 @@ def get_context_for_lines(diff): self.assertAll(lambda x: '+' == x, get_context_for_lines(diff_swaped)) def test_diff_revparse(self): - diff = self.repo.diff('HEAD','HEAD~6') + diff = self.repo.diff('HEAD', 'HEAD~6') self.assertEqual(type(diff), pygit2.Diff) def test_diff_tree_opts(self): diff --git a/test/test_note.py b/test/test_note.py index d6ea11f16..a0ef44385 100644 --- a/test/test_note.py +++ b/test/test_note.py @@ -40,8 +40,8 @@ ('ab533997b80705767be3dae8cbb06a0740809f79', 'First Note - HEAD\n', '784855caf26449a1914d2cf62d12b9374d76ae78'), ('d879714d880671ed84f8aaed8b27fca23ba01f27', 'Second Note - HEAD~1\n', - 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87') - ] + 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87')] + class NotesTest(utils.BareRepoTestCase): diff --git a/test/test_oid.py b/test/test_oid.py index c303ce611..e631399b2 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -44,6 +44,7 @@ HEX = "15b648aec6ed045b5ca6f57f8b7831a8b4757298" RAW = unhexlify(HEX.encode('ascii')) + class OidTest(utils.BareRepoTestCase): def test_raw(self): diff --git a/test/test_refs.py b/test/test_refs.py index ecad5f632..3b0a660f6 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -179,8 +179,8 @@ def test_create_reference(self): 'refs/tags/version1', LAST_COMMIT) # try to create existing reference with force - reference = self.repo.create_reference('refs/tags/version1', - LAST_COMMIT, force=True) + reference = self.repo.create_reference('refs/tags/version1', + LAST_COMMIT, force=True) self.assertEqual(reference.target.hex, LAST_COMMIT) diff --git a/test/test_remote.py b/test/test_remote.py index 279c8110a..d0ddebc2b 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -39,6 +39,7 @@ REMOTE_REPO_OBJECTS = 30 REMOTE_REPO_BYTES = 2758 + class RepositoryTest(utils.RepoTestCase): def test_remote_create(self): name = 'upstream' diff --git a/test/test_revwalk.py b/test/test_revwalk.py index dd97b8280..119f22253 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -69,13 +69,11 @@ class WalkerTest(utils.RepoTestCase): def test_walk(self): walker = self.repo.walk(log[0], GIT_SORT_TIME) - out = [ x.hex for x in walker ] - self.assertEqual(out, log) + self.assertEqual([x.hex for x in walker], log) def test_reverse(self): walker = self.repo.walk(log[0], GIT_SORT_TIME | GIT_SORT_REVERSE) - out = [ x.hex for x in walker ] - self.assertEqual(out, list(reversed(log))) + self.assertEqual([x.hex for x in walker], list(reversed(log))) def test_hide(self): walker = self.repo.walk(log[0], GIT_SORT_TIME) @@ -90,23 +88,20 @@ def test_hide_prefix(self): def test_reset(self): walker = self.repo.walk(log[0], GIT_SORT_TIME) walker.reset() - out = [ x.hex for x in walker ] - self.assertEqual(out, []) + self.assertEqual([x.hex for x in walker], []) def test_push(self): walker = self.repo.walk(log[-1], GIT_SORT_TIME) - out = [ x.hex for x in walker ] - self.assertEqual(out, log[-1:]) + self.assertEqual([x.hex for x in walker], log[-1:]) walker.reset() walker.push(log[0]) - out = [ x.hex for x in walker ] - self.assertEqual(out, log) + self.assertEqual([x.hex for x in walker], log) def test_sort(self): walker = self.repo.walk(log[0], GIT_SORT_TIME) walker.sort(GIT_SORT_TIME | GIT_SORT_REVERSE) - out = [ x.hex for x in walker ] - self.assertEqual(out, list(reversed(log))) + self.assertEqual([x.hex for x in walker], list(reversed(log))) + if __name__ == '__main__': unittest.main() diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index 51e7a1062..6b2d99adc 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -36,6 +36,7 @@ TREE_SHA = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' + class TreeBuilderTest(utils.BareRepoTestCase): def test_new_empty_treebuilder(self): From f2b16445103572865298eab6ec12577c95f67d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Fri, 17 May 2013 21:39:51 +0200 Subject: [PATCH 0537/2237] A basic Branch type (subtype of Reference) --- src/branch.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/branch.h | 35 +++++++++++++++++++ src/pygit2.c | 7 ++++ src/types.h | 2 ++ 4 files changed, 141 insertions(+) create mode 100644 src/branch.c create mode 100644 src/branch.h diff --git a/src/branch.c b/src/branch.c new file mode 100644 index 000000000..20e408554 --- /dev/null +++ b/src/branch.c @@ -0,0 +1,97 @@ +/* + * Copyright 2010-2013 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. + */ + +#include "types.h" +#include "reference.h" + +PyMethodDef Branch_methods[] = { + {NULL} +}; + +PyGetSetDef Branch_getseters[] = { + {NULL} +}; + +PyDoc_STRVAR(Branch__doc__, "Branch."); + +PyTypeObject BranchType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.Branch", /* tp_name */ + sizeof(Branch), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* 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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + Branch__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Branch_methods, /* tp_methods */ + 0, /* tp_members */ + Branch_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 */ +}; + +PyObject * +wrap_branch(git_reference *c_reference, Repository *repo) +{ + Branch *py_branch=NULL; + + py_branch = PyObject_New(Branch, &BranchType); + if (py_branch) { + py_branch->reference = c_reference; + if (repo) { + py_branch->repo = repo; + Py_INCREF(repo); + } + } + + return (PyObject *)py_branch; +} diff --git a/src/branch.h b/src/branch.h new file mode 100644 index 000000000..ecec8761b --- /dev/null +++ b/src/branch.h @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2013 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. + */ + +#ifndef INCLUDE_pygit2_branch_h +#define INCLUDE_pygit2_branch_h + +#include + +PyObject* wrap_branch(git_reference *c_reference, Repository *repo); + +#endif diff --git a/src/pygit2.c b/src/pygit2.c index 758a96c4c..19a83879a 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -59,6 +59,7 @@ extern PyTypeObject ConfigType; extern PyTypeObject ReferenceType; extern PyTypeObject RefLogIterType; extern PyTypeObject RefLogEntryType; +extern PyTypeObject BranchType; extern PyTypeObject SignatureType; extern PyTypeObject RemoteType; extern PyTypeObject NoteType; @@ -331,6 +332,12 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_REF_SYMBOLIC) ADD_CONSTANT_INT(m, GIT_REF_LISTALL) + /* + * Branches + */ + INIT_TYPE(BranchType, &ReferenceType, PyType_GenericNew); + ADD_TYPE(m, Branch) + /* * Index & Working copy */ diff --git a/src/types.h b/src/types.h index ce2714220..926d74ad2 100644 --- a/src/types.h +++ b/src/types.h @@ -160,6 +160,8 @@ SIMPLE_TYPE(Walker, git_revwalk, walk) SIMPLE_TYPE(Reference, git_reference, reference) +typedef Reference Branch; + typedef struct { PyObject_HEAD git_signature *signature; From 1062ab638b9b6dd145ea34cc8fb6b362e49b1af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Fri, 17 May 2013 22:00:52 +0200 Subject: [PATCH 0538/2237] Implement Repository.lookup_branch. Constants for local and remote branches. --- pygit2/repository.py | 1 + src/pygit2.c | 2 ++ src/repository.c | 29 ++++++++++++++++++++ test/test_branch.py | 64 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 test/test_branch.py diff --git a/pygit2/repository.py b/pygit2/repository.py index d235b8dc8..fbb77ced3 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -30,6 +30,7 @@ # Import from pygit2 from _pygit2 import Repository as _Repository +from _pygit2 import GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN from _pygit2 import GIT_CHECKOUT_SAFE_CREATE, GIT_DIFF_NORMAL from _pygit2 import Reference, Tree, Commit, Blob diff --git a/src/pygit2.c b/src/pygit2.c index 19a83879a..a896dcbb1 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -337,6 +337,8 @@ moduleinit(PyObject* m) */ INIT_TYPE(BranchType, &ReferenceType, PyType_GenericNew); ADD_TYPE(m, Branch) + ADD_CONSTANT_INT(m, GIT_BRANCH_LOCAL) + ADD_CONSTANT_INT(m, GIT_BRANCH_REMOTE) /* * Index & Working copy diff --git a/src/repository.c b/src/repository.c index 77327b0fe..6b3b279d6 100644 --- a/src/repository.c +++ b/src/repository.c @@ -36,6 +36,7 @@ #include "note.h" #include "repository.h" #include "remote.h" +#include "branch.h" #include extern PyObject *GitError; @@ -274,6 +275,33 @@ Repository_git_object_lookup_prefix(Repository *self, PyObject *key) } +PyDoc_STRVAR(Repository_lookup_branch__doc__, + "lookup_branch(branch_name, [branch_type]) -> Object\n" + "\n" + "Returns the Git reference for the given branch name (local or remote)."); + +PyObject * +Repository_lookup_branch(Repository *self, PyObject *args) +{ + git_reference *c_reference; + const char *c_name; + git_branch_t branch_type = GIT_BRANCH_LOCAL; + int err; + + if (!PyArg_ParseTuple(args, "s|I", &c_name, &branch_type)) + return NULL; + + err = git_branch_lookup(&c_reference, self->repo, c_name, branch_type); + if (err == 0) + return wrap_branch(c_reference, self); + + if (err == GIT_ENOTFOUND) + Py_RETURN_NONE; + + return Error_set(err); +} + + PyDoc_STRVAR(Repository_revparse_single__doc__, "revparse_single(revision) -> Object\n" "\n" @@ -1321,6 +1349,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, create_note, METH_VARARGS), METHOD(Repository, lookup_note, METH_VARARGS), METHOD(Repository, git_object_lookup_prefix, METH_O), + METHOD(Repository, lookup_branch, METH_VARARGS), {NULL} }; diff --git a/test/test_branch.py b/test/test_branch.py new file mode 100644 index 000000000..8c6a45df3 --- /dev/null +++ b/test/test_branch.py @@ -0,0 +1,64 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2010-2013 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. + +"""Tests for branch methods.""" + +from __future__ import absolute_import +from __future__ import unicode_literals +import unittest + +import pygit2 +from . import utils + +LAST_COMMIT = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' +I18N_LAST_COMMIT = '5470a671a80ac3789f1a6a8cefbcf43ce7af0563' +ORIGIN_MASTER_COMMIT = '784855caf26449a1914d2cf62d12b9374d76ae78' + +class BranchesTestCase(utils.RepoTestCase): + def test_lookup_branch_local(self): + branch = self.repo.lookup_branch('master') + self.assertEqual(branch.target.hex, LAST_COMMIT) + + branch = self.repo.lookup_branch('i18n', pygit2.GIT_BRANCH_LOCAL) + self.assertEqual(branch.target.hex, I18N_LAST_COMMIT) + + self.assertTrue(self.repo.lookup_branch('not-exists') is None) + + +class BranchesEmptyRepoTestCase(utils.EmptyRepoTestCase): + def test_lookup_branch_remote(self): + remote = self.repo.remotes[0] + remote.fetch() + + branch = self.repo.lookup_branch('origin/master', pygit2.GIT_BRANCH_REMOTE) + self.assertEqual(branch.target.hex, ORIGIN_MASTER_COMMIT) + + self.assertTrue(self.repo.lookup_branch('origin/not-exists', pygit2.GIT_BRANCH_REMOTE) is None) + + +if __name__ == '__main__': + unittest.main() From 7b5adf1da2561c06e590ef0f61674c1d4502bfbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Fri, 17 May 2013 22:26:16 +0200 Subject: [PATCH 0539/2237] Implement Repository.listall_branches. --- src/repository.c | 72 +++++++++++++++++++++++++++++++++++++++++++++ src/repository.h | 1 + test/test_branch.py | 14 ++++++++- 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/repository.c b/src/repository.c index 6b3b279d6..d4bebdffb 100644 --- a/src/repository.c +++ b/src/repository.c @@ -886,6 +886,77 @@ Repository_listall_references(Repository *self, PyObject *args) } +PyDoc_STRVAR(Repository_listall_branches__doc__, + "listall_branches([flags]) -> (str, ...)\n" + "\n" + "Return a tuple with all the branches in the repository."); + +struct branch_foreach_s { + PyObject *tuple; + Py_ssize_t pos; +}; + +int +branch_foreach_cb(const char *branch_name, git_branch_t branch_type, void *payload) +{ + /* This is the callback that will be called in git_branch_foreach. It + * will be called for every branch. + * payload is a struct branch_foreach_s. + */ + int err; + struct branch_foreach_s *payload_s = (struct branch_foreach_s *)payload; + + if (PyTuple_Size(payload_s->tuple) <= payload_s->pos) + { + err = _PyTuple_Resize(&(payload_s->tuple), payload_s->pos * 2); + if (err) { + Py_CLEAR(payload_s->tuple); + return GIT_ERROR; + } + } + + PyObject *py_branch_name = to_path(branch_name); + if (py_branch_name == NULL) { + Py_CLEAR(payload_s->tuple); + return GIT_ERROR; + } + + PyTuple_SET_ITEM(payload_s->tuple, payload_s->pos++, py_branch_name); + + return GIT_OK; +} + + +PyObject * +Repository_listall_branches(Repository *self, PyObject *args) +{ + unsigned int list_flags = GIT_BRANCH_LOCAL; + int err; + + /* 1- Get list_flags */ + if (!PyArg_ParseTuple(args, "|I", &list_flags)) + return NULL; + + /* 2- Get the C result */ + struct branch_foreach_s payload; + payload.tuple = PyTuple_New(4); + if (payload.tuple == NULL) + return NULL; + + payload.pos = 0; + err = git_branch_foreach(self->repo, list_flags, branch_foreach_cb, &payload); + if (err != GIT_OK) + return Error_set(err); + + /* 3- Trim the tuple */ + err = _PyTuple_Resize(&payload.tuple, payload.pos); + if (err) + return Error_set(err); + + return payload.tuple; +} + + PyDoc_STRVAR(Repository_lookup_reference__doc__, "lookup_reference(name) -> Reference\n" "\n" @@ -1350,6 +1421,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, lookup_note, METH_VARARGS), METHOD(Repository, git_object_lookup_prefix, METH_O), METHOD(Repository, lookup_branch, METH_VARARGS), + METHOD(Repository, listall_branches, METH_VARARGS), {NULL} }; diff --git a/src/repository.h b/src/repository.h index 4cb7d8a1b..b718f5684 100644 --- a/src/repository.h +++ b/src/repository.h @@ -55,6 +55,7 @@ PyObject* Repository_create_blob_fromfile(Repository *self, PyObject *args); PyObject* Repository_create_commit(Repository *self, PyObject *args); PyObject* Repository_create_tag(Repository *self, PyObject *args); PyObject* Repository_listall_references(Repository *self, PyObject *args); +PyObject* Repository_listall_branches(Repository *self, PyObject *args); PyObject* Repository_lookup_reference(Repository *self, PyObject *py_name); PyObject* diff --git a/test/test_branch.py b/test/test_branch.py index 8c6a45df3..c6a556532 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -48,17 +48,29 @@ def test_lookup_branch_local(self): self.assertTrue(self.repo.lookup_branch('not-exists') is None) + def test_listall_branches(self): + branches = sorted(self.repo.listall_branches()) + self.assertEqual(branches, ['i18n', 'master']) + + class BranchesEmptyRepoTestCase(utils.EmptyRepoTestCase): - def test_lookup_branch_remote(self): + def setUp(self): + super(utils.EmptyRepoTestCase, self).setUp() + remote = self.repo.remotes[0] remote.fetch() + def test_lookup_branch_remote(self): branch = self.repo.lookup_branch('origin/master', pygit2.GIT_BRANCH_REMOTE) self.assertEqual(branch.target.hex, ORIGIN_MASTER_COMMIT) self.assertTrue(self.repo.lookup_branch('origin/not-exists', pygit2.GIT_BRANCH_REMOTE) is None) + def test_listall_branches(self): + branches = sorted(self.repo.listall_branches(pygit2.GIT_BRANCH_REMOTE)) + self.assertEqual(branches, ['origin/master']) + if __name__ == '__main__': unittest.main() From 3b4c8fe4bdab5af2f558d6a8e5712a3d7c7937c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Fri, 17 May 2013 22:33:17 +0200 Subject: [PATCH 0540/2237] Implement Repository.create_branch. --- src/repository.c | 35 +++++++++++++++++++++++++++++++++++ src/repository.h | 1 + test/test_branch.py | 15 +++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/src/repository.c b/src/repository.c index d4bebdffb..538e0ceb3 100644 --- a/src/repository.c +++ b/src/repository.c @@ -45,6 +45,7 @@ extern PyTypeObject IndexType; extern PyTypeObject WalkerType; extern PyTypeObject SignatureType; extern PyTypeObject ObjectType; +extern PyTypeObject CommitType; extern PyTypeObject TreeType; extern PyTypeObject TreeBuilderType; extern PyTypeObject ConfigType; @@ -842,6 +843,39 @@ Repository_create_tag(Repository *self, PyObject *args) } +PyDoc_STRVAR(Repository_create_branch__doc__, + "create_branch(name, commit, force=False) -> bytes\n" + "\n" + "Create a new branch \"name\" which points to a commit.\n" + "\n" + "Arguments:\n" + "\n" + "force\n" + " If True branches will be overridden, otherwise (the default) an\n" + " exception is raised.\n" + "\n" + "Examples::\n" + "\n" + " repo.create_branch('foo', repo.head.hex, force=False)"); + +PyObject* Repository_create_branch(Repository *self, PyObject *args) +{ + Commit *py_commit; + git_reference *c_reference; + char *c_name; + int err, force = 0; + + if (!PyArg_ParseTuple(args, "sO!|i", &c_name, &CommitType, &py_commit, &force)) + return NULL; + + err = git_branch_create(&c_reference, self->repo, c_name, py_commit->commit, force); + if (err < 0) + return Error_set(err); + + return wrap_branch(c_reference, self); +} + + PyDoc_STRVAR(Repository_listall_references__doc__, "listall_references([flags]) -> (str, ...)\n" "\n" @@ -1422,6 +1456,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, git_object_lookup_prefix, METH_O), METHOD(Repository, lookup_branch, METH_VARARGS), METHOD(Repository, listall_branches, METH_VARARGS), + METHOD(Repository, create_branch, METH_VARARGS), {NULL} }; diff --git a/src/repository.h b/src/repository.h index b718f5684..fd9c524b7 100644 --- a/src/repository.h +++ b/src/repository.h @@ -54,6 +54,7 @@ PyObject* Repository_create_blob(Repository *self, PyObject *args); PyObject* Repository_create_blob_fromfile(Repository *self, PyObject *args); PyObject* Repository_create_commit(Repository *self, PyObject *args); PyObject* Repository_create_tag(Repository *self, PyObject *args); +PyObject* Repository_create_branch(Repository *self, PyObject *args); PyObject* Repository_listall_references(Repository *self, PyObject *args); PyObject* Repository_listall_branches(Repository *self, PyObject *args); PyObject* Repository_lookup_reference(Repository *self, PyObject *py_name); diff --git a/test/test_branch.py b/test/test_branch.py index c6a556532..4cc21caa3 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -52,6 +52,21 @@ def test_listall_branches(self): branches = sorted(self.repo.listall_branches()) self.assertEqual(branches, ['i18n', 'master']) + def test_create_branch(self): + commit = self.repo[LAST_COMMIT] + reference = self.repo.create_branch('version1', commit) + refs = self.repo.listall_branches() + self.assertTrue('version1' in refs) + reference = self.repo.lookup_branch('version1') + self.assertEqual(reference.target.hex, LAST_COMMIT) + + # try to create existing reference + self.assertRaises(ValueError, + lambda: self.repo.create_branch('version1', commit)) + + # try to create existing reference with force + reference = self.repo.create_branch('version1', commit, True) + self.assertEqual(reference.target.hex, LAST_COMMIT) class BranchesEmptyRepoTestCase(utils.EmptyRepoTestCase): From 79f1e54153af174059ccfcd4d12c20504c2bdca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Fri, 17 May 2013 22:41:36 +0200 Subject: [PATCH 0541/2237] Implement Branch.delete. --- src/branch.c | 30 ++++++++++++++++++++++++++++++ src/branch.h | 4 ++++ test/test_branch.py | 11 +++++++++++ 3 files changed, 45 insertions(+) diff --git a/src/branch.c b/src/branch.c index 20e408554..fe5516e1b 100644 --- a/src/branch.c +++ b/src/branch.c @@ -26,9 +26,39 @@ */ #include "types.h" +#include "branch.h" +#include "error.h" #include "reference.h" +#include "utils.h" + +extern PyObject *GitError; + +PyDoc_STRVAR(Branch_delete__doc__, + "delete()\n" + "\n" + "Delete this branch. It will no longer be valid!"); + +PyObject * +Branch_delete(Branch *self, PyObject *args) +{ + int err; + + CHECK_REFERENCE(self); + + /* Delete the branch */ + err = git_branch_delete(self->reference); + if (err < 0) + return Error_set(err); + + git_reference_free(self->reference); + self->reference = NULL; /* Invalidate the pointer */ + + Py_RETURN_NONE; +} + PyMethodDef Branch_methods[] = { + METHOD(Branch, delete, METH_NOARGS), {NULL} }; diff --git a/src/branch.h b/src/branch.h index ecec8761b..ab7b0eac2 100644 --- a/src/branch.h +++ b/src/branch.h @@ -28,8 +28,12 @@ #ifndef INCLUDE_pygit2_branch_h #define INCLUDE_pygit2_branch_h +#define PY_SSIZE_T_CLEAN +#include #include +PyObject* Branch_delete(Branch *self, PyObject *args); + PyObject* wrap_branch(git_reference *c_reference, Repository *repo); #endif diff --git a/test/test_branch.py b/test/test_branch.py index 4cc21caa3..80828b6d2 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -68,6 +68,17 @@ def test_create_branch(self): reference = self.repo.create_branch('version1', commit, True) self.assertEqual(reference.target.hex, LAST_COMMIT) + def test_delete(self): + branch = self.repo.lookup_branch('i18n') + branch.delete() + + self.assertTrue(self.repo.lookup_branch('i18n') is None) + + def test_cant_delete_master(self): + branch = self.repo.lookup_branch('master') + + self.assertRaises(pygit2.GitError, lambda: branch.delete()) + class BranchesEmptyRepoTestCase(utils.EmptyRepoTestCase): def setUp(self): From 2f08236aecf5aa8fac9bb47013729c5ef082dc50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Fri, 17 May 2013 22:50:38 +0200 Subject: [PATCH 0542/2237] Implement Branch.is_head. --- src/branch.c | 23 +++++++++++++++++++++++ src/branch.h | 1 + test/test_branch.py | 8 ++++++++ 3 files changed, 32 insertions(+) diff --git a/src/branch.c b/src/branch.c index fe5516e1b..579c25344 100644 --- a/src/branch.c +++ b/src/branch.c @@ -57,8 +57,31 @@ Branch_delete(Branch *self, PyObject *args) } +PyDoc_STRVAR(Branch_is_head__doc__, + "is_head()\n" + "\n" + "True if HEAD points at the branch, False otherwise."); + +PyObject * +Branch_is_head(Branch *self) +{ + int err; + + CHECK_REFERENCE(self); + + err = git_branch_is_head(self->reference); + if (err == 1) + Py_RETURN_TRUE; + else if (err == 0) + Py_RETURN_FALSE; + else + return Error_set(err); +} + + PyMethodDef Branch_methods[] = { METHOD(Branch, delete, METH_NOARGS), + METHOD(Branch, is_head, METH_NOARGS), {NULL} }; diff --git a/src/branch.h b/src/branch.h index ab7b0eac2..2c42b76a0 100644 --- a/src/branch.h +++ b/src/branch.h @@ -33,6 +33,7 @@ #include PyObject* Branch_delete(Branch *self, PyObject *args); +PyObject* Branch_is_head(Branch *self); PyObject* wrap_branch(git_reference *c_reference, Repository *repo); diff --git a/test/test_branch.py b/test/test_branch.py index 80828b6d2..6f3f2d13c 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -79,6 +79,14 @@ def test_cant_delete_master(self): self.assertRaises(pygit2.GitError, lambda: branch.delete()) + def test_branch_is_head_returns_true_if_branch_is_head(self): + branch = self.repo.lookup_branch('master') + self.assertTrue(branch.is_head()) + + def test_branch_is_head_returns_false_if_branch_is_not_head(self): + branch = self.repo.lookup_branch('i18n') + self.assertFalse(branch.is_head()) + class BranchesEmptyRepoTestCase(utils.EmptyRepoTestCase): def setUp(self): From bf060aeceb84f390117c633672e281097feb81e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Fri, 17 May 2013 22:56:38 +0200 Subject: [PATCH 0543/2237] Implement Branch.rename. --- src/branch.c | 27 +++++++++++++++++++++++++++ src/branch.h | 1 + test/test_branch.py | 30 ++++++++++++++++++++++++++++-- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/branch.c b/src/branch.c index 579c25344..a3b4c7803 100644 --- a/src/branch.c +++ b/src/branch.c @@ -79,9 +79,36 @@ Branch_is_head(Branch *self) } +PyDoc_STRVAR(Branch_rename__doc__, + "rename(name, force=False)\n" + "\n" + "Move/rename an existing local branch reference. The new branch name will be " + "checked for validity.\n" + "Returns the new branch."); + +PyObject* Branch_rename(Branch *self, PyObject *args) +{ + int err, force = 0; + git_reference *c_out; + const char *c_name; + + CHECK_REFERENCE(self); + + if (!PyArg_ParseTuple(args, "s|i", &c_name, &force)) + return NULL; + + err = git_branch_move(&c_out, self->reference, c_name, force); + if (err == GIT_OK) + return wrap_branch(c_out, self->repo); + else + return Error_set(err); +} + + PyMethodDef Branch_methods[] = { METHOD(Branch, delete, METH_NOARGS), METHOD(Branch, is_head, METH_NOARGS), + METHOD(Branch, rename, METH_VARARGS), {NULL} }; diff --git a/src/branch.h b/src/branch.h index 2c42b76a0..ee1185e09 100644 --- a/src/branch.h +++ b/src/branch.h @@ -34,6 +34,7 @@ PyObject* Branch_delete(Branch *self, PyObject *args); PyObject* Branch_is_head(Branch *self); +PyObject* Branch_move(Branch *self, PyObject *args); PyObject* wrap_branch(git_reference *c_reference, Repository *repo); diff --git a/test/test_branch.py b/test/test_branch.py index 6f3f2d13c..eebf1e03e 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -38,6 +38,7 @@ I18N_LAST_COMMIT = '5470a671a80ac3789f1a6a8cefbcf43ce7af0563' ORIGIN_MASTER_COMMIT = '784855caf26449a1914d2cf62d12b9374d76ae78' + class BranchesTestCase(utils.RepoTestCase): def test_lookup_branch_local(self): branch = self.repo.lookup_branch('master') @@ -87,6 +88,28 @@ def test_branch_is_head_returns_false_if_branch_is_not_head(self): branch = self.repo.lookup_branch('i18n') self.assertFalse(branch.is_head()) + def test_branch_rename_succeeds(self): + original_branch = self.repo.lookup_branch('i18n') + new_branch = original_branch.rename('new-branch') + self.assertEqual(new_branch.target.hex, I18N_LAST_COMMIT) + + new_branch_2 = self.repo.lookup_branch('new-branch') + self.assertEqual(new_branch_2.target.hex, I18N_LAST_COMMIT) + + def test_branch_rename_fails_if_destination_already_exists(self): + original_branch = self.repo.lookup_branch('i18n') + self.assertRaises(ValueError, lambda: original_branch.rename('master')) + + def test_branch_rename_not_fails_if_force_is_true(self): + original_branch = self.repo.lookup_branch('master') + new_branch = original_branch.rename('i18n', True) + self.assertEqual(new_branch.target.hex, LAST_COMMIT) + + def test_branch_rename_fails_with_invalid_names(self): + original_branch = self.repo.lookup_branch('i18n') + self.assertRaises(ValueError, + lambda: original_branch.rename('abc@{123')) + class BranchesEmptyRepoTestCase(utils.EmptyRepoTestCase): def setUp(self): @@ -96,10 +119,13 @@ def setUp(self): remote.fetch() def test_lookup_branch_remote(self): - branch = self.repo.lookup_branch('origin/master', pygit2.GIT_BRANCH_REMOTE) + branch = self.repo.lookup_branch('origin/master', + pygit2.GIT_BRANCH_REMOTE) self.assertEqual(branch.target.hex, ORIGIN_MASTER_COMMIT) - self.assertTrue(self.repo.lookup_branch('origin/not-exists', pygit2.GIT_BRANCH_REMOTE) is None) + self.assertTrue( + self.repo.lookup_branch('origin/not-exists', + pygit2.GIT_BRANCH_REMOTE) is None) def test_listall_branches(self): branches = sorted(self.repo.listall_branches(pygit2.GIT_BRANCH_REMOTE)) From 761d23bb2d2707ab13288a35b49b69a7bab1b766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Fri, 17 May 2013 23:00:53 +0200 Subject: [PATCH 0544/2237] Implement Branch.branch_name. --- src/branch.c | 19 +++++++++++++++++++ test/test_branch.py | 9 +++++++++ 2 files changed, 28 insertions(+) diff --git a/src/branch.c b/src/branch.c index a3b4c7803..48909d283 100644 --- a/src/branch.c +++ b/src/branch.c @@ -105,6 +105,24 @@ PyObject* Branch_rename(Branch *self, PyObject *args) } +PyDoc_STRVAR(Branch_branch_name__doc__, + "The name of the local or remote branch."); + +PyObject* Branch_branch_name__get__(Branch *self) +{ + int err; + const char *c_name; + + CHECK_REFERENCE(self); + + err = git_branch_name(&c_name, self->reference); + if (err == GIT_OK) + return to_unicode(c_name, NULL, NULL); + else + return Error_set(err); +} + + PyMethodDef Branch_methods[] = { METHOD(Branch, delete, METH_NOARGS), METHOD(Branch, is_head, METH_NOARGS), @@ -113,6 +131,7 @@ PyMethodDef Branch_methods[] = { }; PyGetSetDef Branch_getseters[] = { + GETTER(Branch, branch_name), {NULL} }; diff --git a/test/test_branch.py b/test/test_branch.py index eebf1e03e..a1289d7ba 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -110,6 +110,15 @@ def test_branch_rename_fails_with_invalid_names(self): self.assertRaises(ValueError, lambda: original_branch.rename('abc@{123')) + def test_branch_name(self): + branch = self.repo.lookup_branch('master') + self.assertEqual(branch.branch_name, 'master') + self.assertEqual(branch.name, 'refs/heads/master') + + branch = self.repo.lookup_branch('i18n') + self.assertEqual(branch.branch_name, 'i18n') + self.assertEqual(branch.name, 'refs/heads/i18n') + class BranchesEmptyRepoTestCase(utils.EmptyRepoTestCase): def setUp(self): From 8d9f59edec9c5ee400c52c88df258856dff6c1e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Fri, 17 May 2013 23:03:40 +0200 Subject: [PATCH 0545/2237] Implement Branch.remote_name. --- src/branch.c | 39 +++++++++++++++++++++++++++++++++++++++ test/test_branch.py | 6 ++++++ 2 files changed, 45 insertions(+) diff --git a/src/branch.c b/src/branch.c index 48909d283..a3a3126a0 100644 --- a/src/branch.c +++ b/src/branch.c @@ -123,6 +123,44 @@ PyObject* Branch_branch_name__get__(Branch *self) } +PyDoc_STRVAR(Branch_remote_name__doc__, + "The name of the remote that the remote tracking branch belongs to."); + +PyObject* Branch_remote_name__get__(Branch *self) +{ + int err; + const char *branch_name; + char *c_name = NULL; + + CHECK_REFERENCE(self); + + branch_name = git_reference_name(self->reference); + // get the length of the remote name + err = git_branch_remote_name(NULL, 0, self->repo->repo, branch_name); + if (err < GIT_OK) + return Error_set(err); + + // get the actual remote name + c_name = calloc(err, sizeof(char)); + if (c_name == NULL) + return PyErr_NoMemory(); + + err = git_branch_remote_name(c_name, + err * sizeof(char), + self->repo->repo, + branch_name); + if (err < GIT_OK) { + free(c_name); + return Error_set(err); + } + + PyObject *py_name = to_unicode(c_name, NULL, NULL); + free(c_name); + + return py_name; +} + + PyMethodDef Branch_methods[] = { METHOD(Branch, delete, METH_NOARGS), METHOD(Branch, is_head, METH_NOARGS), @@ -132,6 +170,7 @@ PyMethodDef Branch_methods[] = { PyGetSetDef Branch_getseters[] = { GETTER(Branch, branch_name), + GETTER(Branch, remote_name), {NULL} }; diff --git a/test/test_branch.py b/test/test_branch.py index a1289d7ba..dd2414153 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -140,6 +140,12 @@ def test_listall_branches(self): branches = sorted(self.repo.listall_branches(pygit2.GIT_BRANCH_REMOTE)) self.assertEqual(branches, ['origin/master']) + def test_branch_remote_name(self): + self.repo.remotes[0].fetch() + branch = self.repo.lookup_branch('origin/master', + pygit2.GIT_BRANCH_REMOTE) + self.assertEqual(branch.remote_name, 'origin') + if __name__ == '__main__': unittest.main() From b7490db82f9a2de675c68be8e0493cfcee3ab2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Fri, 17 May 2013 23:10:54 +0200 Subject: [PATCH 0546/2237] Implement Branch.upstream getter and setter. Thanks to @cholin for the help! --- src/branch.c | 55 +++++++++++++++++++++++++++++++++++++++++++++ test/test_branch.py | 18 +++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/src/branch.c b/src/branch.c index a3a3126a0..84a243838 100644 --- a/src/branch.c +++ b/src/branch.c @@ -31,7 +31,10 @@ #include "reference.h" #include "utils.h" + extern PyObject *GitError; +extern PyTypeObject ReferenceType; + PyDoc_STRVAR(Branch_delete__doc__, "delete()\n" @@ -161,6 +164,57 @@ PyObject* Branch_remote_name__get__(Branch *self) } +PyDoc_STRVAR(Branch_upstream__doc__, + "The branch supporting the remote tracking branch or None if this is not a " + "remote tracking branch. Set to None to unset."); + +PyObject* Branch_upstream__get__(Branch *self) +{ + int err; + git_reference *c_reference; + + CHECK_REFERENCE(self); + + err = git_branch_upstream(&c_reference, self->reference); + if (err == GIT_ENOTFOUND) + Py_RETURN_NONE; + else if (err < GIT_OK) + return Error_set(err); + + return wrap_branch(c_reference, self->repo); +} + +int Branch_upstream__set__(Branch *self, Reference *py_ref) +{ + int err; + const char *branch_name = NULL; + + CHECK_REFERENCE_INT(self); + + if ((PyObject *)py_ref != Py_None) { + if (!PyObject_TypeCheck(py_ref, (PyTypeObject *)&ReferenceType)) { + PyErr_SetObject(PyExc_TypeError, (PyObject *)py_ref); + return -1; + } + + CHECK_REFERENCE_INT(py_ref); + err = git_branch_name(&branch_name, py_ref->reference); + if (err < GIT_OK) { + Error_set(err); + return -1; + } + } + + err = git_branch_set_upstream(self->reference, branch_name); + if (err < GIT_OK) { + Error_set(err); + return -1; + } + + return 0; +} + + PyMethodDef Branch_methods[] = { METHOD(Branch, delete, METH_NOARGS), METHOD(Branch, is_head, METH_NOARGS), @@ -171,6 +225,7 @@ PyMethodDef Branch_methods[] = { PyGetSetDef Branch_getseters[] = { GETTER(Branch, branch_name), GETTER(Branch, remote_name), + GETSET(Branch, upstream), {NULL} }; diff --git a/test/test_branch.py b/test/test_branch.py index dd2414153..52ce9ca61 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -146,6 +146,24 @@ def test_branch_remote_name(self): pygit2.GIT_BRANCH_REMOTE) self.assertEqual(branch.remote_name, 'origin') + def test_branch_upstream(self): + self.repo.remotes[0].fetch() + remote_master = self.repo.lookup_branch('origin/master', + pygit2.GIT_BRANCH_REMOTE) + master = self.repo.create_branch('master', + self.repo[remote_master.target.hex]) + + self.assertTrue(master.upstream is None) + master.upstream = remote_master + self.assertEqual(master.upstream.branch_name, 'origin/master') + + def set_bad_upstream(): + master.upstream = 2.5 + self.assertRaises(TypeError, set_bad_upstream) + + master.upstream = None + self.assertTrue(master.upstream is None) + if __name__ == '__main__': unittest.main() From db5b4e90659db3ec006ffc1636a69b57df8947fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Fri, 17 May 2013 23:13:35 +0200 Subject: [PATCH 0547/2237] Implement Branch.upstream_name getter. --- src/branch.c | 39 +++++++++++++++++++++++++++++++++++++++ test/test_branch.py | 10 ++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/branch.c b/src/branch.c index 84a243838..48f5bbaab 100644 --- a/src/branch.c +++ b/src/branch.c @@ -215,6 +215,44 @@ int Branch_upstream__set__(Branch *self, Reference *py_ref) } +PyDoc_STRVAR(Branch_upstream_name__doc__, + "The name of the reference supporting the remote tracking branch."); + +PyObject* Branch_upstream_name__get__(Branch *self) +{ + int err; + const char *branch_name; + char *c_name = NULL; + + CHECK_REFERENCE(self); + + branch_name = git_reference_name(self->reference); + // get the length of the upstream name + err = git_branch_upstream_name(NULL, 0, self->repo->repo, branch_name); + if (err < GIT_OK) + return Error_set(err); + + // get the actual upstream name + c_name = calloc(err, sizeof(char)); + if (c_name == NULL) + return PyErr_NoMemory(); + + err = git_branch_upstream_name(c_name, + err * sizeof(char), + self->repo->repo, + branch_name); + if (err < GIT_OK) { + free(c_name); + return Error_set(err); + } + + PyObject *py_name = to_unicode(c_name, NULL, NULL); + free(c_name); + + return py_name; +} + + PyMethodDef Branch_methods[] = { METHOD(Branch, delete, METH_NOARGS), METHOD(Branch, is_head, METH_NOARGS), @@ -226,6 +264,7 @@ PyGetSetDef Branch_getseters[] = { GETTER(Branch, branch_name), GETTER(Branch, remote_name), GETSET(Branch, upstream), + GETTER(Branch, upstream_name), {NULL} }; diff --git a/test/test_branch.py b/test/test_branch.py index 52ce9ca61..8dc62cb1a 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -164,6 +164,16 @@ def set_bad_upstream(): master.upstream = None self.assertTrue(master.upstream is None) + def test_branch_upstream_name(self): + self.repo.remotes[0].fetch() + remote_master = self.repo.lookup_branch('origin/master', + pygit2.GIT_BRANCH_REMOTE) + master = self.repo.create_branch('master', + self.repo[remote_master.target.hex]) + + master.upstream = remote_master + self.assertEqual(master.upstream_name, 'refs/remotes/origin/master') + if __name__ == '__main__': unittest.main() From 6866a3aac5c4d32f78c2f1328910f61eb946420c Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 28 May 2013 11:23:26 +0200 Subject: [PATCH 0548/2237] Added: Reference.get_object() method get_object() retrieves the object the current reference is pointing to --- docs/references.rst | 3 ++- src/reference.c | 23 +++++++++++++++++++++++ test/test_refs.py | 6 ++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/docs/references.rst b/docs/references.rst index 943c36712..c8790ad44 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -11,7 +11,7 @@ Example:: >>> all_refs = repo.listall_references() >>> master_ref = repo.lookup_reference("refs/heads/master") - >>> commit = repo[master_ref.target] + >>> commit = master_ref.get_object() # or repo[master_ref.target] The Reference type @@ -25,6 +25,7 @@ The Reference type .. automethod:: pygit2.Reference.rename .. automethod:: pygit2.Reference.resolve .. automethod:: pygit2.Reference.log +.. automethod:: pygit2.Reference.get_object The HEAD diff --git a/src/reference.c b/src/reference.c index e01eb47a0..4d11cb5c5 100644 --- a/src/reference.c +++ b/src/reference.c @@ -29,6 +29,7 @@ #include #include #include +#include "object.h" #include "error.h" #include "types.h" #include "utils.h" @@ -312,6 +313,27 @@ Reference_log(Reference *self) } +PyDoc_STRVAR(Reference_get_object__doc__, + "get_object() -> object\n" + "\n" + "Retrieves the object the current reference is pointing to."); + +PyObject * +Reference_get_object(Reference *self) +{ + int err; + git_object* obj; + + CHECK_REFERENCE(self); + + err = git_reference_peel(&obj, self->reference, GIT_OBJ_ANY); + if (err < 0) + return Error_set(err); + + return wrap_object(obj, self->repo); +} + + PyDoc_STRVAR(RefLogEntry_committer__doc__, "Committer."); PyObject * @@ -404,6 +426,7 @@ PyMethodDef Reference_methods[] = { METHOD(Reference, rename, METH_O), METHOD(Reference, resolve, METH_NOARGS), METHOD(Reference, log, METH_NOARGS), + METHOD(Reference, get_object, METH_NOARGS), {NULL} }; diff --git a/test/test_refs.py b/test/test_refs.py index 3b0a660f6..11537c9f0 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -209,5 +209,11 @@ def test_create_symbolic_reference(self): # self.repo.packall_references() + def test_get_object(self): + repo = self.repo + ref = repo.lookup_reference('refs/heads/master') + self.assertEqual(repo[ref.target].oid, ref.get_object().oid) + + if __name__ == '__main__': unittest.main() From 9605100deae8d55b49d6168b3e38a40b4d3d7139 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Tue, 28 May 2013 14:54:07 +0200 Subject: [PATCH 0549/2237] Fixed: typo in repository.c --- src/repository.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repository.c b/src/repository.c index 538e0ceb3..cc3b25bec 100644 --- a/src/repository.c +++ b/src/repository.c @@ -528,7 +528,7 @@ Repository_config__get__(Repository *self) py_config->config = config; self->config = (PyObject*)py_config; // We need 2 refs here. - // One is returned, one is keep internally. + // One is returned, one is kept internally. Py_INCREF(self->config); } else { Py_INCREF(self->config); From a6567c4eb660af73cbc5e766785234895520e573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Wed, 29 May 2013 00:24:40 +0200 Subject: [PATCH 0550/2237] Add Remote.save method. --- src/remote.c | 20 ++++++++++++++++++++ test/test_remote.py | 12 ++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/remote.c b/src/remote.c index df238a3c6..63f588fc7 100644 --- a/src/remote.c +++ b/src/remote.c @@ -215,8 +215,28 @@ Remote_fetch(Remote *self, PyObject *args) } +PyDoc_STRVAR(Remote_save__doc__, + "save()\n\n" + "Save a remote to its repository configuration."); + +PyObject * +Remote_save(Remote *self, PyObject *args) +{ + int err; + + err = git_remote_save(self->remote); + if (err == GIT_OK) { + Py_RETURN_NONE; + } + else { + return Error_set(err); + } +} + + PyMethodDef Remote_methods[] = { METHOD(Remote, fetch, METH_NOARGS), + METHOD(Remote, save, METH_NOARGS), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index d0ddebc2b..7ff5ef09a 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -98,6 +98,18 @@ def test_remote_list(self): remote = self.repo.create_remote(name, url) self.assertTrue(remote.name in [x.name for x in self.repo.remotes]) + def test_remote_save(self): + remote = self.repo.remotes[0] + + remote.name = 'new-name' + remote.url = 'http://example.com/test.git' + + remote.save() + + self.assertEqual('new-name', self.repo.remotes[0].name) + self.assertEqual('http://example.com/test.git', + self.repo.remotes[0].url) + class EmptyRepositoryTest(utils.EmptyRepoTestCase): def test_fetch(self): From 877b4698ee32d592b43774e61e8b86c0fb916851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Thu, 30 May 2013 00:00:35 +0200 Subject: [PATCH 0551/2237] Documentation for Branch type and Branch-related methods. --- docs/references.rst | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/docs/references.rst b/docs/references.rst index c8790ad44..4db5eae7a 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -40,6 +40,46 @@ Example. These two lines are equivalent:: .. autoattribute:: pygit2.Repository.head_is_detached .. autoattribute:: pygit2.Repository.head_is_orphaned +Branches +==================== + +Branches inherit from References, and additionally provide spetialized +accessors for some unique features. + +.. automethod:: pygit2.Repository.listall_branches +.. automethod:: pygit2.Repository.lookup_branch +.. automethod:: pygit2.Repository.create_branch + +Example:: + + >>> local_branches = repo.listall_branches() + >>> # equivalent to + >>> local_branches = repo.listall_branches(pygit2.GIT_BRANCH_LOCAL) + + >>> remote_branches = repo.listall_branches(pygit2.GIT_BRANCH_REMOTE) + + >>> all_branches = repo.listall_branches(pygit2.GIT_BRANCH_REMOTE | + pygit2.GIT_BRANCH_LOCAL) + + >>> master_branch = repo.lookup_branch('master') + >>> # equivalent to + >>> master_branch = repo.lookup_branch('master', + pygit2.GIT_BRANCH_LOCAL) + + >>> remote_branch = repo.lookup_branch('upstream/feature', + pygit2.GIT_BRANCH_REMOTE) + +The Branch type +==================== + +.. autoattribute:: pygit2.Branch.branch_name +.. autoattribute:: pygit2.Branch.remote_name +.. autoattribute:: pygit2.Branch.upstream +.. autoattribute:: pygit2.Branch.upstream_name + +.. automethod:: pygit2.Branch.rename +.. automethod:: pygit2.Branch.delete +.. automethod:: pygit2.Branch.is_head The reference log ==================== From 5125b46664cc91980701e90d70b26eb9fd5468eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Thu, 30 May 2013 00:02:56 +0200 Subject: [PATCH 0552/2237] Documentation for Remote.save. --- docs/remotes.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/remotes.rst b/docs/remotes.rst index 9042cbb14..70dd50a4f 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -14,3 +14,4 @@ The Remote type .. autoattribute:: pygit2.Remote.url .. autoattribute:: pygit2.Remote.fetchspec .. automethod:: pygit2.Remote.fetch +.. automethod:: pygit2.Remote.save \ No newline at end of file From 4b051e38ef9096ed496078e4091b7cdd029e9c6a Mon Sep 17 00:00:00 2001 From: David Fischer Date: Fri, 14 Jun 2013 08:04:56 -0700 Subject: [PATCH 0553/2237] Added note about pygit2/libgit2 versioning --- docs/install.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/install.rst b/docs/install.rst index b18d90806..177e20852 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -25,6 +25,10 @@ When those are installed, you can install pygit2: $ python setup.py install $ python setup.py test +.. note:: A minor version of pygit2 must be used with the corresponding minor + version of libgit2. For example, pygit2 v0.18.x must be used with libgit2 + v0.18.0. + Building on \*nix (including OS X) =================================== From c9690ba1555fe876a350eac947484b7bce6809ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Duraffort?= Date: Wed, 19 Jun 2013 10:38:58 +0200 Subject: [PATCH 0554/2237] Fix documentation typo --- src/object.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/object.c b/src/object.c index 193efdc04..6e018e564 100644 --- a/src/object.c +++ b/src/object.c @@ -94,7 +94,7 @@ Object_type__get__(Object *self) PyDoc_STRVAR(Object_read_raw__doc__, "read_raw()\n" "\n" - "Returns the byte string with the raw contents of the of the object."); + "Returns the byte string with the raw contents of the object."); PyObject * Object_read_raw(Object *self) From 470ad22b732ec68bcd80e739bc2425dd7606b375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Duraffort?= Date: Thu, 20 Jun 2013 09:26:43 +0200 Subject: [PATCH 0555/2237] Fix typo in comments --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0aad84dc1..0717839a8 100644 --- a/setup.py +++ b/setup.py @@ -106,7 +106,7 @@ class BuildWithDLLs(build): # On Windows, we install the git2.dll too. def _get_dlls(self): - # return a list of of (FQ-in-name, relative-out-name) tuples. + # return a list of (FQ-in-name, relative-out-name) tuples. ret = [] bld_ext = self.distribution.get_command_obj('build_ext') compiler_type = bld_ext.compiler.compiler_type From 16a3a4d240f6ec50893448fd499505b15a242dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Duraffort?= Date: Thu, 20 Jun 2013 09:34:15 +0200 Subject: [PATCH 0556/2237] TreeBuilder.insert: improve documentation --- src/treebuilder.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/treebuilder.c b/src/treebuilder.c index 94036a82f..8981e98c3 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -47,7 +47,11 @@ TreeBuilder_dealloc(TreeBuilder *self) PyDoc_STRVAR(TreeBuilder_insert__doc__, "insert(name, oid, attr)\n" "\n" - "Insert or replace an entry in the treebuilder."); + "Insert or replace an entry in the treebuilder.\n" + "\n" + "attr available values are GIT_FILEMODE_BLOB,\n" + " GIT_FILEMODE_BLOB_EXECUTABLE, GIT_FILEMODE_TREE,\n" + " GIT_FILEMODE_LINK and GIT_FILEMODE_COMMIT."); PyObject * TreeBuilder_insert(TreeBuilder *self, PyObject *args) From 9a78ddafa3ba76b3d413ef5dc078da02de64c475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 29 Jun 2013 12:38:41 +0200 Subject: [PATCH 0557/2237] Upgrading to libgit2 0.19 (wip) --- src/repository.c | 17 ++++++----------- test/test_refs.py | 4 ---- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/repository.c b/src/repository.c index cc3b25bec..935c1665f 100644 --- a/src/repository.c +++ b/src/repository.c @@ -877,34 +877,29 @@ PyObject* Repository_create_branch(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_listall_references__doc__, - "listall_references([flags]) -> (str, ...)\n" + "listall_references() -> (str, ...)\n" "\n" "Return a tuple with all the references in the repository."); PyObject * Repository_listall_references(Repository *self, PyObject *args) { - unsigned list_flags=GIT_REF_LISTALL; git_strarray c_result; PyObject *py_result, *py_string; unsigned index; int err; - /* 1- Get list_flags */ - if (!PyArg_ParseTuple(args, "|I", &list_flags)) - return NULL; - - /* 2- Get the C result */ - err = git_reference_list(&c_result, self->repo, list_flags); + /* Get the C result */ + err = git_reference_list(&c_result, self->repo); if (err < 0) return Error_set(err); - /* 3- Create a new PyTuple */ + /* Create a new PyTuple */ py_result = PyTuple_New(c_result.count); if (py_result == NULL) goto out; - /* 4- Fill it */ + /* Fill it */ for (index=0; index < c_result.count; index++) { py_string = to_path((c_result.strings)[index]); if (py_string == NULL) { @@ -1441,7 +1436,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, write, METH_VARARGS), METHOD(Repository, create_reference_direct, METH_VARARGS), METHOD(Repository, create_reference_symbolic, METH_VARARGS), - METHOD(Repository, listall_references, METH_VARARGS), + METHOD(Repository, listall_references, METH_NOARGS), METHOD(Repository, lookup_reference, METH_O), METHOD(Repository, revparse_single, METH_O), METHOD(Repository, status, METH_NOARGS), diff --git a/test/test_refs.py b/test/test_refs.py index 11537c9f0..461218527 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -54,10 +54,6 @@ def test_list_all_references(self): ['refs/heads/i18n', 'refs/heads/master', 'refs/tags/version1']) - # Now we list only the symbolic references - self.assertEqual(repo.listall_references(GIT_REF_SYMBOLIC), - ('refs/tags/version1', )) - def test_head(self): head = self.repo.head self.assertEqual(LAST_COMMIT, self.repo[head.target].hex) From e56e8600ac78dea16d1c081239ce07a694ff5a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 30 Jun 2013 13:29:55 +0200 Subject: [PATCH 0558/2237] Upgrading to libgit2 0.19 (wip) Removed: - Remote.fetchspec Added: - Remote.refspec_count - Remote.get_refspec(n) --- docs/remotes.rst | 5 ++-- src/remote.c | 65 +++++++++++++++++---------------------------- src/utils.h | 1 + test/test_remote.py | 16 ++++++----- 4 files changed, 38 insertions(+), 49 deletions(-) diff --git a/docs/remotes.rst b/docs/remotes.rst index 70dd50a4f..91a69d055 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -12,6 +12,7 @@ The Remote type .. autoattribute:: pygit2.Remote.name .. autoattribute:: pygit2.Remote.url -.. autoattribute:: pygit2.Remote.fetchspec +.. autoattribute:: pygit2.Remote.refspec_count +.. automethod:: pygit2.Remote.get_refspec .. automethod:: pygit2.Remote.fetch -.. automethod:: pygit2.Remote.save \ No newline at end of file +.. automethod:: pygit2.Remote.save diff --git a/src/remote.c b/src/remote.c index 63f588fc7..9faf11435 100644 --- a/src/remote.c +++ b/src/remote.c @@ -125,57 +125,41 @@ Remote_url__set__(Remote *self, PyObject* py_url) } -PyDoc_STRVAR(Remote_fetchspec__doc__, - "= (source:str, destination:str)\n" - "\n" - "Name of the remote source and destination fetch refspecs\n"); - +PyDoc_STRVAR(Remote_refspec_count__doc__, "Number of refspecs."); PyObject * -Remote_fetchspec__get__(Remote *self) +Remote_refspec_count__get__(Remote *self) { - PyObject* py_tuple = NULL; - const git_refspec * refspec; - - refspec = git_remote_fetchspec(self->remote); - if (refspec != NULL) { - py_tuple = Py_BuildValue( - "(ss)", - git_refspec_src(refspec), - git_refspec_dst(refspec) - ); - - return py_tuple; - } + size_t count; - return Error_set(GIT_ENOTFOUND); + count = git_remote_refspec_count(self->remote); + return PyLong_FromSize_t(count); } -int -Remote_fetchspec__set__(Remote *self, PyObject* py_tuple) -{ - int err; - size_t length = 0; - char* src = NULL, *dst = NULL, *buf = NULL; - if (!PyArg_ParseTuple(py_tuple, "ss", &src, &dst)) - return -1; +PyDoc_STRVAR(Remote_get_refspec__doc__, + "get_refspec(n) -> (str, str)\n" + "\n" + "Return the refspec at the given position."); - /* length is strlen('+' + src + ':' + dst) and Null-Byte */ - length = strlen(src) + strlen(dst) + 3; - buf = (char*) calloc(length, sizeof(char)); - if (buf != NULL) { - sprintf(buf, "+%s:%s", src, dst); - err = git_remote_set_fetchspec(self->remote, buf); - free(buf); +PyObject * +Remote_get_refspec(Remote *self, PyObject *value) +{ + size_t n; + const git_refspec *refspec; - if (err == GIT_OK) - return 0; + n = PyLong_AsSize_t(value); + if (PyErr_Occurred()) + return NULL; - Error_set_exc(PyExc_ValueError); + refspec = git_remote_get_refspec(self->remote, n); + if (refspec == NULL) { + PyErr_SetObject(PyExc_IndexError, value); + return NULL; } - return -1; + return Py_BuildValue("(ss)", git_refspec_src(refspec), + git_refspec_dst(refspec)); } @@ -237,13 +221,14 @@ Remote_save(Remote *self, PyObject *args) PyMethodDef Remote_methods[] = { METHOD(Remote, fetch, METH_NOARGS), METHOD(Remote, save, METH_NOARGS), + METHOD(Remote, get_refspec, METH_O), {NULL} }; PyGetSetDef Remote_getseters[] = { GETSET(Remote, name), GETSET(Remote, url), - GETSET(Remote, fetchspec), + GETTER(Remote, refspec_count), {NULL} }; diff --git a/src/utils.h b/src/utils.h index 99a89db1d..9b6a1b564 100644 --- a/src/utils.h +++ b/src/utils.h @@ -42,6 +42,7 @@ /* Python 2 support */ #if PY_MAJOR_VERSION == 2 #define PyLong_FromSize_t PyInt_FromSize_t + #define PyLong_AsSize_t (size_t)PyInt_AsSsize_t #define PyLong_AsLong PyInt_AsLong #undef PyLong_Check #define PyLong_Check PyInt_Check diff --git a/test/test_remote.py b/test/test_remote.py index 7ff5ef09a..36d113c13 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -75,16 +75,18 @@ def test_remote_set_url(self): self.assertRaisesAssign(ValueError, remote, 'url', '') - def test_remote_fetchspec(self): + def test_refspec(self): remote = self.repo.remotes[0] - self.assertEqual(REMOTE_FETCHSPEC_SRC, remote.fetchspec[0]) - self.assertEqual(REMOTE_FETCHSPEC_DST, remote.fetchspec[1]) + self.assertEqual(remote.refspec_count, 1) + refspec = remote.get_refspec(0) + self.assertEqual(refspec[0], REMOTE_FETCHSPEC_SRC) + self.assertEqual(refspec[1], REMOTE_FETCHSPEC_DST) - new_fetchspec = ('refs/foo/*', 'refs/remotes/foo/*') - remote.fetchspec = new_fetchspec - self.assertEqual(new_fetchspec[0], remote.fetchspec[0]) - self.assertEqual(new_fetchspec[1], remote.fetchspec[1]) +# new_fetchspec = ('refs/foo/*', 'refs/remotes/foo/*') +# remote.fetchspec = new_fetchspec +# self.assertEqual(new_fetchspec[0], remote.fetchspec[0]) +# self.assertEqual(new_fetchspec[1], remote.fetchspec[1]) def test_remote_list(self): From 5dfabbd825c3ca0714214f859ec620181d5240c9 Mon Sep 17 00:00:00 2001 From: Jiunn Haur Lim Date: Fri, 5 Jul 2013 10:44:39 -0400 Subject: [PATCH 0559/2237] added some quick examples to documentation --- docs/examples.rst | 41 +++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 1 + 2 files changed, 42 insertions(+) create mode 100644 docs/examples.rst diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 000000000..aeb6a5a50 --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,41 @@ +********************************************************************** +Quick examples +********************************************************************** + +A list of some common command-line operations and their pygit2 equivalents. + +Creating a new repository with ``git init`` + + >>> pygit2.init_repository('repo_name', False) + + +Viewing a commit with ``git show d370f56`` + + >>> repo = pygit2.Repository('/path/to/repository') + >>> commit = repo['d370f56'] + +Viewing the last commit message + + >>> repo[repo.head.oid].message + 'commit message' + +Traversing the commit history with ``git log`` + + >>> last = repo[repo.head.oid] + >>> for commit in repo.walk(last.oid, pygit2.GIT_SORT_TIME): + >>> print(commit.message) # or some other operation + +Listing all branches with ``git branch`` + + >>> regex = re.compile('^refs/heads/') + >>> filter(lambda r: regex.match(r), repo.listall_references()) + +Similarly, listing all tags with ``git tag`` + + >>> regex = re.compile('^refs/tags') + >>> filter(lambda r: regex.match(r), repo.listall_references()) + +Listing all files in the last commit + + >>> for e in repo[repo.head.oid].tree: + >>> print(e.name) diff --git a/docs/index.rst b/docs/index.rst index dcd3ed63c..59a88546c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -25,6 +25,7 @@ Start: :maxdepth: 1 install + examples Usage guide: From 534d98b6b2398d053003b3a626fde666acf4417c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 5 Jul 2013 23:23:31 +0200 Subject: [PATCH 0560/2237] Comment the remote test that segfaults See libgit2/libgit2#1692 To uncomment whenever libgit2 0.20 is released --- src/pygit2.c | 22 ++++++++++------------ test/test_remote.py | 6 ++++-- test/test_repository.py | 19 +++++++++---------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index a896dcbb1..1a5ac6c23 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -137,21 +137,19 @@ clone_repository(PyObject *self, PyObject *args) { const char *remote_name, *push_url, *fetch_spec; const char *push_spec, *checkout_branch; int err; + git_clone_options opts = GIT_CLONE_OPTIONS_INIT; if (!PyArg_ParseTuple(args, "zzIzzzzz", - &url, &path, &bare, &remote_name, &push_url, - &fetch_spec, &push_spec, &checkout_branch)) + &url, &path, &bare, &remote_name, &push_url, + &fetch_spec, &push_spec, &checkout_branch)) return NULL; - git_clone_options opts = { - .version=1, - .bare=bare, - .remote_name=remote_name, - .pushurl=push_url, - .fetch_spec=fetch_spec, - .push_spec=push_spec, - .checkout_branch=checkout_branch - }; + opts.bare = bare; + opts.remote_name = remote_name; + opts.pushurl = push_url; + opts.fetch_spec = fetch_spec; + opts.push_spec = push_spec; + opts.checkout_branch = checkout_branch; err = git_clone(&repo, url, path, &opts); if (err < 0) @@ -237,7 +235,7 @@ hash(PyObject *self, PyObject *args) PyMethodDef module_methods[] = { {"init_repository", init_repository, METH_VARARGS, init_repository__doc__}, {"clone_repository", clone_repository, METH_VARARGS, - clone_repository__doc__}, + clone_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, diff --git a/test/test_remote.py b/test/test_remote.py index 36d113c13..a4a125966 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -85,8 +85,9 @@ def test_refspec(self): # new_fetchspec = ('refs/foo/*', 'refs/remotes/foo/*') # remote.fetchspec = new_fetchspec -# self.assertEqual(new_fetchspec[0], remote.fetchspec[0]) -# self.assertEqual(new_fetchspec[1], remote.fetchspec[1]) +# refspec = remote.get_refspec(0) +# self.assertEqual(new_fetchspec[0], refspec[0]) +# self.assertEqual(new_fetchspec[1], refspec[1]) def test_remote_list(self): @@ -100,6 +101,7 @@ def test_remote_list(self): remote = self.repo.create_remote(name, url) self.assertTrue(remote.name in [x.name for x in self.repo.remotes]) + def test_remote_save(self): remote = self.repo.remotes[0] diff --git a/test/test_repository.py b/test/test_repository.py index d6e762e4e..0c0592e3e 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -331,10 +331,11 @@ def test_clone_push_url(self): def test_clone_fetch_spec(self): repo_path = "./test/data/testrepo.git/" - repo = clone_repository( - repo_path, self._temp_dir, fetch_spec="refs/heads/test" - ) - self.assertFalse(repo.is_empty) + ## FIXME Uncomment these lines once libgit2 0.20 is released + ## repo = clone_repository(repo_path, self._temp_dir, + ## fetch_spec="refs/heads/test") + ## self.assertFalse(repo.is_empty) + # FIXME: When pygit2 retrieve the fetchspec we passed to git clone. # fetchspec seems to be going through, but the Repository class is # not getting it. @@ -342,9 +343,8 @@ def test_clone_fetch_spec(self): def test_clone_push_spec(self): repo_path = "./test/data/testrepo.git/" - repo = clone_repository( - repo_path, self._temp_dir, push_spec="refs/heads/test" - ) + repo = clone_repository(repo_path, self._temp_dir, + push_spec="refs/heads/test") self.assertFalse(repo.is_empty) # FIXME: When pygit2 supports retrieving the pushspec parameter, # enable this test @@ -353,9 +353,8 @@ def test_clone_push_spec(self): def test_clone_checkout_branch(self): repo_path = "./test/data/testrepo.git/" - repo = clone_repository( - repo_path, self._temp_dir, checkout_branch="test" - ) + repo = clone_repository(repo_path, self._temp_dir, + checkout_branch="test") self.assertFalse(repo.is_empty) # FIXME: When pygit2 supports retrieving the current branch, # enable this test From 38bd4c065d864c4302dc089a17e16c0c03cdd2f9 Mon Sep 17 00:00:00 2001 From: Jiunn Haur Lim Date: Fri, 5 Jul 2013 20:14:33 -0400 Subject: [PATCH 0561/2237] restructured recipes --- docs/examples.rst | 41 ------------------------------- docs/index.rst | 2 +- docs/recipes.rst | 26 ++++++++++++++++++++ docs/recipes/git-branch.rst | 30 +++++++++++++++++++++++ docs/recipes/git-init.rst | 41 +++++++++++++++++++++++++++++++ docs/recipes/git-log.rst | 43 ++++++++++++++++++++++++++++++++ docs/recipes/git-show.rst | 49 +++++++++++++++++++++++++++++++++++++ docs/recipes/git-tag.rst | 24 ++++++++++++++++++ 8 files changed, 214 insertions(+), 42 deletions(-) delete mode 100644 docs/examples.rst create mode 100644 docs/recipes.rst create mode 100644 docs/recipes/git-branch.rst create mode 100644 docs/recipes/git-init.rst create mode 100644 docs/recipes/git-log.rst create mode 100644 docs/recipes/git-show.rst create mode 100644 docs/recipes/git-tag.rst diff --git a/docs/examples.rst b/docs/examples.rst deleted file mode 100644 index aeb6a5a50..000000000 --- a/docs/examples.rst +++ /dev/null @@ -1,41 +0,0 @@ -********************************************************************** -Quick examples -********************************************************************** - -A list of some common command-line operations and their pygit2 equivalents. - -Creating a new repository with ``git init`` - - >>> pygit2.init_repository('repo_name', False) - - -Viewing a commit with ``git show d370f56`` - - >>> repo = pygit2.Repository('/path/to/repository') - >>> commit = repo['d370f56'] - -Viewing the last commit message - - >>> repo[repo.head.oid].message - 'commit message' - -Traversing the commit history with ``git log`` - - >>> last = repo[repo.head.oid] - >>> for commit in repo.walk(last.oid, pygit2.GIT_SORT_TIME): - >>> print(commit.message) # or some other operation - -Listing all branches with ``git branch`` - - >>> regex = re.compile('^refs/heads/') - >>> filter(lambda r: regex.match(r), repo.listall_references()) - -Similarly, listing all tags with ``git tag`` - - >>> regex = re.compile('^refs/tags') - >>> filter(lambda r: regex.match(r), repo.listall_references()) - -Listing all files in the last commit - - >>> for e in repo[repo.head.oid].tree: - >>> print(e.name) diff --git a/docs/index.rst b/docs/index.rst index 59a88546c..5d25e6862 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -25,7 +25,7 @@ Start: :maxdepth: 1 install - examples + recipes Usage guide: diff --git a/docs/recipes.rst b/docs/recipes.rst new file mode 100644 index 000000000..019f863a8 --- /dev/null +++ b/docs/recipes.rst @@ -0,0 +1,26 @@ +********************************************************************** +pygit2 Recipes +********************************************************************** + +A list of some standard git commands and their pygit2 equivalents. This +document is a work in progress, and is organized according to the `git man +page`_. + +---------------------------------------------------------------------- +High Level Commands +---------------------------------------------------------------------- + +====================================================================== +Main porcelain commands +====================================================================== + +.. toctree:: + :maxdepth: 1 + + git-branch (List, create, or delete branches.) + git-init (Create an empty git repository or reinitialize an existing one.) + git-log (Show commit logs.) + git-show (Show various types of objects.) + git-tag (Create, list, delete or verify a tag object signed with GPG.) + +.. _git man page: https://www.kernel.org/pub/software/scm/git/docs/git.html diff --git a/docs/recipes/git-branch.rst b/docs/recipes/git-branch.rst new file mode 100644 index 000000000..5db822bc7 --- /dev/null +++ b/docs/recipes/git-branch.rst @@ -0,0 +1,30 @@ +********************************************************************** +git-branch +********************************************************************** + +---------------------------------------------------------------------- +Listing branches +---------------------------------------------------------------------- + +====================================================================== +List all branches +====================================================================== + +.. code-block:: bash + + $> git branch + +.. code-block:: python + + >>> regex = re.compile('^refs/heads/') + >>> branches = filter(lambda r: regex.match(r), repo.listall_references()) + +`Note that the next release will probably allow` ``repo.listall_branches()``. + +---------------------------------------------------------------------- +References +---------------------------------------------------------------------- + +- git-branch_. + +.. _git-branch: https://www.kernel.org/pub/software/scm/git/docs/git-branch.html diff --git a/docs/recipes/git-init.rst b/docs/recipes/git-init.rst new file mode 100644 index 000000000..75211a99a --- /dev/null +++ b/docs/recipes/git-init.rst @@ -0,0 +1,41 @@ +********************************************************************** +git-init +********************************************************************** + +---------------------------------------------------------------------- +Creating a new repository +---------------------------------------------------------------------- + +====================================================================== +Create bare repository +====================================================================== + +.. code-block:: bash + + $> git init --bare relative/path + +.. code-block:: python + + >>> pygit2.init_repository('relative/path', True) + + +====================================================================== +Create standard repository +====================================================================== + +.. code-block:: bash + + $> git init relative/path + +.. code-block:: python + + >>> pygit2.init_repository('relative/path', False) + + +---------------------------------------------------------------------- +References +---------------------------------------------------------------------- + +- git-init_. + +.. _git-init: https://www.kernel.org/pub/software/scm/git/docs/git-init.html diff --git a/docs/recipes/git-log.rst b/docs/recipes/git-log.rst new file mode 100644 index 000000000..e9c10b736 --- /dev/null +++ b/docs/recipes/git-log.rst @@ -0,0 +1,43 @@ +********************************************************************** +git-log +********************************************************************** + +---------------------------------------------------------------------- +Showing HEAD commit logs +---------------------------------------------------------------------- + +====================================================================== +Show HEAD commit +====================================================================== + +.. code-block:: bash + + $> git log -1 + +.. code-block:: python + + >>> commit = repo[repo.head.oid] + >>> commit.message + 'commit message' + +====================================================================== +Traverse commit history +====================================================================== + +.. code-block:: bash + + $> git log + +.. code-block:: python + + >>> last = repo[repo.head.oid] + >>> for commit in repo.walk(last.oid, pygit2.GIT_SORT_TIME): + >>> print(commit.message) # or some other operation + +---------------------------------------------------------------------- +References +---------------------------------------------------------------------- + +- git-log_. + +.. _git-log: https://www.kernel.org/pub/software/scm/git/docs/git-log.html diff --git a/docs/recipes/git-show.rst b/docs/recipes/git-show.rst new file mode 100644 index 000000000..0cba72d19 --- /dev/null +++ b/docs/recipes/git-show.rst @@ -0,0 +1,49 @@ +********************************************************************** +git-show +********************************************************************** + +---------------------------------------------------------------------- +Showing a commit +---------------------------------------------------------------------- + +.. code-block:: bash + + $> git show d370f56 + +.. code-block:: python + + >>> repo = pygit2.Repository('/path/to/repository') + >>> commit = repo.revparse_single('d370f56') + +====================================================================== +Show log message +====================================================================== + + >>> message = commit.message + +====================================================================== +Show SHA hash +====================================================================== + + >>> hash = commit.hex + +====================================================================== +Show diff +====================================================================== + + >>> diff = commit.tree.diff() + +====================================================================== +Show all files in commit +====================================================================== + + >>> for e in commit.tree: + >>> print(e.name) + +---------------------------------------------------------------------- +References +---------------------------------------------------------------------- + +- git-show_. + +.. _git-show: https://www.kernel.org/pub/software/scm/git/docs/git-show.html diff --git a/docs/recipes/git-tag.rst b/docs/recipes/git-tag.rst new file mode 100644 index 000000000..8ed899ed0 --- /dev/null +++ b/docs/recipes/git-tag.rst @@ -0,0 +1,24 @@ +********************************************************************** +git-tag +********************************************************************** + +---------------------------------------------------------------------- +Showing all tags +---------------------------------------------------------------------- + +.. code-block:: bash + + $> git tag + +.. code-block:: python + + >>> regex = re.compile('^refs/tags') + >>> filter(lambda r: regex.match(r), repo.listall_references()) + +---------------------------------------------------------------------- +References +---------------------------------------------------------------------- + +- git-tag_. + +.. _git-tag: https://www.kernel.org/pub/software/scm/git/docs/git-tag.html From 1cfa429f4e014c7020e7f4bc0794b448e6d35a70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 10 Jul 2013 11:28:21 +0200 Subject: [PATCH 0562/2237] Fix unit test in config --- src/config.c | 50 ++++++++++++++++++++++++++++---------------------- src/utils.h | 3 ++- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/config.c b/src/config.c index 6fa58d986..a9f3322d9 100644 --- a/src/config.c +++ b/src/config.c @@ -75,11 +75,10 @@ Config_init(Config *self, PyObject *args, PyObject *kwds) if (err < 0) { git_config_free(self->config); - if (err == GIT_ENOTFOUND) { + if (err == GIT_ENOTFOUND) Error_set_exc(PyExc_IOError); - } else { + else Error_set(err); - } return -1; } @@ -187,11 +186,11 @@ Config_getitem(Config *self, PyObject *py_key) goto cleanup; if (git_config_parse_int64(&value_int, value_str) == 0) - py_value = PyLong_FromLongLong(value_int); + py_value = PyLong_FromLongLong(value_int); else if(git_config_parse_bool(&value_bool, value_str) == 0) - py_value = PyBool_FromLong(value_bool); + py_value = PyBool_FromLong(value_bool); else - py_value = to_unicode(value_str, NULL, NULL); + py_value = to_unicode(value_str, NULL, NULL); cleanup: free(key); @@ -323,10 +322,8 @@ Config_add_file(Config *self, PyObject *args, PyObject *kwds) return NULL; err = git_config_add_file_ondisk(self->config, path, level, force); - if (err < 0) { - Error_set_str(err, path); - return NULL; - } + if (err < 0) + return Error_set_str(err, path); Py_RETURN_NONE; } @@ -342,15 +339,21 @@ PyDoc_STRVAR(Config_get_multivar__doc__, int Config_get_multivar_fn_wrapper(const git_config_entry *value, void *data) { - PyObject *item = NULL; - - if (!(item = to_unicode(value->value, NULL, NULL))) + PyObject *item; + + item = to_unicode(value->value, NULL, NULL); + if (item == NULL) + /* FIXME Right now there is no way to forward errors through the + * libgit2 API, open an issue or pull-request to libgit2. + * + * See libgit2/src/config_file.c:443 (config_get_multivar). + * Comment says "early termination by the user is not an error". + * That's wrong. + */ return -2; PyList_Append((PyObject *)data, item); - Py_CLEAR(item); - return 0; } @@ -359,6 +362,7 @@ Config_get_multivar(Config *self, PyObject *args) { int err; PyObject *list; + Py_ssize_t size; const char *name = NULL; const char *regex = NULL; @@ -369,14 +373,16 @@ Config_get_multivar(Config *self, PyObject *args) err = git_config_get_multivar(self->config, name, regex, Config_get_multivar_fn_wrapper, (void *)list); - if (err < 0) { - Py_CLEAR(list); - if (err == GIT_ENOTFOUND) - Error_set(err); - else - PyErr_SetNone(PyExc_TypeError); - return NULL; + if (err < 0) { + /* XXX The return value of git_config_get_multivar is not reliable, + * see https://github.com/libgit2/libgit2/pull/1712 + * Once libgit2 0.20 is released, we will remove this test. */ + if (err == GIT_ENOTFOUND && PyList_Size(list) != 0) + return list; + + Py_CLEAR(list); + return Error_set(err); } return list; diff --git a/src/utils.h b/src/utils.h index 9b6a1b564..62699e835 100644 --- a/src/utils.h +++ b/src/utils.h @@ -85,7 +85,8 @@ PYGIT2_FN_UNUSED Py_LOCAL_INLINE(PyObject*) -to_unicode_n(const char *value, size_t len, const char *encoding, const char *errors) +to_unicode_n(const char *value, size_t len, const char *encoding, + const char *errors) { if (encoding == NULL) { /* If the encoding is not explicit, it may not be UTF-8, so it From da97e005929463ba4891478714b76b1a7289cc23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 10 Jul 2013 21:03:58 +0200 Subject: [PATCH 0563/2237] Comment broken tests And make clone_repository to give more informative errors. --- src/pygit2.c | 2 +- test/test_repository.py | 101 +++++++++++++++++++++++----------------- 2 files changed, 58 insertions(+), 45 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index 1a5ac6c23..a4729df63 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -153,7 +153,7 @@ clone_repository(PyObject *self, PyObject *args) { err = git_clone(&repo, url, path, &opts); if (err < 0) - return Error_set_str(err, path); + return Error_set(err); git_repository_free(repo); Py_RETURN_NONE; diff --git a/test/test_repository.py b/test/test_repository.py index 0c0592e3e..87c9fc704 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -40,11 +40,8 @@ # Import from pygit2 from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT -from pygit2 import ( - init_repository, clone_repository, discover_repository, - Reference, hashfile -) -from pygit2 import Oid +from pygit2 import init_repository, clone_repository, discover_repository +from pygit2 import Oid, Reference, hashfile import pygit2 from . import utils @@ -277,7 +274,9 @@ def test_keyword_arg_true(self): self.assertTrue(repo.is_bare) + class DiscoverRepositoryTest(utils.NoRepoTestCase): + def test_discover_repo(self): repo = init_repository(self._temp_dir, False) subdir = os.path.join(self._temp_dir, "test1", "test2") @@ -285,6 +284,7 @@ def test_discover_repo(self): self.assertEqual(repo.path, discover_repository(subdir)) + class EmptyRepositoryTest(utils.EmptyRepoTestCase): def test_is_empty(self): @@ -298,7 +298,9 @@ def test_head(self): self.assertFalse(self.repo.head_is_detached) + class CloneRepositoryTest(utils.NoRepoTestCase): + def test_clone_repository(self): repo_path = "./test/data/testrepo.git/" repo = clone_repository(repo_path, self._temp_dir) @@ -319,46 +321,57 @@ def test_clone_remote_name(self): self.assertFalse(repo.is_empty) self.assertEqual(repo.remotes[0].name, "custom_remote") - def test_clone_push_url(self): - repo_path = "./test/data/testrepo.git/" - repo = clone_repository( - repo_path, self._temp_dir, push_url="custom_push_url" - ) - self.assertFalse(repo.is_empty) - # FIXME: When pygit2 supports retrieving the pushurl parameter, - # enable this test - # self.assertEqual(repo.remotes[0].pushurl, "custom_push_url") - def test_clone_fetch_spec(self): - repo_path = "./test/data/testrepo.git/" - ## FIXME Uncomment these lines once libgit2 0.20 is released - ## repo = clone_repository(repo_path, self._temp_dir, - ## fetch_spec="refs/heads/test") - ## self.assertFalse(repo.is_empty) - - # FIXME: When pygit2 retrieve the fetchspec we passed to git clone. - # fetchspec seems to be going through, but the Repository class is - # not getting it. - # self.assertEqual(repo.remotes[0].fetchspec, "refs/heads/test") - - def test_clone_push_spec(self): - repo_path = "./test/data/testrepo.git/" - repo = clone_repository(repo_path, self._temp_dir, - push_spec="refs/heads/test") - self.assertFalse(repo.is_empty) - # FIXME: When pygit2 supports retrieving the pushspec parameter, - # enable this test - # not sure how to test this either... couldn't find pushspec - # self.assertEqual(repo.remotes[0].fetchspec, "refs/heads/test") - - def test_clone_checkout_branch(self): - repo_path = "./test/data/testrepo.git/" - repo = clone_repository(repo_path, self._temp_dir, - checkout_branch="test") - self.assertFalse(repo.is_empty) - # FIXME: When pygit2 supports retrieving the current branch, - # enable this test - # self.assertEqual(repo.remotes[0].current_branch, "test") + # FIXME The tests below are commented because they are broken: + # + # - test_clone_push_url: Passes, but does nothing useful. + # + # - test_clone_fetch_spec: Segfaults because of a bug in libgit2 0.19, + # this has been fixed already, so wait for 0.20 + # + # - test_clone_push_spec: Passes, but does nothing useful. + # + # - test_clone_checkout_branch: Fails, because the test fixture does not + # have any branch named "test" + +# def test_clone_push_url(self): +# repo_path = "./test/data/testrepo.git/" +# repo = clone_repository( +# repo_path, self._temp_dir, push_url="custom_push_url" +# ) +# self.assertFalse(repo.is_empty) +# # FIXME: When pygit2 supports retrieving the pushurl parameter, +# # enable this test +# # self.assertEqual(repo.remotes[0].pushurl, "custom_push_url") + +# def test_clone_fetch_spec(self): +# repo_path = "./test/data/testrepo.git/" +# repo = clone_repository(repo_path, self._temp_dir, +# fetch_spec="refs/heads/test") +# self.assertFalse(repo.is_empty) +# # FIXME: When pygit2 retrieve the fetchspec we passed to git clone. +# # fetchspec seems to be going through, but the Repository class is +# # not getting it. +# # self.assertEqual(repo.remotes[0].fetchspec, "refs/heads/test") + +# def test_clone_push_spec(self): +# repo_path = "./test/data/testrepo.git/" +# repo = clone_repository(repo_path, self._temp_dir, +# push_spec="refs/heads/test") +# self.assertFalse(repo.is_empty) +# # FIXME: When pygit2 supports retrieving the pushspec parameter, +# # enable this test +# # not sure how to test this either... couldn't find pushspec +# # self.assertEqual(repo.remotes[0].fetchspec, "refs/heads/test") + +# def test_clone_checkout_branch(self): +# repo_path = "./test/data/testrepo.git/" +# repo = clone_repository(repo_path, self._temp_dir, +# checkout_branch="test") +# self.assertFalse(repo.is_empty) +# # FIXME: When pygit2 supports retrieving the current branch, +# # enable this test +# # self.assertEqual(repo.remotes[0].current_branch, "test") if __name__ == '__main__': From 075495cb80db8751d3190e968ca62873b3a48bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Duraffort?= Date: Fri, 12 Jul 2013 10:13:45 +0200 Subject: [PATCH 0564/2237] Clarify order of assignment and test --- src/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index a9f3322d9..dc9b44f53 100644 --- a/src/config.c +++ b/src/config.c @@ -261,7 +261,7 @@ Config_foreach_callback_wrapper(const git_config_entry *entry, void *c_payload) if (!(py_result = PyObject_CallObject(py_callback, args))) return -1; - if ((c_result = PyLong_AsLong(py_result) == -1)) + if ((c_result = PyLong_AsLong(py_result)) == -1) return -1; Py_CLEAR(args); From 5a007802d0592a6db2cfaa5a58929d97bb2c4897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 13 Jul 2013 12:04:14 +0200 Subject: [PATCH 0565/2237] Release 0.19.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API changes: - New Oid type - Changed signature of Repository.create_reference - Reference.oid and Reference.hex removed, use Reference.target instead - Drop "del Index[path]" from the API, use Index.remove(path) instead - Drop TreeEntry.to_object - Changed signature of Repository.checkout - Repository.create_blob_from_file removed, use instead new methods Repository.create_blob_fromworkdir and Repository.create_blob_from_disk New features: - Add len(TreeBuilder) and TreeBuilder.get - Add Repository.merge_base - Support changing the head with "Repository.head = refname" - Improved support for diff - Add support for clone - Python 2: Support hex oids as byte strings - Add Reference.get_object() - Add Remote.save - Add support for branches, new type Branch Other: - Upgraded to libgit2 0.19 - Partial documentation review Thanks to Nico von Geyso, Daniel Rodríguez Troitiño, Bernardo Heynemann, Rémi Duraffort, Andrey Devyatkin, Hervé Cauwelier, Jiunn Haur Lim, Richo Healey, Carlos Martín Nieto, David Fischer, Fraser Tweedale, Jun Omae and Xu Tao. --- README.rst | 5 ++++- docs/conf.py | 4 ++-- docs/install.rst | 4 ++-- pygit2/version.py | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index f421b76cf..a64219bda 100644 --- a/README.rst +++ b/README.rst @@ -53,6 +53,7 @@ This is the list of authors of pygit2, sorted by number of commits (as shown by - W Trevor King - Dave Borowitz - Carlos Martín Nieto +- Daniel Rodríguez Troitiño - Richo Healey - Christian Boos - Julien Miotte @@ -64,6 +65,7 @@ This is the list of authors of pygit2, sorted by number of commits (as shown by - John Szakmeister - David Versmisse - Petr Hosek +- Rémi Duraffort - Sebastian Thiel - Han-Wen Nienhuys - Petr Viktorin @@ -71,9 +73,9 @@ This is the list of authors of pygit2, sorted by number of commits (as shown by - Amit Bakshi - Andrey Devyatkin - Ben Davis -- Daniel Rodríguez Troitiño - Hervé Cauwelier - Jared Flatow +- Jiunn Haur Lim - Sarath Lakshman - Vicent Marti - Zoran Zaric @@ -81,6 +83,7 @@ This is the list of authors of pygit2, sorted by number of commits (as shown by - Benjamin Kircher - Benjamin Pollack - Bryan O'Sullivan +- David Fischer - David Sanders - Eric Davis - Eric Schrijver diff --git a/docs/conf.py b/docs/conf.py index d854107b4..bb653d7c2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,9 +48,9 @@ # built documents. # # The short X.Y version. -version = '0.18' +version = '0.19' # The full version, including alpha/beta/rc tags. -release = '0.18.1' +release = '0.19.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/install.rst b/docs/install.rst index 177e20852..93a5563d1 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -26,8 +26,8 @@ When those are installed, you can install pygit2: $ python setup.py test .. note:: A minor version of pygit2 must be used with the corresponding minor - version of libgit2. For example, pygit2 v0.18.x must be used with libgit2 - v0.18.0. + version of libgit2. For example, pygit2 v0.19.x must be used with libgit2 + v0.19.0. Building on \*nix (including OS X) =================================== diff --git a/pygit2/version.py b/pygit2/version.py index ffe9169ea..3ac321ca4 100644 --- a/pygit2/version.py +++ b/pygit2/version.py @@ -23,4 +23,4 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -__version__ = '0.18.1' +__version__ = '0.19.0' From 298f941036192f80aaa90ed2eaab2a24bc4d5e77 Mon Sep 17 00:00:00 2001 From: Brodie Rao Date: Wed, 14 Aug 2013 20:11:19 -0700 Subject: [PATCH 0566/2237] tag: add get_object() method This maps to git_tag_peel(). --- docs/objects.rst | 2 ++ src/tag.c | 27 ++++++++++++++++++++++++++- test/test_tag.py | 5 +++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/objects.rst b/docs/objects.rst index 36edf958f..6ee3e1ed0 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -267,6 +267,8 @@ A tag is a static label for a commit. See references for more information. .. autoattribute:: pygit2.Tag.tagger .. autoattribute:: pygit2.Tag.message +.. automethod:: pygit2.Tag.get_object + Creating tags -------------------- diff --git a/src/tag.c b/src/tag.c index bc4eaada7..35b9af578 100644 --- a/src/tag.c +++ b/src/tag.c @@ -27,6 +27,7 @@ #define PY_SSIZE_T_CLEAN #include +#include "object.h" #include "error.h" #include "types.h" #include "utils.h" @@ -47,6 +48,25 @@ Tag_target__get__(Tag *self) } +PyDoc_STRVAR(Tag_get_object__doc__, + "get_object() -> object\n" + "\n" + "Retrieves the object the current reference is pointing to."); + +PyObject * +Tag_get_object(Tag *self) +{ + int err; + git_object* obj; + + err = git_tag_peel(&obj, self->tag); + if (err < 0) + return Error_set(err); + + return wrap_object(obj, self->repo); +} + + PyDoc_STRVAR(Tag_name__doc__, "Tag name."); PyObject * @@ -94,6 +114,11 @@ Tag__message__get__(Tag *self) return PyBytes_FromString(git_tag_message(self->tag)); } +PyMethodDef Tag_methods[] = { + METHOD(Tag, get_object, METH_NOARGS), + {NULL} +}; + PyGetSetDef Tag_getseters[] = { GETTER(Tag, target), GETTER(Tag, name), @@ -134,7 +159,7 @@ PyTypeObject TagType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + Tag_methods, /* tp_methods */ 0, /* tp_members */ Tag_getseters, /* tp_getset */ 0, /* tp_base */ diff --git a/test/test_tag.py b/test/test_tag.py index 12d89f055..9530eb5cc 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -89,6 +89,11 @@ def test_modify_tag(self): self.assertRaises(AttributeError, setattr, tag, 'tagger', tagger) self.assertRaises(AttributeError, setattr, tag, 'message', message) + def test_get_object(self): + repo = self.repo + tag = repo[TAG_SHA] + self.assertEqual(repo[tag.target].oid, tag.get_object().oid) + if __name__ == '__main__': unittest.main() From 6d87567e0f029528b7fc89e8d49e6898ab5b7ab5 Mon Sep 17 00:00:00 2001 From: Brodie Rao Date: Fri, 16 Aug 2013 10:56:15 -0700 Subject: [PATCH 0567/2237] tag: fix incorrect doc string wording for get_object() --- src/tag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tag.c b/src/tag.c index 35b9af578..c5fdd0511 100644 --- a/src/tag.c +++ b/src/tag.c @@ -51,7 +51,7 @@ Tag_target__get__(Tag *self) PyDoc_STRVAR(Tag_get_object__doc__, "get_object() -> object\n" "\n" - "Retrieves the object the current reference is pointing to."); + "Retrieves the object the current tag is pointing to."); PyObject * Tag_get_object(Tag *self) From b55650e093bd7507eec25ce35debd37497368c4b Mon Sep 17 00:00:00 2001 From: Brodie Rao Date: Mon, 19 Aug 2013 11:53:48 -0700 Subject: [PATCH 0568/2237] commit: rename Commit._message to Commit.raw_message --- docs/objects.rst | 1 + src/commit.c | 6 +++--- test/test_commit.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index 6ee3e1ed0..6cfd80b3d 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -216,6 +216,7 @@ committer and others. .. autoattribute:: pygit2.Commit.committer .. autoattribute:: pygit2.Commit.message .. autoattribute:: pygit2.Commit.message_encoding +.. autoattribute:: pygit2.Commit.raw_message .. autoattribute:: pygit2.Commit.tree .. autoattribute:: pygit2.Commit.parents .. autoattribute:: pygit2.Commit.commit_time diff --git a/src/commit.c b/src/commit.c index 74b1ecc16..725a3c44e 100644 --- a/src/commit.c +++ b/src/commit.c @@ -64,10 +64,10 @@ Commit_message__get__(Commit *commit) } -PyDoc_STRVAR(Commit__message__doc__, "Message (bytes)."); +PyDoc_STRVAR(Commit_raw_message__doc__, "Message (bytes)."); PyObject * -Commit__message__get__(Commit *commit) +Commit_raw_message__get__(Commit *commit) { return PyBytes_FromString(git_commit_message(commit->commit)); } @@ -195,7 +195,7 @@ Commit_parents__get__(Commit *self) PyGetSetDef Commit_getseters[] = { GETTER(Commit, message_encoding), GETTER(Commit, message), - GETTER(Commit, _message), + GETTER(Commit, raw_message), GETTER(Commit, commit_time), GETTER(Commit, commit_time_offset), GETTER(Commit, committer), diff --git a/test/test_commit.py b/test/test_commit.py index 13acf982e..85f3d2350 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -113,7 +113,7 @@ def test_new_commit_encoding(self): self.assertEqual(GIT_OBJ_COMMIT, commit.type) self.assertEqual('iso-8859-1', commit.message_encoding) - self.assertEqual(message, commit.message) + self.assertEqual(message.encode(encoding), commit.raw_message) self.assertEqual(12346, commit.commit_time) self.assertEqualSignature(committer, commit.committer) self.assertEqualSignature(author, commit.author) From aec44c9ca2978d91c231491929f5f750fb3e4aa8 Mon Sep 17 00:00:00 2001 From: Brodie Rao Date: Mon, 19 Aug 2013 13:38:57 -0700 Subject: [PATCH 0569/2237] signature: rename Signature._name/_email to Signature.raw_name/raw_email --- docs/objects.rst | 2 ++ src/signature.c | 12 ++++++------ test/test_signature.py | 24 ++++++++++++++++++------ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index 6ee3e1ed0..2458511b7 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -232,7 +232,9 @@ objects:: .. autoattribute:: pygit2.Signature.name +.. autoattribute:: pygit2.Signature.raw_name .. autoattribute:: pygit2.Signature.email +.. autoattribute:: pygit2.Signature.raw_email .. autoattribute:: pygit2.Signature.time .. autoattribute:: pygit2.Signature.offset diff --git a/src/signature.c b/src/signature.c index 24e02a83e..61fd0746c 100644 --- a/src/signature.c +++ b/src/signature.c @@ -107,19 +107,19 @@ Signature__encoding__get__(Signature *self) } -PyDoc_STRVAR(Signature__name__doc__, "Name (bytes)."); +PyDoc_STRVAR(Signature_raw_name__doc__, "Name (bytes)."); PyObject * -Signature__name__get__(Signature *self) +Signature_raw_name__get__(Signature *self) { return to_bytes(self->signature->name); } -PyDoc_STRVAR(Signature__email__doc__, "Email (bytes)."); +PyDoc_STRVAR(Signature_raw_email__doc__, "Email (bytes)."); PyObject * -Signature__email__get__(Signature *self) +Signature_raw_email__get__(Signature *self) { return to_bytes(self->signature->email); } @@ -162,8 +162,8 @@ Signature_offset__get__(Signature *self) PyGetSetDef Signature_getseters[] = { GETTER(Signature, _encoding), - GETTER(Signature, _name), - GETTER(Signature, _email), + GETTER(Signature, raw_name), + GETTER(Signature, raw_email), GETTER(Signature, name), GETTER(Signature, email), GETTER(Signature, time), diff --git a/test/test_signature.py b/test/test_signature.py index cf21b33f1..4131c6c83 100644 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -41,8 +41,12 @@ def test_default(self): 'Foo', 'foo@example.com', 1322174594, 60) encoding = signature._encoding self.assertEqual(encoding, 'ascii') - self.assertEqual(signature.name, signature._name.decode(encoding)) - self.assertEqual(signature.name.encode(encoding), signature._name) + self.assertEqual(signature.name, signature.raw_name.decode(encoding)) + self.assertEqual(signature.name.encode(encoding), signature.raw_name) + self.assertEqual(signature.email, + signature.raw_email.decode(encoding)) + self.assertEqual(signature.email.encode(encoding), + signature.raw_email) def test_ascii(self): self.assertRaises(UnicodeEncodeError, @@ -53,16 +57,24 @@ def test_latin1(self): signature = Signature( 'Foo Ibáñez', 'foo@example.com', encoding=encoding) self.assertEqual(encoding, signature._encoding) - self.assertEqual(signature.name, signature._name.decode(encoding)) - self.assertEqual(signature.name.encode(encoding), signature._name) + self.assertEqual(signature.name, signature.raw_name.decode(encoding)) + self.assertEqual(signature.name.encode(encoding), signature.raw_name) + self.assertEqual(signature.email, + signature.raw_email.decode(encoding)) + self.assertEqual(signature.email.encode(encoding), + signature.raw_email) def test_now(self): encoding = 'utf-8' signature = Signature( 'Foo Ibáñez', 'foo@example.com', encoding=encoding) self.assertEqual(encoding, signature._encoding) - self.assertEqual(signature.name, signature._name.decode(encoding)) - self.assertEqual(signature.name.encode(encoding), signature._name) + self.assertEqual(signature.name, signature.raw_name.decode(encoding)) + self.assertEqual(signature.name.encode(encoding), signature.raw_name) + self.assertEqual(signature.email, + signature.raw_email.decode(encoding)) + self.assertEqual(signature.email.encode(encoding), + signature.raw_email) self.assertTrue(abs(signature.time - time.time()) < 5) From af68816d4abed88cf72d7c977119cb9152c1d8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 24 Aug 2013 11:59:44 +0200 Subject: [PATCH 0570/2237] Make pygit2.TreeBuilder() to fail (issue #265) --- src/pygit2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygit2.c b/src/pygit2.c index a4729df63..20fd27a1d 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -281,7 +281,7 @@ moduleinit(PyObject* m) INIT_TYPE(TreeType, &ObjectType, NULL) INIT_TYPE(TreeEntryType, NULL, NULL) INIT_TYPE(TreeIterType, NULL, NULL) - INIT_TYPE(TreeBuilderType, NULL, PyType_GenericNew) + INIT_TYPE(TreeBuilderType, NULL, NULL) INIT_TYPE(BlobType, &ObjectType, NULL) INIT_TYPE(TagType, &ObjectType, NULL) ADD_TYPE(m, Object) From 179c7db9400b18242cc10f4832b181f6bd5ec0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 29 Aug 2013 17:18:01 +0200 Subject: [PATCH 0571/2237] Don't forget to remove the temporary directory in the config tests When overriding the tear-down function, we must remember to call the parent's function or we won't clean up the temporary directory for the test. --- test/test_config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_config.py b/test/test_config.py index a8afe86cb..8740a64c9 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -46,6 +46,7 @@ def foreach_test_wrapper(key, name, lst): class ConfigTest(utils.RepoTestCase): def tearDown(self): + super(ConfigTest, self).tearDown() try: os.remove(CONFIG_FILENAME) except OSError: From 74b1628e5170a0406db62d726b61933de4825b56 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Thu, 29 Aug 2013 23:22:36 +1000 Subject: [PATCH 0572/2237] test utils: implement a repo context manager The current hierarchy of test cases that create and blow away test repositories in the ``setUp`` and ``tearDown`` methods is inadequate for testing scenarios where multiple repositories must be edited, e.g. testing push. Extract the temporary repository behaviour into a context manager class ``TemporaryRepository`` which can be used via the ``with`` keyword. The context manager's ``__enter__` method returns creates the specified repository in a temporary directory and returns its path. The temporary directory is removed in ``__exit__``. Update the existing test case base classes to use the context manager and invoke ``__enter__`` and ``__exit__`` in the ``setUp`` and ``tearDown`` methods respectively. Finally, make some small tweaks to a handful of tests to get them working under this new system. --- test/test_blob.py | 5 ++-- test/test_repository.py | 6 ++-- test/utils.py | 65 +++++++++++++++++++++++------------------ 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/test/test_blob.py b/test/test_blob.py index 8105455f7..2f5259649 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -93,13 +93,12 @@ def test_create_blob_fromworkdir(self): def test_create_blob_outside_workdir(self): - path = join(dirname(__file__), 'data', self.repo_dir + '.tar') + path = __file__ self.assertRaises(KeyError, self.repo.create_blob_fromworkdir, path) def test_create_blob_fromdisk(self): - path = join(dirname(__file__), 'data', self.repo_dir + '.tar') - blob_oid = self.repo.create_blob_fromdisk(path) + blob_oid = self.repo.create_blob_fromdisk(__file__) blob = self.repo[blob_oid] self.assertTrue(isinstance(blob, pygit2.Blob)) diff --git a/test/test_repository.py b/test/test_repository.py index 87c9fc704..ab0b22c89 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -143,7 +143,7 @@ def test_lookup_commit_prefix(self): def test_get_path(self): directory = realpath(self.repo.path) - expected = realpath(join(self._temp_dir, 'testrepo.git')) + expected = realpath(self.repo_path) self.assertEqual(directory, expected) def test_get_workdir(self): @@ -179,12 +179,12 @@ def test_is_bare(self): def test_get_path(self): directory = realpath(self.repo.path) - expected = realpath(join(self._temp_dir, 'testrepo', '.git')) + expected = realpath(join(self.repo_path, '.git')) self.assertEqual(directory, expected) def test_get_workdir(self): directory = realpath(self.repo.workdir) - expected = realpath(join(self._temp_dir, 'testrepo')) + expected = realpath(self.repo_path) self.assertEqual(directory, expected) def test_checkout_ref(self): diff --git a/test/utils.py b/test/utils.py index 10a4e1719..fb7ca58f9 100644 --- a/test/utils.py +++ b/test/utils.py @@ -65,6 +65,27 @@ def rmtree(path): shutil.rmtree(path, onerror=onerror) +class TemporaryRepository(object): + def __init__(self, repo_spec): + self.repo_spec = repo_spec + + def __enter__(self): + container, name = self.repo_spec + repo_path = os.path.join(os.path.dirname(__file__), 'data', name) + self.temp_dir = tempfile.mkdtemp() + temp_repo_path = os.path.join(self.temp_dir, name) + if container == 'tar': + tar = tarfile.open('.'.join((repo_path, 'tar'))) + tar.extractall(self.temp_dir) + tar.close() + else: + shutil.copytree(repo_path, temp_repo_path) + return temp_repo_path + + def __exit__(self, exc_type, exc_value, traceback): + rmtree(self.temp_dir) + + class NoRepoTestCase(unittest.TestCase): def setUp(self): @@ -103,45 +124,33 @@ def assertEqualSignature(self, a, b): self.assertEqual(a.offset, b.offset) -class BareRepoTestCase(NoRepoTestCase): - - repo_dir = 'testrepo.git' - +class AutoRepoTestCase(NoRepoTestCase): def setUp(self): - super(BareRepoTestCase, self).setUp() - - repo_dir = self.repo_dir - repo_path = os.path.join(os.path.dirname(__file__), 'data', repo_dir) - temp_repo_path = os.path.join(self._temp_dir, repo_dir) - - shutil.copytree(repo_path, temp_repo_path) - - self.repo = pygit2.Repository(temp_repo_path) + super(AutoRepoTestCase, self).setUp() + self.repo_ctxtmgr = TemporaryRepository(self.repo_spec) + self.repo_path = self.repo_ctxtmgr.__enter__() + self.repo = pygit2.Repository(self.repo_path) + def tearDown(self): + self.repo_ctxtmgr.__exit__(None, None, None) + super(AutoRepoTestCase, self).tearDown() -class RepoTestCase(NoRepoTestCase): - repo_dir = 'testrepo' +class BareRepoTestCase(AutoRepoTestCase): - def setUp(self): - super(RepoTestCase, self).setUp() + repo_spec = 'git', 'testrepo.git' - repo_dir = self.repo_dir - repo_path = os.path.join(os.path.dirname(__file__), 'data', repo_dir) - temp_repo_path = os.path.join(self._temp_dir, repo_dir, '.git') - tar = tarfile.open(repo_path + '.tar') - tar.extractall(self._temp_dir) - tar.close() +class RepoTestCase(AutoRepoTestCase): - self.repo = pygit2.Repository(temp_repo_path) + repo_spec = 'tar', 'testrepo' -class DirtyRepoTestCase(RepoTestCase): +class DirtyRepoTestCase(AutoRepoTestCase): - repo_dir = 'dirtyrepo' + repo_spec = 'tar', 'dirtyrepo' -class EmptyRepoTestCase(RepoTestCase): +class EmptyRepoTestCase(AutoRepoTestCase): - repo_dir = 'emptyrepo' + repo_spec = 'tar', 'emptyrepo' From 134d87ab2a06459cb6c273cdbc82cc5a0aa5aa3c Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Sun, 25 Aug 2013 19:51:20 +1000 Subject: [PATCH 0573/2237] implement push support Implement push support via Remote.push which is called with a single refspec and raises GitError (with an appropriate message where possible) if the push fails. Note that local push to non-bare repository is currently not supported by libgit2. --- docs/remotes.rst | 1 + src/remote.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ test/test_remote.py | 40 +++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/docs/remotes.rst b/docs/remotes.rst index 91a69d055..4b9692fcd 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -15,4 +15,5 @@ The Remote type .. autoattribute:: pygit2.Remote.refspec_count .. automethod:: pygit2.Remote.get_refspec .. automethod:: pygit2.Remote.fetch +.. automethod:: pygit2.Remote.push .. automethod:: pygit2.Remote.save diff --git a/src/remote.c b/src/remote.c index 9faf11435..e482d8b1c 100644 --- a/src/remote.c +++ b/src/remote.c @@ -218,10 +218,76 @@ Remote_save(Remote *self, PyObject *args) } +int +push_status_foreach_callback(const char *ref, const char *msg, void *data) +{ + const char **msg_dst = (const char **)data; + if (msg != NULL && *msg_dst == NULL) + *msg_dst = msg; + return 0; +} + +PyDoc_STRVAR(Remote_push__doc__, + "push(refspec)\n" + "\n" + "Push the given refspec to the remote. Raises ``GitError`` on error."); + +PyObject * +Remote_push(Remote *self, PyObject *args) +{ + git_push *push = NULL; + const char *refspec = NULL; + const char *msg = NULL; + int err; + + if (!PyArg_ParseTuple(args, "s", &refspec)) + return NULL; + + err = git_push_new(&push, self->remote); + if (err < 0) + return Error_set(err); + + err = git_push_add_refspec(push, refspec); + if (err < 0) + goto error; + + err = git_push_finish(push); + if (err < 0) + goto error; + + if (!git_push_unpack_ok(push)) { + git_push_free(push); + PyErr_SetString(GitError, "Remote failed to unpack objects"); + return NULL; + } + + err = git_push_status_foreach(push, push_status_foreach_callback, &msg); + if (err < 0) + goto error; + if (msg != NULL) { + git_push_free(push); + PyErr_SetString(GitError, msg); + return NULL; + } + + err = git_push_update_tips(push); + if (err < 0) + goto error; + + git_push_free(push); + Py_RETURN_NONE; + +error: + git_push_free(push); + return Error_set(err); +} + + PyMethodDef Remote_methods[] = { METHOD(Remote, fetch, METH_NOARGS), METHOD(Remote, save, METH_NOARGS), METHOD(Remote, get_refspec, METH_O), + METHOD(Remote, push, METH_VARARGS), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index a4a125966..b6a9edc5d 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -124,5 +124,45 @@ def test_fetch(self): self.assertEqual(stats['received_objects'], REMOTE_REPO_OBJECTS) +class PushTestCase(unittest.TestCase): + def setUp(self): + self.origin_ctxtmgr = utils.TemporaryRepository(('git', 'testrepo.git')) + self.clone_ctxtmgr = utils.TemporaryRepository(('git', 'testrepo.git')) + self.origin = pygit2.Repository(self.origin_ctxtmgr.__enter__()) + self.clone = pygit2.Repository(self.clone_ctxtmgr.__enter__()) + self.remote = self.clone.create_remote('origin', self.origin.path) + + def tearDown(self): + self.origin_ctxtmgr.__exit__(None, None, None) + self.clone_ctxtmgr.__exit__(None, None, None) + + def test_push_fast_forward_commits_to_remote_succeeds(self): + tip = self.clone[self.clone.head.target] + oid = self.clone.create_commit( + 'refs/heads/master', tip.author, tip.author, 'empty commit', + tip.tree.oid, [tip.oid] + ) + self.remote.push('refs/heads/master') + self.assertEqual(self.origin[self.origin.head.target].oid, oid) + + def test_push_when_up_to_date_succeeds(self): + self.remote.push('refs/heads/master') + origin_tip = self.origin[self.origin.head.target].oid + clone_tip = self.clone[self.clone.head.target].oid + self.assertEqual(origin_tip, clone_tip) + + def test_push_non_fast_forward_commits_to_remote_fails(self): + tip = self.origin[self.origin.head.target] + oid = self.origin.create_commit( + 'refs/heads/master', tip.author, tip.author, 'some commit', + tip.tree.oid, [tip.oid] + ) + tip = self.clone[self.clone.head.target] + oid = self.clone.create_commit( + 'refs/heads/master', tip.author, tip.author, 'other commit', + tip.tree.oid, [tip.oid] + ) + self.assertRaises(pygit2.GitError, self.remote.push, 'refs/heads/master') + if __name__ == '__main__': unittest.main() From d5c0a23630d2471e4fe3772d39343201f7cd130a Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sun, 1 Sep 2013 12:58:55 -0400 Subject: [PATCH 0574/2237] Doc fixes: change head.oid to head.target in examples --- docs/recipes/git-log.rst | 4 ++-- src/repository.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/recipes/git-log.rst b/docs/recipes/git-log.rst index e9c10b736..b234e0f61 100644 --- a/docs/recipes/git-log.rst +++ b/docs/recipes/git-log.rst @@ -16,7 +16,7 @@ Show HEAD commit .. code-block:: python - >>> commit = repo[repo.head.oid] + >>> commit = repo[repo.head.target] >>> commit.message 'commit message' @@ -30,7 +30,7 @@ Traverse commit history .. code-block:: python - >>> last = repo[repo.head.oid] + >>> last = repo[repo.head.target] >>> for commit in repo.walk(last.oid, pygit2.GIT_SORT_TIME): >>> print(commit.message) # or some other operation diff --git a/src/repository.c b/src/repository.c index 935c1665f..2ce42ab29 100644 --- a/src/repository.c +++ b/src/repository.c @@ -591,9 +591,9 @@ PyDoc_STRVAR(Repository_walk__doc__, " >>> from pygit2 import Repository\n" " >>> from pygit2 import GIT_SORT_TOPOLOGICAL, GIT_SORT_REVERSE\n" " >>> repo = Repository('.git')\n" - " >>> for commit in repo.walk(repo.head.oid, GIT_SORT_TOPOLOGICAL):\n" + " >>> for commit in repo.walk(repo.head.target, GIT_SORT_TOPOLOGICAL):\n" " ... print commit.message\n" - " >>> for commit in repo.walk(repo.head.oid, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE):\n" + " >>> for commit in repo.walk(repo.head.target, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE):\n" " ... print commit.message\n" " >>>\n"); From 9c13be8dec39c60460182d1632324999333f3769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 3 Oct 2013 20:35:58 +0200 Subject: [PATCH 0575/2237] Release 0.19.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API changes: - Rename Commit._message to Commit.raw_message - Rename Signature._name to Signature.raw_name - Rename Signature._email to Signature.raw_email New features: - Remote.push(refspec) - Tag.get_object() And some bugs fixed. Thanks to Brodie Rao, Fraser Tweedale, Andrew Chin and Carlos Martín Nieto. --- .mailmap | 5 +++-- README.rst | 4 +++- docs/conf.py | 2 +- pygit2/version.py | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.mailmap b/.mailmap index da003e239..5c3799bdd 100644 --- a/.mailmap +++ b/.mailmap @@ -1,6 +1,7 @@ +Carlos Martín Nieto +Christian Boos J. David Ibáñez +Martin Lenders Richo Healey Xavier Delannoy -Christian Boos -Martin Lenders Xu Tao diff --git a/README.rst b/README.rst index a64219bda..3b2d6e562 100644 --- a/README.rst +++ b/README.rst @@ -63,10 +63,12 @@ This is the list of authors of pygit2, sorted by number of commits (as shown by - Valentin Haenel - Bernardo Heynemann - John Szakmeister +- Brodie Rao - David Versmisse - Petr Hosek - Rémi Duraffort - Sebastian Thiel +- Fraser Tweedale - Han-Wen Nienhuys - Petr Viktorin - Alex Chamberlain @@ -79,6 +81,7 @@ This is the list of authors of pygit2, sorted by number of commits (as shown by - Sarath Lakshman - Vicent Marti - Zoran Zaric +- Andrew Chin - András Veres-Szentkirályi - Benjamin Kircher - Benjamin Pollack @@ -89,7 +92,6 @@ This is the list of authors of pygit2, sorted by number of commits (as shown by - Eric Schrijver - Erik van Zijst - Ferengee -- Fraser Tweedale - Hugh Cole-Baker - Josh Bleecher Snyder - Jun Omae diff --git a/docs/conf.py b/docs/conf.py index bb653d7c2..4ec29bcbd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,7 +50,7 @@ # The short X.Y version. version = '0.19' # The full version, including alpha/beta/rc tags. -release = '0.19.0' +release = '0.19.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/pygit2/version.py b/pygit2/version.py index 3ac321ca4..8fe724b7a 100644 --- a/pygit2/version.py +++ b/pygit2/version.py @@ -23,4 +23,4 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -__version__ = '0.19.0' +__version__ = '0.19.1' From c6d2a65d9fe61a993b784de1e59aed0e37269ffc Mon Sep 17 00:00:00 2001 From: XTao Date: Wed, 30 Oct 2013 14:59:17 +0800 Subject: [PATCH 0576/2237] Add additions and deletions for patch. --- src/diff.c | 7 ++++++- src/types.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/diff.c b/src/diff.c index f46d4bdb7..489e7f9eb 100644 --- a/src/diff.c +++ b/src/diff.c @@ -63,7 +63,7 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) const git_diff_delta* delta; const git_diff_range* range; git_diff_patch* patch = NULL; - size_t i, j, hunk_amounts, lines_in_hunk, line_len, header_len; + size_t i, j, hunk_amounts, lines_in_hunk, line_len, header_len, additions, deletions; const char* line, *header; char line_origin; int err; @@ -84,6 +84,9 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) py_patch->old_oid = git_oid_allocfmt(&delta->old_file.oid); py_patch->new_oid = git_oid_allocfmt(&delta->new_file.oid); + git_diff_patch_line_stats(NULL, &additions, &deletions, patch); + py_patch->additions = additions; + py_patch->deletions = deletions; hunk_amounts = git_diff_patch_num_hunks(patch); py_patch->hunks = PyList_New(hunk_amounts); @@ -152,6 +155,8 @@ PyMemberDef Patch_members[] = { MEMBER(Patch, status, T_CHAR, "status"), MEMBER(Patch, similarity, T_INT, "similarity"), MEMBER(Patch, hunks, T_OBJECT, "hunks"), + MEMBER(Patch, additions, T_INT, "additions"), + MEMBER(Patch, deletions, T_INT, "deletions"), {NULL} }; diff --git a/src/types.h b/src/types.h index 926d74ad2..757ea4eb4 100644 --- a/src/types.h +++ b/src/types.h @@ -113,6 +113,8 @@ typedef struct { char* new_oid; char status; unsigned similarity; + unsigned additions; + unsigned deletions; } Patch; typedef struct { From d0b366e86614ec4b7e29ca3e98c89632c1c5f226 Mon Sep 17 00:00:00 2001 From: XTao Date: Wed, 30 Oct 2013 15:06:40 +0800 Subject: [PATCH 0577/2237] Add binary for patch. --- src/diff.c | 19 ++++++++++++++++++- src/types.h | 1 + test/test_diff.py | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/diff.c b/src/diff.c index f46d4bdb7..e7adb50cc 100644 --- a/src/diff.c +++ b/src/diff.c @@ -81,6 +81,7 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) py_patch->new_file_path = delta->new_file.path; py_patch->status = git_diff_status_char(delta->status); py_patch->similarity = delta->similarity; + py_patch->flags = delta->flags; py_patch->old_oid = git_oid_allocfmt(&delta->old_file.oid); py_patch->new_oid = git_oid_allocfmt(&delta->new_file.oid); @@ -155,6 +156,22 @@ PyMemberDef Patch_members[] = { {NULL} }; +PyDoc_STRVAR(Patch_binary__doc__, "Binary."); + +PyObject * +Patch_binary__get__(Patch *self) +{ + if (!(self->flags & GIT_DIFF_FLAG_NOT_BINARY) && + (self->flags & GIT_DIFF_FLAG_BINARY)) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + +PyGetSetDef Patch_getseters[] = { + GETTER(Patch, binary), + {NULL} +}; + PyDoc_STRVAR(Patch__doc__, "Diff patch object."); PyTypeObject PatchType = { @@ -187,7 +204,7 @@ PyTypeObject PatchType = { 0, /* tp_iternext */ 0, /* tp_methods */ Patch_members, /* tp_members */ - 0, /* tp_getset */ + Patch_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ diff --git a/src/types.h b/src/types.h index 926d74ad2..2b6c2251e 100644 --- a/src/types.h +++ b/src/types.h @@ -113,6 +113,7 @@ typedef struct { char* new_oid; char status; unsigned similarity; + unsigned flags; } Patch; typedef struct { diff --git a/test/test_diff.py b/test/test_diff.py index 697b8be19..0dcd22fec 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -175,6 +175,7 @@ def _test(diff): self.assertEqual(patch.old_file_path, 'a') self.assertEqual(patch.new_file_path, 'a') + self.assertEqual(patch.binary, False) _test(commit_a.tree.diff_to_tree(commit_b.tree)) _test(self.repo.diff(COMMIT_SHA1_1, COMMIT_SHA1_2)) From f74110a023b98322cc271533d3aec813dbf4bb9d Mon Sep 17 00:00:00 2001 From: XTao Date: Wed, 30 Oct 2013 15:24:07 +0800 Subject: [PATCH 0578/2237] Add binary for blob. --- src/blob.c | 12 ++++++++++++ test/test_blob.py | 1 + 2 files changed, 13 insertions(+) diff --git a/src/blob.c b/src/blob.c index 465d8d651..1d41a2283 100644 --- a/src/blob.c +++ b/src/blob.c @@ -41,12 +41,24 @@ Blob_size__get__(Blob *self) } +PyDoc_STRVAR(Blob_binary__doc__, "Binary."); + +PyObject * +Blob_binary__get__(Blob *self) +{ + if (git_blob_is_binary(self->blob)) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + + PyDoc_STRVAR(Blob_data__doc__, "The contents of the blob, a bytes string. This is the same as\n" "Blob.read_raw()"); PyGetSetDef Blob_getseters[] = { GETTER(Blob, size), + GETTER(Blob, binary), {"data", (getter)Object_read_raw, NULL, Blob_data__doc__, NULL}, {NULL} }; diff --git a/test/test_blob.py b/test/test_blob.py index 2f5259649..d1a6897e9 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -53,6 +53,7 @@ def test_read_blob(self): sha = blob.oid.hex self.assertEqual(sha, BLOB_SHA) self.assertTrue(isinstance(blob, pygit2.Blob)) + self.assertFalse(blob.binary) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) self.assertEqual(BLOB_CONTENT, blob.data) self.assertEqual(len(BLOB_CONTENT), blob.size) From 9730564c735c7746c85614c42d0f10e40321261e Mon Sep 17 00:00:00 2001 From: Huang Huang Date: Wed, 30 Oct 2013 15:48:29 +0800 Subject: [PATCH 0579/2237] the number of deltas/patches in one diff --- src/diff.c | 7 +++++++ test/test_diff.py | 1 + 2 files changed, 8 insertions(+) diff --git a/src/diff.c b/src/diff.c index f46d4bdb7..0728d7de5 100644 --- a/src/diff.c +++ b/src/diff.c @@ -249,6 +249,12 @@ PyTypeObject DiffIterType = { (iternextfunc) DiffIter_iternext, /* tp_iternext */ }; +PyDoc_STRVAR(Diff_size__doc__, "Returns the number of deltas/patches in this diff."); +PyObject * +Diff_size__get__(Diff *self) +{ + return PyLong_FromSize_t(git_diff_num_deltas(self->list)); +} PyDoc_STRVAR(Diff_patch__doc__, "Patch diff string."); @@ -440,6 +446,7 @@ Diff_dealloc(Diff *self) PyGetSetDef Diff_getseters[] = { GETTER(Diff, patch), + GETTER(Diff, size), {NULL} }; diff --git a/test/test_diff.py b/test/test_diff.py index 697b8be19..a2564c0cd 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -254,6 +254,7 @@ def test_diff_patch(self): diff = commit_a.tree.diff_to_tree(commit_b.tree) self.assertEqual(diff.patch, PATCH) + self.assertEqual(diff.size, len([patch for patch in diff])) def test_diff_oids(self): commit_a = self.repo[COMMIT_SHA1_1] From 50744352d72c5810e0d22dfd00ae2c85a4b79598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 31 Oct 2013 09:24:00 +0100 Subject: [PATCH 0580/2237] Rename Patch.binary to Patch.is_binary for consistency --- src/diff.c | 6 +++--- test/test_diff.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/diff.c b/src/diff.c index 2ed1160c2..a539000ee 100644 --- a/src/diff.c +++ b/src/diff.c @@ -161,10 +161,10 @@ PyMemberDef Patch_members[] = { {NULL} }; -PyDoc_STRVAR(Patch_binary__doc__, "Binary."); +PyDoc_STRVAR(Patch_is_binary__doc__, "True if binary data, False if not."); PyObject * -Patch_binary__get__(Patch *self) +Patch_is_binary__get__(Patch *self) { if (!(self->flags & GIT_DIFF_FLAG_NOT_BINARY) && (self->flags & GIT_DIFF_FLAG_BINARY)) @@ -173,7 +173,7 @@ Patch_binary__get__(Patch *self) } PyGetSetDef Patch_getseters[] = { - GETTER(Patch, binary), + GETTER(Patch, is_binary), {NULL} }; diff --git a/test/test_diff.py b/test/test_diff.py index 0dcd22fec..3f762d8ed 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -175,7 +175,7 @@ def _test(diff): self.assertEqual(patch.old_file_path, 'a') self.assertEqual(patch.new_file_path, 'a') - self.assertEqual(patch.binary, False) + self.assertEqual(patch.is_binary, False) _test(commit_a.tree.diff_to_tree(commit_b.tree)) _test(self.repo.diff(COMMIT_SHA1_1, COMMIT_SHA1_2)) From c0b1ab9024ba67addfb83648ea5c14780a7330b5 Mon Sep 17 00:00:00 2001 From: Huang Huang Date: Fri, 1 Nov 2013 14:32:49 +0800 Subject: [PATCH 0581/2237] add len(diff) instead of diff.size --- src/diff.c | 11 +++++------ test/test_diff.py | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/diff.c b/src/diff.c index 0728d7de5..2f946e7db 100644 --- a/src/diff.c +++ b/src/diff.c @@ -249,11 +249,11 @@ PyTypeObject DiffIterType = { (iternextfunc) DiffIter_iternext, /* tp_iternext */ }; -PyDoc_STRVAR(Diff_size__doc__, "Returns the number of deltas/patches in this diff."); -PyObject * -Diff_size__get__(Diff *self) +Py_ssize_t +Diff_len(Diff *self) { - return PyLong_FromSize_t(git_diff_num_deltas(self->list)); + assert(self->list); + return (Py_ssize_t)git_diff_num_deltas(self->list); } PyDoc_STRVAR(Diff_patch__doc__, "Patch diff string."); @@ -446,12 +446,11 @@ Diff_dealloc(Diff *self) PyGetSetDef Diff_getseters[] = { GETTER(Diff, patch), - GETTER(Diff, size), {NULL} }; PyMappingMethods Diff_as_mapping = { - 0, /* mp_length */ + (lenfunc)Diff_len, /* mp_length */ (binaryfunc)Diff_getitem, /* mp_subscript */ 0, /* mp_ass_subscript */ }; diff --git a/test/test_diff.py b/test/test_diff.py index a2564c0cd..a42fdf396 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -254,7 +254,7 @@ def test_diff_patch(self): diff = commit_a.tree.diff_to_tree(commit_b.tree) self.assertEqual(diff.patch, PATCH) - self.assertEqual(diff.size, len([patch for patch in diff])) + self.assertEqual(len(diff), len([patch for patch in diff])) def test_diff_oids(self): commit_a = self.repo[COMMIT_SHA1_1] From 3ad89b3f624f140f03b99aef6baf6ef97bde1571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 1 Nov 2013 10:33:53 +0100 Subject: [PATCH 0582/2237] Rename Blob.binary to Blob.is_binary for consistency --- src/blob.c | 6 +++--- test/test_blob.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/blob.c b/src/blob.c index 1d41a2283..83c7ece55 100644 --- a/src/blob.c +++ b/src/blob.c @@ -41,10 +41,10 @@ Blob_size__get__(Blob *self) } -PyDoc_STRVAR(Blob_binary__doc__, "Binary."); +PyDoc_STRVAR(Blob_is_binary__doc__, "True if binary data, False if not."); PyObject * -Blob_binary__get__(Blob *self) +Blob_is_binary__get__(Blob *self) { if (git_blob_is_binary(self->blob)) Py_RETURN_TRUE; @@ -58,7 +58,7 @@ PyDoc_STRVAR(Blob_data__doc__, PyGetSetDef Blob_getseters[] = { GETTER(Blob, size), - GETTER(Blob, binary), + GETTER(Blob, is_binary), {"data", (getter)Object_read_raw, NULL, Blob_data__doc__, NULL}, {NULL} }; diff --git a/test/test_blob.py b/test/test_blob.py index d1a6897e9..4e7a6cdc9 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -53,7 +53,7 @@ def test_read_blob(self): sha = blob.oid.hex self.assertEqual(sha, BLOB_SHA) self.assertTrue(isinstance(blob, pygit2.Blob)) - self.assertFalse(blob.binary) + self.assertFalse(blob.is_binary) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) self.assertEqual(BLOB_CONTENT, blob.data) self.assertEqual(len(BLOB_CONTENT), blob.size) From ceac655eabfe556a95bb36afca103c6d8adb91a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 2 Nov 2013 01:33:46 +0100 Subject: [PATCH 0583/2237] status: don't use the callback version This allows us more straightforward code as well as returning NULL on error with much less hassle than it would take with the callback version. --- src/repository.c | 56 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/repository.c b/src/repository.c index 935c1665f..4bfa300e5 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1096,33 +1096,53 @@ PyDoc_STRVAR(Repository_status__doc__, "Reads the status of the repository and returns a dictionary with file\n" "paths as keys and status flags as values. See pygit2.GIT_STATUS_*."); -int -read_status_cb(const char *path, unsigned int status_flags, void *payload) +PyObject * +Repository_status(Repository *self, PyObject *args) { - /* This is the callback that will be called in git_status_foreach. It - * will be called for every path.*/ - PyObject *flags; + PyObject *dict; int err; + size_t len, i; + git_status_list *list; - flags = PyLong_FromLong((long) status_flags); - err = PyDict_SetItemString(payload, path, flags); - Py_CLEAR(flags); + dict = PyDict_New(); + if (dict == NULL) + return NULL; + err = git_status_list_new(&list, self->repo, NULL); if (err < 0) - return GIT_ERROR; + return Error_set(err); - return GIT_OK; -} + len = git_status_list_entrycount(list); + for (i = 0; i < len; i++) { + const git_status_entry *entry; + const char *path; + PyObject *status; -PyObject * -Repository_status(Repository *self, PyObject *args) -{ - PyObject *payload_dict; + entry = git_status_byindex(list, i); + if (entry == NULL) + goto on_error; + + /* We need to choose one of the strings */ + path = entry->head_to_index ? + entry->head_to_index->old_file.path : + entry->index_to_workdir->old_file.path; + status = PyLong_FromLong((long) entry->status); + + err = PyDict_SetItemString(dict, path, status); + Py_CLEAR(status); + + if (err < 0) + goto on_error; + + } - payload_dict = PyDict_New(); - git_status_foreach(self->repo, read_status_cb, payload_dict); + git_status_list_free(list); + return dict; - return payload_dict; + on_error: + git_status_list_free(list); + Py_CLEAR(dict); + return NULL; } From 9da4d4fb9f766c110a6df6e0098053d47da8092e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 2 Nov 2013 15:45:55 +0100 Subject: [PATCH 0584/2237] reference: add a shorthand property Add Reference.shorthand to make it simpler to present a human-readable name for a particular reference. --- src/reference.c | 9 +++++++++ test/test_refs.py | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/src/reference.c b/src/reference.c index 4d11cb5c5..1df1fe9c3 100644 --- a/src/reference.c +++ b/src/reference.c @@ -276,6 +276,14 @@ Reference_name__get__(Reference *self) return to_path(git_reference_name(self->reference)); } +PyDoc_STRVAR(Reference_shorthand__doc__, "The shorthand \"human-readable\" name of the reference."); + +PyObject * +Reference_shorthand__get__(Reference *self) +{ + CHECK_REFERENCE(self); + return to_path(git_reference_shorthand(self->reference)); +} PyDoc_STRVAR(Reference_type__doc__, "Type, either GIT_REF_OID or GIT_REF_SYMBOLIC."); @@ -432,6 +440,7 @@ PyMethodDef Reference_methods[] = { PyGetSetDef Reference_getseters[] = { GETTER(Reference, name), + GETTER(Reference, shorthand), GETSET(Reference, target), GETTER(Reference, type), {NULL} diff --git a/test/test_refs.py b/test/test_refs.py index 461218527..347d38114 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -103,6 +103,11 @@ def test_set_target(self): reference.target = 'refs/heads/i18n' self.assertEqual(reference.target, 'refs/heads/i18n') + def test_get_shorthand(self): + reference = self.repo.lookup_reference('refs/heads/master') + self.assertEqual(reference.shorthand, 'master') + reference = self.repo.create_reference('refs/remotes/origin/master', LAST_COMMIT) + self.assertEqual(reference.shorthand, 'origin/master') def test_delete(self): repo = self.repo From e345d23501fda235ce94620e0b001ac97870d0fd Mon Sep 17 00:00:00 2001 From: XTao Date: Wed, 30 Oct 2013 15:14:15 +0800 Subject: [PATCH 0585/2237] Add append_log to reference. --- src/oid.c | 2 +- src/reference.c | 68 +++++++++++++++++++++++++++++++++++++++++++++ test/test_reflog.py | 44 +++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 test/test_reflog.py diff --git a/src/oid.c b/src/oid.c index d1db630ef..40a5d79e4 100644 --- a/src/oid.c +++ b/src/oid.c @@ -46,7 +46,7 @@ git_oid_to_python(const git_oid *oid) } size_t -py_hex_to_git_oid (PyObject *py_oid, git_oid *oid) +py_hex_to_git_oid(PyObject *py_oid, git_oid *oid) { PyObject *py_hex; int err; diff --git a/src/reference.c b/src/reference.c index 4d11cb5c5..e9abc6c6c 100644 --- a/src/reference.c +++ b/src/reference.c @@ -40,6 +40,7 @@ extern PyObject *GitError; extern PyTypeObject RefLogEntryType; +extern PyTypeObject SignatureType; void RefLogIter_dealloc(RefLogIter *self) @@ -312,6 +313,72 @@ Reference_log(Reference *self) return (PyObject*)iter; } +PyDoc_STRVAR(Reference_log_append__doc__, + "log_append(committer, message, oid)\n" + "\n" + "Append reflog to the current reference."); + +PyObject * +Reference_log_append(Reference *self, PyObject *args, PyObject *kwds) +{ + git_signature *committer; + const char *message = NULL; + git_reflog *reflog; + git_oid oid; + git_oid *ref_oid; + int err; + Signature *py_committer; + PyObject *py_message = NULL; + PyObject *py_hex = NULL; + char *keywords[] = {"committer", "message", "oid", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OO", keywords, + &SignatureType, &py_committer, + &py_message, + &py_hex)) + return NULL; + + /* FIXME: encoding */ + if (py_message != NULL) { + message = py_str_to_c_str(py_message, NULL); + if (message == NULL) + return NULL; + } + + if (py_hex != NULL) { + err = py_oid_to_git_oid_expand(self->repo->repo, py_hex, &oid); + if (err < 0) + return NULL; + } + + CHECK_REFERENCE(self); + + err = git_reflog_read(&reflog, self->reference); + if (err < 0) { + free((void *)message); + return NULL; + } + + if (py_hex != NULL) + ref_oid = &oid; + else + ref_oid = git_reference_target(self->reference); + + committer = (git_signature *)py_committer->signature; + if (!(err = git_reflog_append(reflog, + ref_oid, + committer, + message))) + err = git_reflog_write(reflog); + + git_reflog_free(reflog); + free((void *)message); + + if (err < 0) + return NULL; + + Py_RETURN_NONE; +} PyDoc_STRVAR(Reference_get_object__doc__, "get_object() -> object\n" @@ -426,6 +493,7 @@ PyMethodDef Reference_methods[] = { METHOD(Reference, rename, METH_O), METHOD(Reference, resolve, METH_NOARGS), METHOD(Reference, log, METH_NOARGS), + METHOD(Reference, log_append, METH_VARARGS|METH_KEYWORDS), METHOD(Reference, get_object, METH_NOARGS), {NULL} }; diff --git a/test/test_reflog.py b/test/test_reflog.py new file mode 100644 index 000000000..af68f5b89 --- /dev/null +++ b/test/test_reflog.py @@ -0,0 +1,44 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2010-2013 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. + +"""Tests for reference log.""" + +from __future__ import absolute_import +from __future__ import unicode_literals + +from pygit2 import Signature +from . import utils + + +class ReflogTest(utils.RepoTestCase): + + def test_log_append(self): + repo = self.repo + master = repo.lookup_reference("refs/heads/master") + signature = Signature('xtao', 'xutao@douban.com') + master.log_append(signature, 'reflog') + self.assertTrue('reflog' in [entry.message for entry in master.log()]) From 629e31827573d127d0773b04eef116c081694029 Mon Sep 17 00:00:00 2001 From: Petr Hosek Date: Mon, 4 Nov 2013 14:58:08 +0000 Subject: [PATCH 0586/2237] Add Blame support --- src/blame.c | 384 +++++++++++++++++++++++++++++++++++++++++++++ src/blame.h | 38 +++++ src/pygit2.c | 15 ++ src/repository.c | 67 ++++++++ src/repository.h | 2 + src/types.h | 24 +++ test/test_blame.py | 111 +++++++++++++ 7 files changed, 641 insertions(+) create mode 100644 src/blame.c create mode 100644 src/blame.h create mode 100644 test/test_blame.py diff --git a/src/blame.c b/src/blame.c new file mode 100644 index 000000000..d5545bf83 --- /dev/null +++ b/src/blame.c @@ -0,0 +1,384 @@ +/* + * Copyright 2010-2013 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 "error.h" +#include "types.h" +#include "utils.h" +#include "signature.h" +#include "blame.h" + +extern PyObject *GitError; + +extern PyTypeObject BlameType; +extern PyTypeObject BlameIterType; +extern PyTypeObject BlameHunkType; + +PyObject* +wrap_blame(git_blame *blame, Repository *repo) +{ + Blame *py_blame; + + py_blame = PyObject_New(Blame, &BlameType); + if (py_blame) { + Py_INCREF(repo); + py_blame->repo = repo; + py_blame->blame = blame; + } + + return (PyObject*) py_blame; +} + +#include +PyObject* +wrap_blame_hunk(const git_blame_hunk *hunk, Blame *blame) +{ + BlameHunk *py_hunk = NULL; + + if (!hunk) + Py_RETURN_NONE; + + py_hunk = PyObject_New(BlameHunk, &BlameHunkType); + if (py_hunk != NULL) { + py_hunk->lines_in_hunk = hunk->lines_in_hunk; + py_hunk->final_commit_id = git_oid_allocfmt(&hunk->final_commit_id); + py_hunk->final_start_line_number = hunk->final_start_line_number; + py_hunk->final_signature = hunk->final_signature != NULL ? + git_signature_dup(hunk->final_signature) : NULL; + py_hunk->orig_commit_id = git_oid_allocfmt(&hunk->orig_commit_id); + py_hunk->orig_path = hunk->orig_path != NULL ? + strdup(hunk->orig_path) : NULL; + py_hunk->orig_start_line_number = hunk->orig_start_line_number; + py_hunk->orig_signature = hunk->orig_signature != NULL ? + git_signature_dup(hunk->orig_signature) : NULL; + py_hunk->boundary = hunk->boundary; + } + + return (PyObject*) py_hunk; +} + +PyDoc_STRVAR(BlameHunk_final_committer__doc__, "Final committer."); + +PyObject * +BlameHunk_final_committer__get__(BlameHunk *self) +{ + if (!self->final_signature) + Py_RETURN_NONE; + + return build_signature((Object*) self, self->final_signature, "utf-8"); +} + +PyDoc_STRVAR(BlameHunk_orig_committer__doc__, "Origin committer."); + +PyObject * +BlameHunk_orig_committer__get__(BlameHunk *self) +{ + if (!self->orig_signature) + Py_RETURN_NONE; + + return build_signature((Object*) self, self->orig_signature, "utf-8"); +} + +static int +BlameHunk_init(BlameHunk *self, PyObject *args, PyObject *kwds) +{ + self->final_commit_id = NULL; + self->final_signature = NULL; + self->orig_commit_id = NULL; + self->orig_path = NULL; + self->orig_signature = NULL; + + return 0; +} + +static void +BlameHunk_dealloc(BlameHunk *self) +{ + free(self->final_commit_id); + if (self->final_signature) + git_signature_free(self->final_signature); + free(self->orig_commit_id); + if (self->orig_path) + free(self->orig_path); + if (self->orig_signature) + git_signature_free(self->orig_signature); + PyObject_Del(self); +} + +PyMemberDef BlameHunk_members[] = { + MEMBER(BlameHunk, lines_in_hunk, T_UINT, "Number of lines."), + MEMBER(BlameHunk, final_commit_id, T_STRING, "Last changed oid."), + MEMBER(BlameHunk, final_start_line_number, T_UINT, "final start line no."), + MEMBER(BlameHunk, orig_commit_id, T_STRING, "oid where hunk was found."), + MEMBER(BlameHunk, orig_path, T_STRING, "Origin path."), + MEMBER(BlameHunk, orig_start_line_number, T_UINT, "Origin start line no."), + MEMBER(BlameHunk, boundary, T_BOOL, "Tracked to a boundary commit."), + {NULL} +}; + +PyGetSetDef BlameHunk_getseters[] = { + GETTER(BlameHunk, final_committer), + GETTER(BlameHunk, orig_committer), + {NULL} +}; + +PyDoc_STRVAR(BlameHunk__doc__, "Blame Hunk object."); + +PyTypeObject BlameHunkType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.BlameHunk", /* tp_name */ + sizeof(BlameHunk), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)BlameHunk_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + BlameHunk__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + BlameHunk_members, /* tp_members */ + BlameHunk_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)BlameHunk_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +PyObject * +BlameIter_iternext(BlameIter *self) +{ + if (self->i < self->n) + return wrap_blame_hunk(git_blame_get_hunk_byindex( + self->blame->blame, self->i++), self->blame); + + PyErr_SetNone(PyExc_StopIteration); + return NULL; +} + +static void +BlameIter_dealloc(BlameIter *self) +{ + Py_CLEAR(self->blame); + PyObject_Del(self); +} + + +PyDoc_STRVAR(BlameIter__doc__, "Blame iterator object."); + +PyTypeObject BlameIterType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.BlameIter", /* tp_name */ + sizeof(BlameIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)BlameIter_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + BlameIter__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc) BlameIter_iternext, /* tp_iternext */ +}; + + +PyObject * +Blame_iter(Blame *self) +{ + BlameIter *iter; + + iter = PyObject_New(BlameIter, &BlameIterType); + if (iter != NULL) { + Py_INCREF(self); + iter->blame = self; + iter->i = 0; + iter->n = git_blame_get_hunk_count(self->blame); + } + return (PyObject*)iter; +} + +Py_ssize_t +Blame_len(Blame *self) +{ + assert(self->blame); + return (Py_ssize_t)git_blame_get_hunk_count(self->blame); +} + +PyObject * +Blame_getitem(Blame *self, PyObject *value) +{ + size_t i; + const git_blame_hunk *hunk; + + if (PyLong_Check(value) < 0) { + PyErr_SetObject(PyExc_IndexError, value); + return NULL; + } + + i = PyLong_AsUnsignedLong(value); + if (PyErr_Occurred()) { + PyErr_SetObject(PyExc_IndexError, value); + return NULL; + } + + hunk = git_blame_get_hunk_byindex(self->blame, i); + if (!hunk) { + PyErr_SetObject(PyExc_IndexError, value); + return NULL; + } + + return wrap_blame_hunk(hunk, self); +} + +PyDoc_STRVAR(Blame_for_line__doc__, + "for_line(line_no) -> hunk\n" + "\n" + "Returns the blame hunk data for the given \"line_no\" in blame.\n" + "\n" + "Arguments:\n" + "\n" + "line_no\n" + " Line number, countings starts with 1."); + +PyObject * +Blame_for_line(Blame *self, PyObject *args) +{ + size_t line_no; + const git_blame_hunk *hunk; + + if (!PyArg_ParseTuple(args, "I", &line_no)) + return NULL; + + hunk = git_blame_get_hunk_byline(self->blame, line_no); + if (!hunk) { + PyErr_SetObject(PyExc_IndexError, args); + return NULL; + } + + return wrap_blame_hunk(hunk, self); +} + +static void +Blame_dealloc(Blame *self) +{ + git_blame_free(self->blame); + Py_CLEAR(self->repo); + PyObject_Del(self); +} + +PyMappingMethods Blame_as_mapping = { + (lenfunc)Blame_len, /* mp_length */ + (binaryfunc)Blame_getitem, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + +static PyMethodDef Blame_methods[] = { + METHOD(Blame, for_line, METH_VARARGS), + {NULL} +}; + + +PyDoc_STRVAR(Blame__doc__, "Blame objects."); + +PyTypeObject BlameType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.Blame", /* tp_name */ + sizeof(Blame), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Blame_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 */ + &Blame_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Blame__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)Blame_iter, /* tp_iter */ + 0, /* tp_iternext */ + Blame_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* 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/blame.h b/src/blame.h new file mode 100644 index 000000000..a6f1e53e4 --- /dev/null +++ b/src/blame.h @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2013 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. + */ + +#ifndef INCLUDE_pygit2_blame_h +#define INCLUDE_pygit2_blame_h + +#define PY_SSIZE_T_CLEAN +#include +#include +#include "types.h" + +PyObject* wrap_blame(git_blame *blame, Repository *repo); + +#endif diff --git a/src/pygit2.c b/src/pygit2.c index 20fd27a1d..cf24120b0 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -64,6 +64,9 @@ extern PyTypeObject SignatureType; extern PyTypeObject RemoteType; extern PyTypeObject NoteType; extern PyTypeObject NoteIterType; +extern PyTypeObject BlameType; +extern PyTypeObject BlameIterType; +extern PyTypeObject BlameHunkType; @@ -418,6 +421,18 @@ moduleinit(PyObject* m) INIT_TYPE(RemoteType, NULL, NULL) ADD_TYPE(m, Remote) + /* Blame */ + INIT_TYPE(BlameType, NULL, NULL) + INIT_TYPE(BlameIterType, NULL, NULL) + INIT_TYPE(BlameHunkType, NULL, NULL) + ADD_TYPE(m, Blame) + ADD_TYPE(m, BlameHunk) + ADD_CONSTANT_INT(m, GIT_BLAME_NORMAL) + ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_SAME_FILE) + ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES) + ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES) + ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES) + /* Global initialization of libgit2 */ git_threads_init(); diff --git a/src/repository.c b/src/repository.c index facc0ce2a..88f34a1e6 100644 --- a/src/repository.c +++ b/src/repository.c @@ -37,6 +37,7 @@ #include "repository.h" #include "remote.h" #include "branch.h" +#include "blame.h" #include extern PyObject *GitError; @@ -1443,6 +1444,71 @@ Repository_lookup_note(Repository *self, PyObject* args) return (PyObject*) wrap_note(self, &annotated_id, ref); } +PyDoc_STRVAR(Repository_blame__doc__, + "blame(path, [flags, min_match_characters, newest_commit, oldest_commit,\n" + " min_line, max_line]) -> blame\n" + "\n" + "Get the blame for a single file.\n" + "\n" + "Arguments:\n" + "\n" + "path\n" + " A path to file to consider.\n" + "flags\n" + " A GIT_BLAME_* constant.\n" + "min_match_characters\n" + " The number of alphanum chars that must be detected as moving/copying\n" + " within a file for it to associate those lines with the parent commit.\n" + "newest_commit\n" + " The id of the newest commit to consider.\n" + "oldest_commit\n" + " The id of the oldest commit to consider.\n" + "min_line\n" + " The first line in the file to blame.\n" + "max_line\n" + " The last line in the file to blame.\n" + "\n" + "Examples::\n" + "\n" + " repo.blame('foo.c', flags=GIT_BLAME_TRACK_COPIES_SAME_FILE)"); + +PyObject* Repository_blame(Repository *self, PyObject *args, PyObject *kwds) +{ + git_blame_options opts = GIT_BLAME_OPTIONS_INIT; + git_blame *blame; + char *path; + PyObject *value1 = NULL; + PyObject *value2 = NULL; + int err; + char *keywords[] = {"flags", "min_match_characters", "newest_commit", + "oldest_commit", "min_line", "max_line", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|IHOOII", keywords, + &path, &opts.flags, + &opts.min_match_characters, + &value1, &value2, + &opts.min_line, &opts.max_line)) + return NULL; + + if (value1) { + err = py_oid_to_git_oid_expand(self->repo, value1, &opts.newest_commit); + if (err < 0) + return NULL; + } + if (value2) { + err = py_oid_to_git_oid_expand(self->repo, value2, &opts.oldest_commit); + if (err < 0) + return NULL; + } + + err = git_blame_file(&blame, self->repo, path, NULL); + if (err < 0) + return Error_set(err); + + return wrap_blame(blame, self); +} + + PyMethodDef Repository_methods[] = { METHOD(Repository, create_blob, METH_VARARGS), METHOD(Repository, create_blob_fromworkdir, METH_VARARGS), @@ -1472,6 +1538,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, lookup_branch, METH_VARARGS), METHOD(Repository, listall_branches, METH_VARARGS), METHOD(Repository, create_branch, METH_VARARGS), + METHOD(Repository, blame, METH_VARARGS | METH_KEYWORDS), {NULL} }; diff --git a/src/repository.h b/src/repository.h index fd9c524b7..3c6094872 100644 --- a/src/repository.h +++ b/src/repository.h @@ -67,4 +67,6 @@ PyObject* Repository_status(Repository *self, PyObject *args); PyObject* Repository_status_file(Repository *self, PyObject *value); PyObject* Repository_TreeBuilder(Repository *self, PyObject *args); +PyObject* Repository_blame(Repository *self, PyObject *args, PyObject *kwds); + #endif diff --git a/src/types.h b/src/types.h index 6c2136d25..04782ebf0 100644 --- a/src/types.h +++ b/src/types.h @@ -194,4 +194,28 @@ typedef struct { SIMPLE_TYPE(Remote, git_remote, remote) +/* git_blame */ +SIMPLE_TYPE(Blame, git_blame, blame) + +typedef struct { + PyObject_HEAD + Blame* blame; + size_t i; + size_t n; +} BlameIter; + +typedef struct { + PyObject_HEAD + unsigned lines_in_hunk; + char* final_commit_id; + unsigned final_start_line_number; + git_signature* final_signature; + char* orig_commit_id; + char* orig_path; + unsigned orig_start_line_number; + git_signature* orig_signature; + char boundary; +} BlameHunk; + + #endif diff --git a/test/test_blame.py b/test/test_blame.py new file mode 100644 index 000000000..909e7c4cf --- /dev/null +++ b/test/test_blame.py @@ -0,0 +1,111 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2010-2013 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. + +"""Tests for Blame objects.""" + +from __future__ import absolute_import +from __future__ import unicode_literals +import unittest +import pygit2 +from pygit2 import Signature +from pygit2 import GIT_DIFF_INCLUDE_UNMODIFIED +from pygit2 import GIT_DIFF_IGNORE_WHITESPACE, GIT_DIFF_IGNORE_WHITESPACE_EOL +from . import utils +from itertools import chain +from datetime import datetime + +PATH = 'hello.txt' + +HUNKS = [ + ('acecd5ea2924a4b900e7e149496e1f4b57976e51', 1, + Signature('J. David Ibañez', 'jdavid@itaapy.com', + 1297179898, 60, encoding='utf-8'), True), + ('6aaa262e655dd54252e5813c8e5acd7780ed097d', 2, + Signature('J. David Ibañez', 'jdavid@itaapy.com', + 1297696877, 60, encoding='utf-8'), False), + ('4ec4389a8068641da2d6578db0419484972284c8', 3, + Signature('J. David Ibañez', 'jdavid@itaapy.com', + 1297696908, 60, encoding='utf-8'), False) +] + +class BlameTest(utils.RepoTestCase): + + def test_blame_index(self): + repo = self.repo + blame = repo.blame(PATH) + + self.assertEqual(len(blame), 3) + + for i, hunk in enumerate(blame): + self.assertEqual(hunk.lines_in_hunk, 1) + self.assertEqual(HUNKS[i][0], hunk.final_commit_id) + self.assertEqual(HUNKS[i][1], hunk.final_start_line_number) + self.assertEqualSignature(HUNKS[i][2], hunk.final_committer) + self.assertEqual(hunk.orig_commit_id, + '0000000000000000000000000000000000000000') + self.assertEqual(hunk.orig_path, PATH) + self.assertEqual(HUNKS[i][1], hunk.orig_start_line_number) + self.assertIsNone(hunk.orig_committer) + self.assertEqual(HUNKS[i][3], hunk.boundary) + + def test_blame_with_invalid_index(self): + repo = self.repo + blame = repo.blame(PATH) + + with self.assertRaises(IndexError): + blame[100000] + blame[-1] + + def test_blame_for_line(self): + repo = self.repo + blame = repo.blame(PATH) + + for i, line in zip(range(0, 2), range(1, 3)): + hunk = blame.for_line(line) + + self.assertEqual(hunk.lines_in_hunk, 1) + self.assertEqual(HUNKS[i][0], hunk.final_commit_id) + self.assertEqual(HUNKS[i][1], hunk.final_start_line_number) + self.assertEqualSignature(HUNKS[i][2], hunk.final_committer) + self.assertEqual(hunk.orig_commit_id, + '0000000000000000000000000000000000000000') + self.assertEqual(hunk.orig_path, PATH) + self.assertEqual(HUNKS[i][1], hunk.orig_start_line_number) + self.assertIsNone(hunk.orig_committer) + self.assertEqual(HUNKS[i][3], hunk.boundary) + + def test_blame_with_invalid_line(self): + repo = self.repo + blame = repo.blame(PATH) + + with self.assertRaises(IndexError): + blame.for_line(0) + blame.for_line(100000) + blame.for_line(-1) + +if __name__ == '__main__': + unittest.main() From c267891d1f6a15e0b4225fb539c9d44d27900437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 13 Nov 2013 11:18:20 +0100 Subject: [PATCH 0587/2237] Make Reference.log_append signature more consistent Consistent with the rest of the pygit2 API. --- src/reference.c | 58 ++++++++++++++++++++++----------------------- test/test_reflog.py | 2 +- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/reference.c b/src/reference.c index ea35d8e27..62ebbc671 100644 --- a/src/reference.c +++ b/src/reference.c @@ -277,7 +277,8 @@ Reference_name__get__(Reference *self) return to_path(git_reference_name(self->reference)); } -PyDoc_STRVAR(Reference_shorthand__doc__, "The shorthand \"human-readable\" name of the reference."); +PyDoc_STRVAR(Reference_shorthand__doc__, + "The shorthand \"human-readable\" name of the reference."); PyObject * Reference_shorthand__get__(Reference *self) @@ -322,61 +323,58 @@ Reference_log(Reference *self) } PyDoc_STRVAR(Reference_log_append__doc__, - "log_append(committer, message, oid)\n" + "log_append(oid, committer, message[, encoding])\n" "\n" - "Append reflog to the current reference."); + "Append a reflog entry to the reference. If the oid is None then keep\n" + "the current reference's oid. The message parameter may be None."); PyObject * -Reference_log_append(Reference *self, PyObject *args, PyObject *kwds) +Reference_log_append(Reference *self, PyObject *args) { git_signature *committer; const char *message = NULL; git_reflog *reflog; git_oid oid; - git_oid *ref_oid; + const git_oid *ref_oid; int err; + PyObject *py_oid = NULL; Signature *py_committer; PyObject *py_message = NULL; - PyObject *py_hex = NULL; - char *keywords[] = {"committer", "message", "oid", NULL}; + char *encoding = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OO", keywords, - &SignatureType, &py_committer, - &py_message, - &py_hex)) + CHECK_REFERENCE(self); + + /* Input parameters */ + if (!PyArg_ParseTuple(args, "OO!O|s", &py_oid, + &SignatureType, &py_committer, + &py_message, &encoding)) return NULL; - /* FIXME: encoding */ - if (py_message != NULL) { - message = py_str_to_c_str(py_message, NULL); - if (message == NULL) + if (py_oid == Py_None) + ref_oid = git_reference_target(self->reference); + else { + err = py_oid_to_git_oid_expand(self->repo->repo, py_oid, &oid); + if (err < 0) return NULL; + ref_oid = &oid; } - if (py_hex != NULL) { - err = py_oid_to_git_oid_expand(self->repo->repo, py_hex, &oid); - if (err < 0) + if (py_message != Py_None) { + message = py_str_to_c_str(py_message, encoding); + if (message == NULL) return NULL; } - CHECK_REFERENCE(self); - + /* Go */ err = git_reflog_read(&reflog, self->reference); if (err < 0) { free((void *)message); return NULL; } - if (py_hex != NULL) - ref_oid = &oid; - else - ref_oid = git_reference_target(self->reference); - committer = (git_signature *)py_committer->signature; - if (!(err = git_reflog_append(reflog, - ref_oid, - committer, - message))) + err = git_reflog_append(reflog, ref_oid, committer, message); + if (!err) err = git_reflog_write(reflog); git_reflog_free(reflog); @@ -501,7 +499,7 @@ PyMethodDef Reference_methods[] = { METHOD(Reference, rename, METH_O), METHOD(Reference, resolve, METH_NOARGS), METHOD(Reference, log, METH_NOARGS), - METHOD(Reference, log_append, METH_VARARGS|METH_KEYWORDS), + METHOD(Reference, log_append, METH_VARARGS), METHOD(Reference, get_object, METH_NOARGS), {NULL} }; diff --git a/test/test_reflog.py b/test/test_reflog.py index af68f5b89..84473c8ee 100644 --- a/test/test_reflog.py +++ b/test/test_reflog.py @@ -40,5 +40,5 @@ def test_log_append(self): repo = self.repo master = repo.lookup_reference("refs/heads/master") signature = Signature('xtao', 'xutao@douban.com') - master.log_append(signature, 'reflog') + master.log_append(None, signature, 'reflog') self.assertTrue('reflog' in [entry.message for entry in master.log()]) From b7e906eee9d503fd6dee55fee5c7e7bc8e28978d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 30 Oct 2013 08:57:17 +0100 Subject: [PATCH 0588/2237] Adjust to libgit2 development branch This wraps the previous functionality, though there are some iterator changes we might still want to bring over. --- pygit2/__init__.py | 17 +++---------- src/config.c | 55 +++++++++++++++++------------------------ src/diff.c | 54 +++++++++++++++++++--------------------- src/diff.h | 2 +- src/index.c | 4 +-- src/pygit2.c | 29 ++++++---------------- src/reference.c | 12 +++++++-- src/remote.c | 2 +- src/repository.c | 16 ++++++------ src/tree.c | 6 ++--- src/types.h | 2 +- test/test_repository.py | 4 +-- 12 files changed, 87 insertions(+), 116 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index e59d3c31d..e2d52467c 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -49,9 +49,8 @@ def init_repository(path, bare=False): def clone_repository( - url, path, bare=False, remote_name="origin", - push_url=None, fetch_spec=None, - push_spec=None, checkout_branch=None): + url, path, bare=False, ignore_cert_errors=False, + remote_name="origin", checkout_branch=None): """ Clones a new Git repository from *url* in the given *path*. @@ -60,15 +59,6 @@ def clone_repository( **remote_name** is the name given to the "origin" remote. The default is "origin". - **push_url** is a URL to be used for pushing. - None means use the *url* parameter. - - **fetch_spec** defines the the default fetch spec. - None results in the same behavior as *GIT_REMOTE_DEFAULT_FETCH*. - - **push_spec** is the fetch specification to be used for pushing. - None means use the same spec as for *fetch_spec*. - **checkout_branch** gives the name of the branch to checkout. None means use the remote's *HEAD*. @@ -83,6 +73,5 @@ def clone_repository( """ _pygit2.clone_repository( - url, path, bare, remote_name, push_url, - fetch_spec, push_spec, checkout_branch) + url, path, bare, ignore_cert_errors, remote_name, checkout_branch) return Repository(path) diff --git a/src/config.c b/src/config.c index dc9b44f53..b1e7afdc1 100644 --- a/src/config.c +++ b/src/config.c @@ -336,55 +336,44 @@ PyDoc_STRVAR(Config_get_multivar__doc__, "parameter is expected to be a regular expression to filter the variables\n" "we're interested in."); -int -Config_get_multivar_fn_wrapper(const git_config_entry *value, void *data) -{ - PyObject *item; - - item = to_unicode(value->value, NULL, NULL); - if (item == NULL) - /* FIXME Right now there is no way to forward errors through the - * libgit2 API, open an issue or pull-request to libgit2. - * - * See libgit2/src/config_file.c:443 (config_get_multivar). - * Comment says "early termination by the user is not an error". - * That's wrong. - */ - return -2; - - PyList_Append((PyObject *)data, item); - Py_CLEAR(item); - return 0; -} - PyObject * Config_get_multivar(Config *self, PyObject *args) { int err; PyObject *list; - Py_ssize_t size; const char *name = NULL; const char *regex = NULL; + git_config_iterator *iter; + git_config_entry *entry; if (!PyArg_ParseTuple(args, "s|s", &name, ®ex)) return NULL; list = PyList_New(0); - err = git_config_get_multivar(self->config, name, regex, - Config_get_multivar_fn_wrapper, - (void *)list); + err = git_config_multivar_iterator_new(&iter, self->config, name, regex); + if (err < 0) + return Error_set(err); - if (err < 0) { - /* XXX The return value of git_config_get_multivar is not reliable, - * see https://github.com/libgit2/libgit2/pull/1712 - * Once libgit2 0.20 is released, we will remove this test. */ - if (err == GIT_ENOTFOUND && PyList_Size(list) != 0) - return list; + while ((err = git_config_next(&entry, iter)) == 0) { + PyObject *item; - Py_CLEAR(list); - return Error_set(err); + item = to_unicode(entry->value, NULL, NULL); + if (item == NULL) { + git_config_iterator_free(iter); + return NULL; + } + + PyList_Append(list, item); + Py_CLEAR(item); } + git_config_iterator_free(iter); + if (err == GIT_ITEROVER) + err = 0; + + if (err < 0) + return Error_set(err); + return list; } diff --git a/src/diff.c b/src/diff.c index 3b5c4fe52..01e6e7dbe 100644 --- a/src/diff.c +++ b/src/diff.c @@ -43,7 +43,7 @@ extern PyTypeObject HunkType; PyTypeObject PatchType; PyObject* -wrap_diff(git_diff_list *diff, Repository *repo) +wrap_diff(git_diff *diff, Repository *repo) { Diff *py_diff; @@ -58,23 +58,24 @@ wrap_diff(git_diff_list *diff, Repository *repo) } PyObject* -diff_get_patch_byindex(git_diff_list* list, size_t idx) +diff_get_patch_byindex(git_diff* diff, size_t idx) { const git_diff_delta* delta; - const git_diff_range* range; - git_diff_patch* patch = NULL; - size_t i, j, hunk_amounts, lines_in_hunk, line_len, header_len, additions, deletions; - const char* line, *header; - char line_origin; + const git_diff_hunk *hunk; + const git_diff_line *line; + git_patch* patch = NULL; + size_t i, j, hunk_amounts, lines_in_hunk, additions, deletions; int err; Hunk *py_hunk = NULL; Patch *py_patch = NULL; PyObject *py_line_origin=NULL, *py_line=NULL; - err = git_diff_get_patch(&patch, &delta, list, idx); - if (err < 0) + err = git_patch_from_diff(&patch, diff, idx); + if (err < 0) return Error_set(err); + delta = git_patch_get_delta(patch); + py_patch = PyObject_New(Patch, &PatchType); if (py_patch != NULL) { py_patch->old_file_path = delta->old_file.path; @@ -85,36 +86,34 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) py_patch->old_oid = git_oid_allocfmt(&delta->old_file.oid); py_patch->new_oid = git_oid_allocfmt(&delta->new_file.oid); - git_diff_patch_line_stats(NULL, &additions, &deletions, patch); + git_patch_line_stats(NULL, &additions, &deletions, patch); py_patch->additions = additions; py_patch->deletions = deletions; - hunk_amounts = git_diff_patch_num_hunks(patch); + hunk_amounts = git_patch_num_hunks(patch); py_patch->hunks = PyList_New(hunk_amounts); for (i=0; i < hunk_amounts; ++i) { - err = git_diff_patch_get_hunk(&range, &header, &header_len, - &lines_in_hunk, patch, i); + err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, i); if (err < 0) goto cleanup; py_hunk = PyObject_New(Hunk, &HunkType); if (py_hunk != NULL) { - py_hunk->old_start = range->old_start; - py_hunk->old_lines = range->old_lines; - py_hunk->new_start = range->new_start; - py_hunk->new_lines = range->new_lines; + py_hunk->old_start = hunk->old_start; + py_hunk->old_lines = hunk->old_lines; + py_hunk->new_start = hunk->new_start; + py_hunk->new_lines = hunk->new_lines; py_hunk->lines = PyList_New(lines_in_hunk); for (j=0; j < lines_in_hunk; ++j) { - err = git_diff_patch_get_line_in_hunk(&line_origin, - &line, &line_len, NULL, NULL, patch, i, j); + err = git_patch_get_line_in_hunk(&line, patch, i, j); if (err < 0) goto cleanup; - py_line_origin = to_unicode_n(&line_origin, 1, NULL, NULL); - py_line = to_unicode_n(line, line_len, NULL, NULL); + py_line_origin = to_unicode_n(&line->origin, 1, NULL, NULL); + py_line = to_unicode_n(line->content, line->content_len, NULL, NULL); PyList_SetItem(py_hunk->lines, j, Py_BuildValue("OO", py_line_origin, @@ -132,7 +131,7 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx) } cleanup: - git_diff_patch_free(patch); + git_patch_free(patch); return (err < 0) ? Error_set(err) : (PyObject*) py_patch; } @@ -283,8 +282,7 @@ PyDoc_STRVAR(Diff_patch__doc__, "Patch diff string."); PyObject * Diff_patch__get__(Diff *self) { - const git_diff_delta* delta; - git_diff_patch* patch; + git_patch* patch; char **strings = NULL; char *buffer = NULL; int err = GIT_ERROR; @@ -295,16 +293,16 @@ Diff_patch__get__(Diff *self) MALLOC(strings, num * sizeof(char*), cleanup); for (i = 0, len = 1; i < num ; ++i) { - err = git_diff_get_patch(&patch, &delta, self->list, i); + err = git_patch_from_diff(&patch, self->list, i); if (err < 0) goto cleanup; - err = git_diff_patch_to_str(&(strings[i]), patch); + err = git_patch_to_str(&(strings[i]), patch); if (err < 0) goto cleanup; len += strlen(strings[i]); - git_diff_patch_free(patch); + git_patch_free(patch); } CALLOC(buffer, (len + 1), sizeof(char), cleanup); @@ -461,7 +459,7 @@ Diff_getitem(Diff *self, PyObject *value) static void Diff_dealloc(Diff *self) { - git_diff_list_free(self->list); + git_diff_free(self->list); Py_CLEAR(self->repo); PyObject_Del(self); } diff --git a/src/diff.h b/src/diff.h index 75e633334..f32c93090 100644 --- a/src/diff.h +++ b/src/diff.h @@ -41,6 +41,6 @@ PyObject* Diff_changes(Diff *self); PyObject* Diff_patch(Diff *self); -PyObject* wrap_diff(git_diff_list *diff, Repository *repo); +PyObject* wrap_diff(git_diff *diff, Repository *repo); #endif diff --git a/src/index.c b/src/index.c index 3c73f2178..bd7a76310 100644 --- a/src/index.c +++ b/src/index.c @@ -135,7 +135,7 @@ PyObject * Index_diff_to_workdir(Index *self, PyObject *args) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; - git_diff_list *diff; + git_diff *diff; int err; if (!PyArg_ParseTuple(args, "|IHH", &opts.flags, &opts.context_lines, @@ -177,7 +177,7 @@ Index_diff_to_tree(Index *self, PyObject *args) { Repository *py_repo; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; - git_diff_list *diff; + git_diff *diff; int err; Tree *py_tree = NULL; diff --git a/src/pygit2.c b/src/pygit2.c index 20fd27a1d..85c146f87 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -99,8 +99,7 @@ init_repository(PyObject *self, PyObject *args) { }; PyDoc_STRVAR(clone_repository__doc__, - "clone_repository(url, path, bare, remote_name, push_url," - "fetch_spec, push_spec, checkout_branch)\n" + "clone_repository(url, path, bare, remote_name, checkout_branch)\n" "\n" "Clones a Git repository in the given url to the given path " "with the specified options.\n" @@ -115,14 +114,6 @@ PyDoc_STRVAR(clone_repository__doc__, " If 'bare' is not 0, then a bare git repository will be created.\n" "remote_name\n" " The name given to the 'origin' remote. The default is 'origin'.\n" - "push_url\n" - " URL to be used for pushing.\n" - "fetch_spec\n" - " The fetch specification to be used for fetching. None results in " - "the same behavior as GIT_REMOTE_DEFAULT_FETCH.\n" - "push_spec\n" - " The fetch specification to be used for pushing. None means use the " - "same spec as for 'fetch_spec'\n" "checkout_branch\n" " The name of the branch to checkout. None means use the remote's " "HEAD.\n"); @@ -133,22 +124,18 @@ clone_repository(PyObject *self, PyObject *args) { git_repository *repo; const char *url; const char *path; - unsigned int bare; - const char *remote_name, *push_url, *fetch_spec; - const char *push_spec, *checkout_branch; + unsigned int bare, ignore_cert_errors; + const char *remote_name, *checkout_branch; int err; git_clone_options opts = GIT_CLONE_OPTIONS_INIT; - if (!PyArg_ParseTuple(args, "zzIzzzzz", - &url, &path, &bare, &remote_name, &push_url, - &fetch_spec, &push_spec, &checkout_branch)) + if (!PyArg_ParseTuple(args, "zzIIzz", + &url, &path, &bare, &ignore_cert_errors, &remote_name, &checkout_branch)) return NULL; opts.bare = bare; + opts.ignore_cert_errors = ignore_cert_errors; opts.remote_name = remote_name; - opts.pushurl = push_url; - opts.fetch_spec = fetch_spec; - opts.push_spec = push_spec; opts.checkout_branch = checkout_branch; err = git_clone(&repo, url, path, &opts); @@ -392,8 +379,8 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_UNTRACKED_DIRS) ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_UNTRACKED_DIRS) ADD_CONSTANT_INT(m, GIT_DIFF_DISABLE_PATHSPEC_MATCH) - ADD_CONSTANT_INT(m, GIT_DIFF_DELTAS_ARE_ICASE) - ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) + ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_CASE) + ADD_CONSTANT_INT(m, GIT_DIFF_SHOW_UNTRACKED_CONTENT) ADD_CONSTANT_INT(m, GIT_DIFF_SKIP_BINARY_CHECK) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) diff --git a/src/reference.c b/src/reference.c index 62ebbc671..70108af6b 100644 --- a/src/reference.c +++ b/src/reference.c @@ -309,13 +309,19 @@ PyDoc_STRVAR(Reference_log__doc__, PyObject * Reference_log(Reference *self) { + int err; RefLogIter *iter; + git_repository *repo; CHECK_REFERENCE(self); + repo = git_reference_owner(self->reference); iter = PyObject_New(RefLogIter, &RefLogIterType); if (iter != NULL) { - git_reflog_read(&iter->reflog, self->reference); + err = git_reflog_read(&iter->reflog, repo, git_reference_name(self->reference)); + if (err < 0) + return Error_set(err); + iter->size = git_reflog_entrycount(iter->reflog); iter->i = 0; } @@ -341,6 +347,7 @@ Reference_log_append(Reference *self, PyObject *args) Signature *py_committer; PyObject *py_message = NULL; char *encoding = NULL; + git_repository *repo; CHECK_REFERENCE(self); @@ -366,7 +373,8 @@ Reference_log_append(Reference *self, PyObject *args) } /* Go */ - err = git_reflog_read(&reflog, self->reference); + repo = git_reference_owner(self->reference); + err = git_reflog_read(&reflog, repo, git_reference_name(self->reference)); if (err < 0) { free((void *)message); return NULL; diff --git a/src/remote.c b/src/remote.c index e482d8b1c..6b684ecfc 100644 --- a/src/remote.c +++ b/src/remote.c @@ -179,7 +179,7 @@ Remote_fetch(Remote *self, PyObject *args) err = git_remote_connect(self->remote, GIT_DIRECTION_FETCH); if (err == GIT_OK) { - err = git_remote_download(self->remote, NULL, NULL); + err = git_remote_download(self->remote); if (err == GIT_OK) { stats = git_remote_stats(self->remote); py_stats = Py_BuildValue("{s:I,s:I,s:n}", diff --git a/src/repository.c b/src/repository.c index facc0ce2a..222fd2346 100644 --- a/src/repository.c +++ b/src/repository.c @@ -208,14 +208,14 @@ Repository_head_is_detached__get__(Repository *self) } -PyDoc_STRVAR(Repository_head_is_orphaned__doc__, - "An orphan branch is one named from HEAD but which doesn't exist in the\n" +PyDoc_STRVAR(Repository_head_is_unborn__doc__, + "An unborn branch is one named from HEAD but which doesn't exist in the\n" "refs namespace, because it doesn't have any commit to point to."); PyObject * -Repository_head_is_orphaned__get__(Repository *self) +Repository_head_is_unborn__get__(Repository *self) { - if (git_repository_head_orphan(self->repo) > 0) + if (git_repository_head_unborn(self->repo) > 0) Py_RETURN_TRUE; Py_RETURN_FALSE; @@ -430,9 +430,9 @@ Repository_write(Repository *self, PyObject *args) if (err < 0) return Error_set(err); - stream->write(stream, buffer, buflen); - err = stream->finalize_write(&oid, stream); - stream->free(stream); + git_odb_stream_write(stream, buffer, buflen); + err = git_odb_stream_finalize_write(&oid, stream); + git_odb_stream_free(stream); return git_oid_to_python(&oid); } @@ -1480,7 +1480,7 @@ PyGetSetDef Repository_getseters[] = { GETTER(Repository, path), GETSET(Repository, head), GETTER(Repository, head_is_detached), - GETTER(Repository, head_is_orphaned), + GETTER(Repository, head_is_unborn), GETTER(Repository, is_empty), GETTER(Repository, is_bare), GETTER(Repository, config), diff --git a/src/tree.c b/src/tree.c index e0abbbd57..7599d659b 100644 --- a/src/tree.c +++ b/src/tree.c @@ -290,7 +290,7 @@ PyObject * Tree_diff_to_workdir(Tree *self, PyObject *args) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; - git_diff_list *diff; + git_diff *diff; Repository *py_repo; int err; @@ -328,7 +328,7 @@ PyObject * Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; - git_diff_list *diff; + git_diff *diff; Repository *py_repo; int err; @@ -373,7 +373,7 @@ PyObject * Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; - git_diff_list *diff; + git_diff *diff; git_tree *from, *to, *tmp; Repository *py_repo; int err, swap = 0; diff --git a/src/types.h b/src/types.h index 6c2136d25..cbfa9aa62 100644 --- a/src/types.h +++ b/src/types.h @@ -95,7 +95,7 @@ typedef struct { /* git _diff */ -SIMPLE_TYPE(Diff, git_diff_list, list) +SIMPLE_TYPE(Diff, git_diff, list) typedef struct { PyObject_HEAD diff --git a/test/test_repository.py b/test/test_repository.py index ab0b22c89..e783f80c6 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -65,7 +65,7 @@ def test_head(self): head = self.repo.head self.assertEqual(HEAD_SHA, head.target.hex) self.assertEqual(type(head), Reference) - self.assertFalse(self.repo.head_is_orphaned) + self.assertFalse(self.repo.head_is_unborn) self.assertFalse(self.repo.head_is_detached) def test_read(self): @@ -294,7 +294,7 @@ def test_is_base(self): self.assertFalse(self.repo.is_bare) def test_head(self): - self.assertTrue(self.repo.head_is_orphaned) + self.assertTrue(self.repo.head_is_unborn) self.assertFalse(self.repo.head_is_detached) From 12ea3d2ddaf8b504d368eb71239b4db9b8eba866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 5 Nov 2013 19:20:58 +0100 Subject: [PATCH 0589/2237] Branch: move from foreach to the iterator --- src/repository.c | 95 +++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 50 deletions(-) diff --git a/src/repository.c b/src/repository.c index 222fd2346..1ac660831 100644 --- a/src/repository.c +++ b/src/repository.c @@ -920,69 +920,64 @@ PyDoc_STRVAR(Repository_listall_branches__doc__, "\n" "Return a tuple with all the branches in the repository."); -struct branch_foreach_s { - PyObject *tuple; - Py_ssize_t pos; -}; - -int -branch_foreach_cb(const char *branch_name, git_branch_t branch_type, void *payload) -{ - /* This is the callback that will be called in git_branch_foreach. It - * will be called for every branch. - * payload is a struct branch_foreach_s. - */ - int err; - struct branch_foreach_s *payload_s = (struct branch_foreach_s *)payload; - - if (PyTuple_Size(payload_s->tuple) <= payload_s->pos) - { - err = _PyTuple_Resize(&(payload_s->tuple), payload_s->pos * 2); - if (err) { - Py_CLEAR(payload_s->tuple); - return GIT_ERROR; - } - } - - PyObject *py_branch_name = to_path(branch_name); - if (py_branch_name == NULL) { - Py_CLEAR(payload_s->tuple); - return GIT_ERROR; - } - - PyTuple_SET_ITEM(payload_s->tuple, payload_s->pos++, py_branch_name); - - return GIT_OK; -} - - PyObject * Repository_listall_branches(Repository *self, PyObject *args) { - unsigned int list_flags = GIT_BRANCH_LOCAL; + git_branch_t list_flags = GIT_BRANCH_LOCAL; + git_branch_iterator *iter; + git_reference *ref = NULL; + Py_ssize_t pos = 0; int err; + git_branch_t type; + PyObject *tuple; /* 1- Get list_flags */ if (!PyArg_ParseTuple(args, "|I", &list_flags)) return NULL; - /* 2- Get the C result */ - struct branch_foreach_s payload; - payload.tuple = PyTuple_New(4); - if (payload.tuple == NULL) - return NULL; + tuple = PyTuple_New(4); + if (tuple == NULL) + return NULL; - payload.pos = 0; - err = git_branch_foreach(self->repo, list_flags, branch_foreach_cb, &payload); - if (err != GIT_OK) - return Error_set(err); + if ((err = git_branch_iterator_new(&iter, self->repo, list_flags)) < 0) + return Error_set(err); + + while ((err = git_branch_next(&ref, &type, iter)) == 0) { + if (PyTuple_Size(tuple) <= pos) { + if (_PyTuple_Resize(&tuple, pos * 2) < 0) + goto on_error; + } + + PyObject *py_branch_name = to_path(git_reference_shorthand(ref)); + git_reference_free(ref); + ref = NULL; + + if (py_branch_name == NULL) + goto on_error; + + PyTuple_SET_ITEM(tuple, pos++, py_branch_name); + } - /* 3- Trim the tuple */ - err = _PyTuple_Resize(&payload.tuple, payload.pos); - if (err) + git_branch_iterator_free(iter); + if (err == GIT_ITEROVER) + err = 0; + + if (err < 0) { + Py_CLEAR(tuple); + return Error_set(err); + } + + /* Remove the elements we might have overallocated in the loop */ + if (_PyTuple_Resize(&tuple, pos) < 0) return Error_set(err); - return payload.tuple; + return tuple; + + on_error: + git_reference_free(ref); + git_branch_iterator_free(iter); + Py_CLEAR(tuple); + return NULL; } From 4c47eba8c6ddc19da69983d0ed89c8c864c850d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 5 Nov 2013 19:38:01 +0100 Subject: [PATCH 0590/2237] Index: adjust to index_read() force flag --- src/index.c | 19 +++++++++++++------ src/index.h | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/index.c b/src/index.c index bd7a76310..ce47f13a0 100644 --- a/src/index.c +++ b/src/index.c @@ -222,17 +222,24 @@ Index__find(Index *self, PyObject *py_path) PyDoc_STRVAR(Index_read__doc__, - "read()\n" + "read(force=True)\n" "\n" "Update the contents of an existing index object in memory by reading from\n" - "the hard disk."); + "the hard disk." + "Arguments:\n" + "\n" + "force: if True (the default) allways reload. If False, only if the file has changed" +); PyObject * -Index_read(Index *self) +Index_read(Index *self, PyObject *args) { - int err; + int err, force = 1; + + if (!PyArg_ParseTuple(args, "|i", &force)) + return NULL; - err = git_index_read(self->index); + err = git_index_read(self->index, force); if (err < GIT_OK) return Error_set(err); @@ -427,7 +434,7 @@ PyMethodDef Index_methods[] = { METHOD(Index, diff_to_workdir, METH_VARARGS), METHOD(Index, diff_to_tree, METH_VARARGS), METHOD(Index, _find, METH_O), - METHOD(Index, read, METH_NOARGS), + METHOD(Index, read, METH_VARARGS), METHOD(Index, write, METH_NOARGS), METHOD(Index, read_tree, METH_O), METHOD(Index, write_tree, METH_NOARGS), diff --git a/src/index.h b/src/index.h index 7c63c1c0e..f38cb4a04 100644 --- a/src/index.h +++ b/src/index.h @@ -35,7 +35,7 @@ PyObject* Index_add(Index *self, PyObject *args); PyObject* Index_clear(Index *self); PyObject* Index_find(Index *self, PyObject *py_path); -PyObject* Index_read(Index *self); +PyObject* Index_read(Index *self, PyObject *args); PyObject* Index_write(Index *self); PyObject* Index_iter(Index *self); PyObject* Index_getitem(Index *self, PyObject *value); From aa5877e011ecd9e6c764a643ebefba819ac835f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 20 Nov 2013 16:43:22 +0100 Subject: [PATCH 0591/2237] repository: return a list from listall Make listall_references() and listall_branches() return a list instead of a tuple, as they're simply sequences of objects. --- src/repository.c | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/repository.c b/src/repository.c index 1ac660831..ab5ebb430 100644 --- a/src/repository.c +++ b/src/repository.c @@ -877,9 +877,9 @@ PyObject* Repository_create_branch(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_listall_references__doc__, - "listall_references() -> (str, ...)\n" + "listall_references() -> [str, ...]\n" "\n" - "Return a tuple with all the references in the repository."); + "Return a list with all the references in the repository."); PyObject * Repository_listall_references(Repository *self, PyObject *args) @@ -895,18 +895,18 @@ Repository_listall_references(Repository *self, PyObject *args) return Error_set(err); /* Create a new PyTuple */ - py_result = PyTuple_New(c_result.count); + py_result = PyList_New(c_result.count); if (py_result == NULL) goto out; /* Fill it */ for (index=0; index < c_result.count; index++) { - py_string = to_path((c_result.strings)[index]); + py_string = to_path(c_result.strings[index]); if (py_string == NULL) { Py_CLEAR(py_result); goto out; } - PyTuple_SET_ITEM(py_result, index, py_string); + PyList_SET_ITEM(py_result, index, py_string); } out: @@ -916,7 +916,7 @@ Repository_listall_references(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_listall_branches__doc__, - "listall_branches([flags]) -> (str, ...)\n" + "listall_branches([flags]) -> [str, ...]\n" "\n" "Return a tuple with all the branches in the repository."); @@ -926,36 +926,33 @@ Repository_listall_branches(Repository *self, PyObject *args) git_branch_t list_flags = GIT_BRANCH_LOCAL; git_branch_iterator *iter; git_reference *ref = NULL; - Py_ssize_t pos = 0; int err; git_branch_t type; - PyObject *tuple; + PyObject *list; /* 1- Get list_flags */ if (!PyArg_ParseTuple(args, "|I", &list_flags)) return NULL; - tuple = PyTuple_New(4); - if (tuple == NULL) + list = PyList_New(0); + if (list == NULL) return NULL; if ((err = git_branch_iterator_new(&iter, self->repo, list_flags)) < 0) return Error_set(err); while ((err = git_branch_next(&ref, &type, iter)) == 0) { - if (PyTuple_Size(tuple) <= pos) { - if (_PyTuple_Resize(&tuple, pos * 2) < 0) - goto on_error; - } - PyObject *py_branch_name = to_path(git_reference_shorthand(ref)); git_reference_free(ref); - ref = NULL; if (py_branch_name == NULL) goto on_error; - PyTuple_SET_ITEM(tuple, pos++, py_branch_name); + err = PyList_Append(list, py_branch_name); + Py_DECREF(py_branch_name); + + if (err < 0) + goto on_error; } git_branch_iterator_free(iter); @@ -963,20 +960,15 @@ Repository_listall_branches(Repository *self, PyObject *args) err = 0; if (err < 0) { - Py_CLEAR(tuple); + Py_CLEAR(list); return Error_set(err); } - /* Remove the elements we might have overallocated in the loop */ - if (_PyTuple_Resize(&tuple, pos) < 0) - return Error_set(err); - - return tuple; + return list; on_error: - git_reference_free(ref); git_branch_iterator_free(iter); - Py_CLEAR(tuple); + Py_CLEAR(list); return NULL; } From 6fd851a97e5316b3d0b2cdaac5e63a0bd662dfae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 23 Nov 2013 13:07:13 +0100 Subject: [PATCH 0592/2237] Fix tests with Python 2.6 --- test/test_blame.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/test_blame.py b/test/test_blame.py index 909e7c4cf..9c04e799b 100644 --- a/test/test_blame.py +++ b/test/test_blame.py @@ -69,17 +69,19 @@ def test_blame_index(self): '0000000000000000000000000000000000000000') self.assertEqual(hunk.orig_path, PATH) self.assertEqual(HUNKS[i][1], hunk.orig_start_line_number) - self.assertIsNone(hunk.orig_committer) + self.assertTrue(hunk.orig_committer is None) self.assertEqual(HUNKS[i][3], hunk.boundary) def test_blame_with_invalid_index(self): repo = self.repo blame = repo.blame(PATH) - with self.assertRaises(IndexError): + def test(): blame[100000] blame[-1] + self.assertRaises(IndexError, test) + def test_blame_for_line(self): repo = self.repo blame = repo.blame(PATH) @@ -95,17 +97,19 @@ def test_blame_for_line(self): '0000000000000000000000000000000000000000') self.assertEqual(hunk.orig_path, PATH) self.assertEqual(HUNKS[i][1], hunk.orig_start_line_number) - self.assertIsNone(hunk.orig_committer) + self.assertTrue(hunk.orig_committer is None) self.assertEqual(HUNKS[i][3], hunk.boundary) def test_blame_with_invalid_line(self): repo = self.repo blame = repo.blame(PATH) - with self.assertRaises(IndexError): + def test(): blame.for_line(0) blame.for_line(100000) blame.for_line(-1) + self.assertRaises(IndexError, test) + if __name__ == '__main__': unittest.main() From 5652ed7e37325af0a0c6bdc33ac37fe57034ccdc Mon Sep 17 00:00:00 2001 From: XTao Date: Wed, 30 Oct 2013 15:33:47 +0800 Subject: [PATCH 0593/2237] Fix repository.write --- src/repository.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/repository.c b/src/repository.c index 5f1b4dda5..49e717923 100644 --- a/src/repository.c +++ b/src/repository.c @@ -431,8 +431,9 @@ Repository_write(Repository *self, PyObject *args) if (err < 0) return Error_set(err); - git_odb_stream_write(stream, buffer, buflen); - err = git_odb_stream_finalize_write(&oid, stream); + err = git_odb_stream_write(stream, buffer, buflen); + if (!err) + err = git_odb_stream_finalize_write(&oid, stream); git_odb_stream_free(stream); return git_oid_to_python(&oid); } From c4be96fb7ce4d1f14f954be2a5cc44fb9ec3d7ea Mon Sep 17 00:00:00 2001 From: XTao Date: Wed, 30 Oct 2013 15:42:30 +0800 Subject: [PATCH 0594/2237] Fix multivar interface. --- src/config.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/config.c b/src/config.c index b1e7afdc1..873ba7363 100644 --- a/src/config.c +++ b/src/config.c @@ -352,27 +352,27 @@ Config_get_multivar(Config *self, PyObject *args) list = PyList_New(0); err = git_config_multivar_iterator_new(&iter, self->config, name, regex); if (err < 0) - return Error_set(err); + return Error_set(err); while ((err = git_config_next(&entry, iter)) == 0) { - PyObject *item; + PyObject *item; - item = to_unicode(entry->value, NULL, NULL); - if (item == NULL) { - git_config_iterator_free(iter); - return NULL; - } + item = to_unicode(entry->value, NULL, NULL); + if (item == NULL) { + git_config_iterator_free(iter); + return NULL; + } - PyList_Append(list, item); - Py_CLEAR(item); + PyList_Append(list, item); + Py_CLEAR(item); } git_config_iterator_free(iter); if (err == GIT_ITEROVER) - err = 0; + err = 0; if (err < 0) - return Error_set(err); + return Error_set(err); return list; } From e560a89f903aaeb53330605adfb3170745487332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 24 Nov 2013 10:46:49 +0100 Subject: [PATCH 0595/2237] Fix error handling in Repository.write --- src/repository.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/repository.c b/src/repository.c index 49e717923..f682bcb32 100644 --- a/src/repository.c +++ b/src/repository.c @@ -432,9 +432,16 @@ Repository_write(Repository *self, PyObject *args) return Error_set(err); err = git_odb_stream_write(stream, buffer, buflen); - if (!err) - err = git_odb_stream_finalize_write(&oid, stream); + if (err) { + git_odb_stream_free(stream); + return Error_set(err); + } + + err = git_odb_stream_finalize_write(&oid, stream); git_odb_stream_free(stream); + if (err) + return Error_set(err); + return git_oid_to_python(&oid); } From f26d6bedfec7bf0eca746c3a4381b66c85fe2322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 24 Nov 2013 11:49:17 +0100 Subject: [PATCH 0596/2237] C coding style: indentation fixes --- src/diff.c | 4 ++-- src/note.c | 6 +++--- src/remote.c | 4 ++-- src/repository.c | 39 ++++++++++++++++++++------------------- 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/diff.c b/src/diff.c index 01e6e7dbe..f81f63574 100644 --- a/src/diff.c +++ b/src/diff.c @@ -71,7 +71,7 @@ diff_get_patch_byindex(git_diff* diff, size_t idx) PyObject *py_line_origin=NULL, *py_line=NULL; err = git_patch_from_diff(&patch, diff, idx); - if (err < 0) + if (err < 0) return Error_set(err); delta = git_patch_get_delta(patch); @@ -110,7 +110,7 @@ diff_get_patch_byindex(git_diff* diff, size_t idx) err = git_patch_get_line_in_hunk(&line, patch, i, j); if (err < 0) - goto cleanup; + goto cleanup; py_line_origin = to_unicode_n(&line->origin, 1, NULL, NULL); py_line = to_unicode_n(line->content, line->content_len, NULL, NULL); diff --git a/src/note.c b/src/note.c index c7ef0f2ca..c515d8bd4 100644 --- a/src/note.c +++ b/src/note.c @@ -72,7 +72,7 @@ PyDoc_STRVAR(Note_oid__doc__, PyObject * Note_oid__get__(Note *self) { - return git_oid_to_python(git_note_oid(self->note)); + return git_oid_to_python(git_note_oid(self->note)); } @@ -82,7 +82,7 @@ PyDoc_STRVAR(Note_message__doc__, PyObject * Note_message__get__(Note *self) { - return to_unicode(git_note_message(self->note), NULL, NULL); + return to_unicode(git_note_message(self->note), NULL, NULL); } @@ -207,7 +207,7 @@ PyTypeObject NoteIterType = { 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc) NoteIter_iternext, /* tp_iternext */ + (iternextfunc) NoteIter_iternext, /* tp_iternext */ }; diff --git a/src/remote.c b/src/remote.c index 6b684ecfc..66530ce06 100644 --- a/src/remote.c +++ b/src/remote.c @@ -86,7 +86,7 @@ Remote_name__set__(Remote *self, PyObject* py_name) free(name); if (err == GIT_OK) - return 0; + return 0; Error_set(err); } @@ -116,7 +116,7 @@ Remote_url__set__(Remote *self, PyObject* py_url) free(url); if (err == GIT_OK) - return 0; + return 0; Error_set(err); } diff --git a/src/repository.c b/src/repository.c index f682bcb32..9663999f2 100644 --- a/src/repository.c +++ b/src/repository.c @@ -945,37 +945,37 @@ Repository_listall_branches(Repository *self, PyObject *args) list = PyList_New(0); if (list == NULL) - return NULL; + return NULL; if ((err = git_branch_iterator_new(&iter, self->repo, list_flags)) < 0) - return Error_set(err); + return Error_set(err); while ((err = git_branch_next(&ref, &type, iter)) == 0) { PyObject *py_branch_name = to_path(git_reference_shorthand(ref)); git_reference_free(ref); if (py_branch_name == NULL) - goto on_error; + goto error; err = PyList_Append(list, py_branch_name); Py_DECREF(py_branch_name); if (err < 0) - goto on_error; + goto error; } git_branch_iterator_free(iter); if (err == GIT_ITEROVER) - err = 0; + err = 0; if (err < 0) { Py_CLEAR(list); - return Error_set(err); + return Error_set(err); } return list; - on_error: +error: git_branch_iterator_free(iter); Py_CLEAR(list); return NULL; @@ -1116,26 +1116,27 @@ Repository_status(Repository *self, PyObject *args) entry = git_status_byindex(list, i); if (entry == NULL) - goto on_error; + goto error; /* We need to choose one of the strings */ - path = entry->head_to_index ? - entry->head_to_index->old_file.path : - entry->index_to_workdir->old_file.path; + if (entry->head_to_index) + path = entry->head_to_index->old_file.path; + else + path = entry->index_to_workdir->old_file.path; status = PyLong_FromLong((long) entry->status); err = PyDict_SetItemString(dict, path, status); Py_CLEAR(status); if (err < 0) - goto on_error; + goto error; } git_status_list_free(list); return dict; - on_error: +error: git_status_list_free(list); Py_CLEAR(dict); return NULL; @@ -1486,14 +1487,14 @@ PyObject* Repository_blame(Repository *self, PyObject *args, PyObject *kwds) return NULL; if (value1) { - err = py_oid_to_git_oid_expand(self->repo, value1, &opts.newest_commit); - if (err < 0) - return NULL; + err = py_oid_to_git_oid_expand(self->repo, value1, &opts.newest_commit); + if (err < 0) + return NULL; } if (value2) { - err = py_oid_to_git_oid_expand(self->repo, value2, &opts.oldest_commit); - if (err < 0) - return NULL; + err = py_oid_to_git_oid_expand(self->repo, value2, &opts.oldest_commit); + if (err < 0) + return NULL; } err = git_blame_file(&blame, self->repo, path, NULL); From c80fb4814feda4846a0a1e7f96caf333040e2082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 24 Nov 2013 13:34:27 +0100 Subject: [PATCH 0597/2237] Update documentation --- .mailmap | 4 +++- docs/blame.rst | 46 +++++++++++++++++++++++++++++++++++++++++++++ docs/diff.rst | 12 ++++++++++++ docs/index.rst | 1 + docs/objects.rst | 3 +++ docs/references.rst | 4 +++- 6 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 docs/blame.rst diff --git a/.mailmap b/.mailmap index 5c3799bdd..453de866a 100644 --- a/.mailmap +++ b/.mailmap @@ -4,4 +4,6 @@ J. David Ibáñez Martin Lenders Richo Healey Xavier Delannoy -Xu Tao +Xu Tao +Xu Tao + diff --git a/docs/blame.rst b/docs/blame.rst new file mode 100644 index 000000000..b0c7381bb --- /dev/null +++ b/docs/blame.rst @@ -0,0 +1,46 @@ +********************************************************************** +Blame +********************************************************************** + +.. contents:: + + +.. automethod:: pygit2.Repository.blame + + +The Blame type +============== + +.. automethod:: pygit2.Blame.for_line +.. method:: iter(Blame) +.. method:: len(Blame) +.. method:: Blame[n] + + +The BlameHunk type +================== + +Attributes: + +.. autoattribute:: pygit2.BlameHunk.lines_in_hunk +.. autoattribute:: pygit2.BlameHunk.final_commit_id +.. autoattribute:: pygit2.BlameHunk.final_start_line_number +.. autoattribute:: pygit2.BlameHunk.orig_commit_id +.. autoattribute:: pygit2.BlameHunk.orig_path +.. autoattribute:: pygit2.BlameHunk.orig_start_line_number +.. autoattribute:: pygit2.BlameHunk.boundary + +Getters: + +.. autoattribute:: pygit2.BlameHunk.final_committer +.. autoattribute:: pygit2.BlameHunk.orig_committer + + +Constants +========= + +.. py:data:: GIT_BLAME_NORMAL +.. py:data:: GIT_BLAME_TRACK_COPIES_SAME_FILE +.. py:data:: GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES +.. py:data:: GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES +.. py:data:: GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES diff --git a/docs/diff.rst b/docs/diff.rst index d81fa9c9c..0e5fbaffa 100644 --- a/docs/diff.rst +++ b/docs/diff.rst @@ -35,6 +35,10 @@ The Diff type ==================== .. autoattribute:: pygit2.Diff.patch +.. method:: len(Diff) + + Returns the number of deltas/patches in this diff. + .. automethod:: pygit2.Diff.merge .. automethod:: pygit2.Diff.find_similar @@ -42,6 +46,8 @@ The Diff type The Patch type ==================== +Attributes: + .. autoattribute:: pygit2.Patch.old_file_path .. autoattribute:: pygit2.Patch.new_file_path .. autoattribute:: pygit2.Patch.old_oid @@ -49,6 +55,12 @@ The Patch type .. autoattribute:: pygit2.Patch.status .. autoattribute:: pygit2.Patch.similarity .. autoattribute:: pygit2.Patch.hunks +.. autoattribute:: pygit2.Patch.additions +.. autoattribute:: pygit2.Patch.deletions + +Getters: + +.. autoattribute:: pygit2.Patch.is_binary The Hunk type diff --git a/docs/index.rst b/docs/index.rst index 5d25e6862..f838b3518 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -44,6 +44,7 @@ Usage guide: merge config remotes + blame Indices and tables diff --git a/docs/objects.rst b/docs/objects.rst index 73db010a9..377829e1d 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -109,6 +109,9 @@ This is their API: >>> print blob.size 130 +.. autoattribute:: pygit2.Blob.is_binary + + Creating blobs -------------- diff --git a/docs/references.rst b/docs/references.rst index 4db5eae7a..363e3eebe 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -18,6 +18,7 @@ The Reference type ==================== .. autoattribute:: pygit2.Reference.name +.. autoattribute:: pygit2.Reference.shorthand .. autoattribute:: pygit2.Reference.target .. autoattribute:: pygit2.Reference.type @@ -25,6 +26,7 @@ The Reference type .. automethod:: pygit2.Reference.rename .. automethod:: pygit2.Reference.resolve .. automethod:: pygit2.Reference.log +.. automethod:: pygit2.Reference.log_append .. automethod:: pygit2.Reference.get_object @@ -38,7 +40,7 @@ Example. These two lines are equivalent:: .. autoattribute:: pygit2.Repository.head .. autoattribute:: pygit2.Repository.head_is_detached -.. autoattribute:: pygit2.Repository.head_is_orphaned +.. autoattribute:: pygit2.Repository.head_is_unborn Branches ==================== From e6c270fe3591c42dc6a89c0f49e6a5547baf5acd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 24 Nov 2013 15:22:10 +0100 Subject: [PATCH 0598/2237] Get ready to release v0.20.0 --- README.rst | 121 +++++++++++++++++++++++++--------------------- docs/conf.py | 4 +- docs/install.rst | 4 +- pygit2/version.py | 2 +- 4 files changed, 72 insertions(+), 59 deletions(-) diff --git a/README.rst b/README.rst index 3b2d6e562..4345acb22 100644 --- a/README.rst +++ b/README.rst @@ -45,60 +45,73 @@ for the topic), send a pull request. Authors ============== -This is the list of authors of pygit2, sorted by number of commits (as shown by -``git shortlog -sn``): - -- J David Ibáñez -- Nico von Geyso -- W Trevor King -- Dave Borowitz -- Carlos Martín Nieto -- Daniel Rodríguez Troitiño -- Richo Healey -- Christian Boos -- Julien Miotte -- Martin Lenders -- Xavier Delannoy -- Yonggang Luo -- Valentin Haenel -- Bernardo Heynemann -- John Szakmeister -- Brodie Rao -- David Versmisse -- Petr Hosek -- Rémi Duraffort -- Sebastian Thiel -- Fraser Tweedale -- Han-Wen Nienhuys -- Petr Viktorin -- Alex Chamberlain -- Amit Bakshi -- Andrey Devyatkin -- Ben Davis -- Hervé Cauwelier -- Jared Flatow -- Jiunn Haur Lim -- Sarath Lakshman -- Vicent Marti -- Zoran Zaric -- Andrew Chin -- András Veres-Szentkirályi -- Benjamin Kircher -- Benjamin Pollack -- Bryan O'Sullivan -- David Fischer -- David Sanders -- Eric Davis -- Eric Schrijver -- Erik van Zijst -- Ferengee -- Hugh Cole-Baker -- Josh Bleecher Snyder -- Jun Omae -- Ridge Kennedy -- Rui Abreu Ferreira -- Xu Tao -- pistacchio +52 developers have contributed at least 1 commit to pygit2:: + + J. David Ibáñez Andrey Devyatkin + Nico von Geyso Ben Davis + Carlos Martín Nieto Hervé Cauwelier + W. Trevor King Huang Huang + Dave Borowitz Jared Flatow + Daniel Rodríguez Troitiño Jiunn Haur Lim + Richo Healey Sarath Lakshman + Christian Boos Vicent Marti + Julien Miotte Zoran Zaric + Martin Lenders Andrew Chin + Xavier Delannoy András Veres-Szentkirályi + Yonggang Luo Benjamin Kircher + Valentin Haenel Benjamin Pollack + Xu Tao Bryan O'Sullivan + Bernardo Heynemann David Fischer + John Szakmeister David Sanders + Brodie Rao Eric Davis + Petr Hosek Eric Schrijver + David Versmisse Erik van Zijst + Rémi Duraffort Ferengee + Sebastian Thiel Hugh Cole-Baker + Fraser Tweedale Josh Bleecher Snyder + Han-Wen Nienhuys Jun Omae + Petr Viktorin Ridge Kennedy + Alex Chamberlain Rui Abreu Ferreira + Amit Bakshi pistacchio + + +Changelog +============== + +0.20.0 (2013-11-24) +------------------- + +API changes: + +- Renamed ``Repository.head_is_orphaned`` to ``Repository.head_is_unborn`` + +- ``Repository.listall_references`` and ``Repository.listall_branches`` now + return a list, instead of a tuple + +- The prototype of ``clone_repository`` changed from:: + + # Before + pygit2.clone_repository(url, path, bare=False, remote_name='origin', + push_url=None, fetch_spec=None, push_spec=None, + checkout_branch=None) + + # Now + pygit2.clone_repository(url, path, bare=False, ignore_cert_errors=False, + remote_name='origin', checkout_branch=None) + +New API: + +- Added support for blame + +- New: + + - ``Reference.log_append(...)`` + - ``Reference.shorthand`` + - ``Blog.is_binary`` + - ``len(Diff)`` + - ``Patch.additions`` + - ``Patch.deletions`` + - ``Patch.is_binary`` License diff --git a/docs/conf.py b/docs/conf.py index 4ec29bcbd..92253aaf7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,9 +48,9 @@ # built documents. # # The short X.Y version. -version = '0.19' +version = '0.20' # The full version, including alpha/beta/rc tags. -release = '0.19.1' +release = '0.20.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/install.rst b/docs/install.rst index 93a5563d1..95cba2b9a 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -26,8 +26,8 @@ When those are installed, you can install pygit2: $ python setup.py test .. note:: A minor version of pygit2 must be used with the corresponding minor - version of libgit2. For example, pygit2 v0.19.x must be used with libgit2 - v0.19.0. + version of libgit2. For example, pygit2 v0.20.x must be used with libgit2 + v0.20.0. Building on \*nix (including OS X) =================================== diff --git a/pygit2/version.py b/pygit2/version.py index 8fe724b7a..428045a6d 100644 --- a/pygit2/version.py +++ b/pygit2/version.py @@ -23,4 +23,4 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -__version__ = '0.19.1' +__version__ = '0.20.0' From 336f042e223fbbf853e7c5f6ffff5b05fcef9271 Mon Sep 17 00:00:00 2001 From: Jose Plana Date: Thu, 21 Nov 2013 01:05:36 +0100 Subject: [PATCH 0599/2237] Added methods to interact with the remote refspecs --- src/remote.c | 161 ++++++++++++++++++++++++++++++++++++++++++++ test/test_remote.py | 35 ++++++++-- 2 files changed, 191 insertions(+), 5 deletions(-) diff --git a/src/remote.c b/src/remote.c index 6b684ecfc..12c391349 100644 --- a/src/remote.c +++ b/src/remote.c @@ -33,6 +33,7 @@ #include "types.h" #include "remote.h" + extern PyObject *GitError; extern PyTypeObject RepositoryType; @@ -95,8 +96,164 @@ Remote_name__set__(Remote *self, PyObject* py_name) } +PyObject * get_pylist_from_git_strarray(git_strarray *strarray) +{ + int index; + PyObject *new_list; + + new_list = PyList_New(strarray->count); + for (index = 0; index < strarray->count; (index)++ ) { + PyList_SET_ITEM( + new_list, + index, + PyString_FromString(strarray->strings[index])); + } + return new_list; +} + + +PyDoc_STRVAR(Remote_fetch_refspecs__doc__, "Fetch refspecs"); + + +PyObject * +Remote_fetch_refspecs__get__(Remote *self) +{ + int err; + git_strarray refspecs; + PyObject *new_list; + + err = git_remote_get_fetch_refspecs(&refspecs, self->remote); + + if (err != 0) { + Error_set(err); + return NULL; + } + + new_list = get_pylist_from_git_strarray(&refspecs); + + git_strarray_free(&refspecs); + return new_list; +} + + +PyDoc_STRVAR(Remote_push_refspecs__doc__, "Push refspecs"); + + +PyObject * +Remote_push_refspecs__get__(Remote *self) +{ + int err; + git_strarray refspecs; + PyObject *new_list; + + err = git_remote_get_push_refspecs(&refspecs, self->remote); + + if (err != 0) { + Error_set(err); + return NULL; + } + + new_list = get_pylist_from_git_strarray(&refspecs); + git_strarray_free(&refspecs); + return new_list; +} + + +int +get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) +{ + long index, n; + PyObject *item; + + n = PyObject_Length(pylist); + if (n < 0) + return -1; + + // allocate new git_strarray + void *ptr = calloc(n, sizeof(char *)); + + if (!ptr) + return -1; + + array->strings = ptr; + array->count = n; + + for (index = 0; index < n; index++) { + item = PyList_GetItem(pylist, index); + array->strings[index] = py_str_to_c_str(item, NULL); + } + return 0; +} + + +PyDoc_STRVAR(Remote_set_fetch_refspecs__doc__, + "set_fetch_refspecs([str])\n" + "\n"); + + +PyObject * +Remote_set_fetch_refspecs(Remote *self, PyObject *args) +{ + int err; + PyObject *pyrefspecs; + git_strarray fetch_refspecs; + + if (! PyArg_Parse(args, "O", &pyrefspecs)) + return NULL; + + if (get_strarraygit_from_pylist(&fetch_refspecs , pyrefspecs) != 0) { + return NULL; + } + + err = git_remote_set_fetch_refspecs(self->remote, &fetch_refspecs); + + if (err != 0) { + Error_set(err); + return NULL; + } + + git_strarray_free(&fetch_refspecs); + + Py_RETURN_NONE; +} + + +PyDoc_STRVAR(Remote_set_push_refspecs__doc__, + "set_push_refspecs([str])\n" + "\n"); + + +PyObject * +Remote_set_push_refspecs(Remote *self, PyObject *args) +{ + + int err; + PyObject *pyrefspecs; + git_strarray push_refspecs; + + if (! PyArg_Parse(args, "O", &pyrefspecs)) + return NULL; + + if (get_strarraygit_from_pylist(&push_refspecs, pyrefspecs) != 0) { + return NULL; + } + + err = git_remote_set_push_refspecs(self->remote, &push_refspecs); + + if (err != 0) { + Error_set(err); + return NULL; + } + + git_strarray_free(&push_refspecs); + + Py_RETURN_NONE; +} + + PyDoc_STRVAR(Remote_url__doc__, "Url of the remote"); + PyObject * Remote_url__get__(Remote *self) { @@ -288,6 +445,8 @@ PyMethodDef Remote_methods[] = { METHOD(Remote, save, METH_NOARGS), METHOD(Remote, get_refspec, METH_O), METHOD(Remote, push, METH_VARARGS), + METHOD(Remote, set_fetch_refspecs, METH_O), + METHOD(Remote, set_push_refspecs, METH_O), {NULL} }; @@ -295,6 +454,8 @@ PyGetSetDef Remote_getseters[] = { GETSET(Remote, name), GETSET(Remote, url), GETTER(Remote, refspec_count), + GETTER(Remote, fetch_refspecs), + GETTER(Remote, push_refspecs), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index b6a9edc5d..85a95e1ee 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -83,11 +83,36 @@ def test_refspec(self): self.assertEqual(refspec[0], REMOTE_FETCHSPEC_SRC) self.assertEqual(refspec[1], REMOTE_FETCHSPEC_DST) -# new_fetchspec = ('refs/foo/*', 'refs/remotes/foo/*') -# remote.fetchspec = new_fetchspec -# refspec = remote.get_refspec(0) -# self.assertEqual(new_fetchspec[0], refspec[0]) -# self.assertEqual(new_fetchspec[1], refspec[1]) + self.assertEqual(list, type(remote.fetch_refspecs)) + self.assertEqual(1, len(remote.fetch_refspecs)) + self.assertEqual('+refs/heads/*:refs/remotes/origin/*', + remote.fetch_refspecs[0]) + + self.assertEqual(list, type(remote.fetch_refspecs)) + self.assertEqual(0, len(remote.push_refspecs)) + + remote.set_fetch_refspecs(['+refs/*:refs/remotes/*']) + self.assertEqual('+refs/*:refs/remotes/*', + remote.fetch_refspecs[0]) + + remote.set_fetch_refspecs([ + '+refs/*:refs/remotes/*', + '+refs/test/*:refs/test/remotes/*' + ]) + self.assertEqual('+refs/*:refs/remotes/*', + remote.fetch_refspecs[0]) + self.assertEqual('+refs/test/*:refs/test/remotes/*', + remote.fetch_refspecs[1]) + + remote.set_push_refspecs([ + '+refs/*:refs/remotes/*', + '+refs/test/*:refs/test/remotes/*' + ]) + + self.assertEqual('+refs/*:refs/remotes/*', + remote.push_refspecs[0]) + self.assertEqual('+refs/test/*:refs/test/remotes/*', + remote.push_refspecs[1]) def test_remote_list(self): From ea8901f417ec29eb39524fecf23dda9760fd2148 Mon Sep 17 00:00:00 2001 From: Jose Plana Date: Mon, 25 Nov 2013 14:03:37 +0100 Subject: [PATCH 0600/2237] Fix PEP-7 --- src/remote.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/remote.c b/src/remote.c index 12c391349..c5cfb0408 100644 --- a/src/remote.c +++ b/src/remote.c @@ -87,7 +87,7 @@ Remote_name__set__(Remote *self, PyObject* py_name) free(name); if (err == GIT_OK) - return 0; + return 0; Error_set(err); } @@ -167,13 +167,13 @@ get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) n = PyObject_Length(pylist); if (n < 0) - return -1; + return -1; // allocate new git_strarray void *ptr = calloc(n, sizeof(char *)); if (!ptr) - return -1; + return -1; array->strings = ptr; array->count = n; From 41bedc05f09e36eed11d9fe858b8db9fe5f7b129 Mon Sep 17 00:00:00 2001 From: Jose Plana Date: Mon, 25 Nov 2013 17:36:35 +0100 Subject: [PATCH 0601/2237] Use unicode for python v3 support --- src/remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index c5cfb0408..bf8a7a843 100644 --- a/src/remote.c +++ b/src/remote.c @@ -106,7 +106,7 @@ PyObject * get_pylist_from_git_strarray(git_strarray *strarray) PyList_SET_ITEM( new_list, index, - PyString_FromString(strarray->strings[index])); + to_unicode(strarray->strings[index], NULL, NULL)); } return new_list; } From cb9ffa8b11fd80767da0045fd7d0ccc7a62a60b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 25 Nov 2013 23:01:13 +0100 Subject: [PATCH 0602/2237] PEP-7: Do not use C++ style // one-line comments --- src/branch.c | 8 ++++---- src/diff.c | 4 ++-- src/repository.c | 3 +-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/branch.c b/src/branch.c index 48f5bbaab..57c51a3ae 100644 --- a/src/branch.c +++ b/src/branch.c @@ -138,12 +138,12 @@ PyObject* Branch_remote_name__get__(Branch *self) CHECK_REFERENCE(self); branch_name = git_reference_name(self->reference); - // get the length of the remote name + /* Get the length of the remote name */ err = git_branch_remote_name(NULL, 0, self->repo->repo, branch_name); if (err < GIT_OK) return Error_set(err); - // get the actual remote name + /* Get the actual remote name */ c_name = calloc(err, sizeof(char)); if (c_name == NULL) return PyErr_NoMemory(); @@ -227,12 +227,12 @@ PyObject* Branch_upstream_name__get__(Branch *self) CHECK_REFERENCE(self); branch_name = git_reference_name(self->reference); - // get the length of the upstream name + /* Get the length of the upstream name */ err = git_branch_upstream_name(NULL, 0, self->repo->repo, branch_name); if (err < GIT_OK) return Error_set(err); - // get the actual upstream name + /* Get the actual upstream name */ c_name = calloc(err, sizeof(char)); if (c_name == NULL) return PyErr_NoMemory(); diff --git a/src/diff.c b/src/diff.c index f81f63574..b7bacd58d 100644 --- a/src/diff.c +++ b/src/diff.c @@ -142,8 +142,8 @@ Patch_dealloc(Patch *self) Py_CLEAR(self->hunks); free(self->old_oid); free(self->new_oid); - // we do not have to free old_file_path and new_file_path, they will - // be freed by git_diff_list_free in Diff_dealloc + /* We do not have to free old_file_path and new_file_path, they will + * be freed by git_diff_list_free in Diff_dealloc */ PyObject_Del(self); } diff --git a/src/repository.c b/src/repository.c index 9663999f2..7b6595aa4 100644 --- a/src/repository.c +++ b/src/repository.c @@ -536,8 +536,7 @@ Repository_config__get__(Repository *self) py_config->config = config; self->config = (PyObject*)py_config; - // We need 2 refs here. - // One is returned, one is kept internally. + /* We need 2 refs here. One is returned, one is kept internally. */ Py_INCREF(self->config); } else { Py_INCREF(self->config); From 37ed244a40b284f15792bd33f33341cf8f5f8118 Mon Sep 17 00:00:00 2001 From: Jose Plana Date: Tue, 26 Nov 2013 00:12:33 +0100 Subject: [PATCH 0603/2237] Removed reduntant returns, fixs PEP7 --- src/remote.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/remote.c b/src/remote.c index bf8a7a843..19274b1c2 100644 --- a/src/remote.c +++ b/src/remote.c @@ -124,10 +124,8 @@ Remote_fetch_refspecs__get__(Remote *self) err = git_remote_get_fetch_refspecs(&refspecs, self->remote); - if (err != 0) { - Error_set(err); - return NULL; - } + if (err != 0) + return Error_set(err); new_list = get_pylist_from_git_strarray(&refspecs); @@ -148,10 +146,8 @@ Remote_push_refspecs__get__(Remote *self) err = git_remote_get_push_refspecs(&refspecs, self->remote); - if (err != 0) { - Error_set(err); - return NULL; - } + if (err != 0) + return Error_set(err); new_list = get_pylist_from_git_strarray(&refspecs); git_strarray_free(&refspecs); @@ -169,7 +165,7 @@ get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) if (n < 0) return -1; - // allocate new git_strarray + /* allocate new git_strarray */ void *ptr = calloc(n, sizeof(char *)); if (!ptr) @@ -201,16 +197,13 @@ Remote_set_fetch_refspecs(Remote *self, PyObject *args) if (! PyArg_Parse(args, "O", &pyrefspecs)) return NULL; - if (get_strarraygit_from_pylist(&fetch_refspecs , pyrefspecs) != 0) { + if (get_strarraygit_from_pylist(&fetch_refspecs , pyrefspecs) != 0) return NULL; - } err = git_remote_set_fetch_refspecs(self->remote, &fetch_refspecs); - if (err != 0) { - Error_set(err); - return NULL; - } + if (err != 0) + return Error_set(err); git_strarray_free(&fetch_refspecs); @@ -234,16 +227,14 @@ Remote_set_push_refspecs(Remote *self, PyObject *args) if (! PyArg_Parse(args, "O", &pyrefspecs)) return NULL; - if (get_strarraygit_from_pylist(&push_refspecs, pyrefspecs) != 0) { + if (get_strarraygit_from_pylist(&push_refspecs, pyrefspecs) != 0) return NULL; - } err = git_remote_set_push_refspecs(self->remote, &push_refspecs); - if (err != 0) { - Error_set(err); - return NULL; - } + if (err != 0) + return Error_set(err); + git_strarray_free(&push_refspecs); From 03a6465927fea29e5911268bb6d736afc9e2ae41 Mon Sep 17 00:00:00 2001 From: Jose Plana Date: Tue, 26 Nov 2013 01:05:09 +0100 Subject: [PATCH 0604/2237] Fix documentation --- docs/remotes.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/remotes.rst b/docs/remotes.rst index 4b9692fcd..b8f9957bc 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -13,7 +13,11 @@ The Remote type .. autoattribute:: pygit2.Remote.name .. autoattribute:: pygit2.Remote.url .. autoattribute:: pygit2.Remote.refspec_count +.. autoattribute:: pygit2.Remote.fetch_refspec +.. autoattribute:: pygit2.Remote.push_refspec .. automethod:: pygit2.Remote.get_refspec +.. automethod:: pygit2.Remote.set_fetch_refspecs +.. automethod:: pygit2.Remote.set_push_refspecs .. automethod:: pygit2.Remote.fetch .. automethod:: pygit2.Remote.push .. automethod:: pygit2.Remote.save From f69eeae108322a4e46b88ad5913dbef0a89321f7 Mon Sep 17 00:00:00 2001 From: Jose Plana Date: Wed, 27 Nov 2013 19:26:24 +0100 Subject: [PATCH 0605/2237] Refactor getters to become methods --- src/remote.c | 12 ++++++------ test/test_remote.py | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/remote.c b/src/remote.c index bf8a7a843..b61eb2fea 100644 --- a/src/remote.c +++ b/src/remote.c @@ -112,11 +112,11 @@ PyObject * get_pylist_from_git_strarray(git_strarray *strarray) } -PyDoc_STRVAR(Remote_fetch_refspecs__doc__, "Fetch refspecs"); +PyDoc_STRVAR(Remote_get_fetch_refspecs__doc__, "Fetch refspecs"); PyObject * -Remote_fetch_refspecs__get__(Remote *self) +Remote_get_fetch_refspecs(Remote *self) { int err; git_strarray refspecs; @@ -136,11 +136,11 @@ Remote_fetch_refspecs__get__(Remote *self) } -PyDoc_STRVAR(Remote_push_refspecs__doc__, "Push refspecs"); +PyDoc_STRVAR(Remote_get_push_refspecs__doc__, "Push refspecs"); PyObject * -Remote_push_refspecs__get__(Remote *self) +Remote_get_push_refspecs(Remote *self) { int err; git_strarray refspecs; @@ -445,7 +445,9 @@ PyMethodDef Remote_methods[] = { METHOD(Remote, save, METH_NOARGS), METHOD(Remote, get_refspec, METH_O), METHOD(Remote, push, METH_VARARGS), + METHOD(Remote, get_fetch_refspecs, METH_O), METHOD(Remote, set_fetch_refspecs, METH_O), + METHOD(Remote, get_push_refspecs, METH_O), METHOD(Remote, set_push_refspecs, METH_O), {NULL} }; @@ -454,8 +456,6 @@ PyGetSetDef Remote_getseters[] = { GETSET(Remote, name), GETSET(Remote, url), GETTER(Remote, refspec_count), - GETTER(Remote, fetch_refspecs), - GETTER(Remote, push_refspecs), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index 85a95e1ee..741cd504d 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -83,26 +83,26 @@ def test_refspec(self): self.assertEqual(refspec[0], REMOTE_FETCHSPEC_SRC) self.assertEqual(refspec[1], REMOTE_FETCHSPEC_DST) - self.assertEqual(list, type(remote.fetch_refspecs)) - self.assertEqual(1, len(remote.fetch_refspecs)) + self.assertEqual(list, type(remote.get_fetch_refspecs())) + self.assertEqual(1, len(remote.get_fetch_refspecs())) self.assertEqual('+refs/heads/*:refs/remotes/origin/*', - remote.fetch_refspecs[0]) + remote.get_fetch_refspecs()[0]) - self.assertEqual(list, type(remote.fetch_refspecs)) - self.assertEqual(0, len(remote.push_refspecs)) + self.assertEqual(list, type(remote.get_push_refspecs())) + self.assertEqual(0, len(remote.get_push_refspecs())) remote.set_fetch_refspecs(['+refs/*:refs/remotes/*']) self.assertEqual('+refs/*:refs/remotes/*', - remote.fetch_refspecs[0]) + remote.get_fetch_refspecs()[0]) remote.set_fetch_refspecs([ '+refs/*:refs/remotes/*', '+refs/test/*:refs/test/remotes/*' ]) self.assertEqual('+refs/*:refs/remotes/*', - remote.fetch_refspecs[0]) + remote.get_fetch_refspecs()[0]) self.assertEqual('+refs/test/*:refs/test/remotes/*', - remote.fetch_refspecs[1]) + remote.get_fetch_refspecs()[1]) remote.set_push_refspecs([ '+refs/*:refs/remotes/*', @@ -110,9 +110,9 @@ def test_refspec(self): ]) self.assertEqual('+refs/*:refs/remotes/*', - remote.push_refspecs[0]) + remote.get_push_refspecs()[0]) self.assertEqual('+refs/test/*:refs/test/remotes/*', - remote.push_refspecs[1]) + remote.get_push_refspecs()[1]) def test_remote_list(self): From 369fa3087d32ba18e18021110cce77ee4f58a425 Mon Sep 17 00:00:00 2001 From: Jose Plana Date: Fri, 29 Nov 2013 19:19:54 +0100 Subject: [PATCH 0606/2237] Fix methods signature --- src/remote.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/remote.c b/src/remote.c index b61eb2fea..fbfd1fc26 100644 --- a/src/remote.c +++ b/src/remote.c @@ -445,9 +445,9 @@ PyMethodDef Remote_methods[] = { METHOD(Remote, save, METH_NOARGS), METHOD(Remote, get_refspec, METH_O), METHOD(Remote, push, METH_VARARGS), - METHOD(Remote, get_fetch_refspecs, METH_O), + METHOD(Remote, get_fetch_refspecs, METH_NOARGS), METHOD(Remote, set_fetch_refspecs, METH_O), - METHOD(Remote, get_push_refspecs, METH_O), + METHOD(Remote, get_push_refspecs, METH_NOARGS), METHOD(Remote, set_push_refspecs, METH_O), {NULL} }; From e65ab19625d4f8d79377776cff15e17d3611689d Mon Sep 17 00:00:00 2001 From: Petr Hosek Date: Sat, 30 Nov 2013 23:18:19 +0000 Subject: [PATCH 0607/2237] Provide minimal diff option --- src/pygit2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pygit2.c b/src/pygit2.c index 59ae098b4..64ff4b6db 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -376,6 +376,7 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_WHITESPACE_EOL) ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_SUBMODULES) ADD_CONSTANT_INT(m, GIT_DIFF_PATIENCE) + ADD_CONSTANT_INT(m, GIT_DIFF_MINIMAL) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_IGNORED) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNTRACKED) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNMODIFIED) From 9f4f978845908fb44466a3f1151b7ca829203c6c Mon Sep 17 00:00:00 2001 From: osanjose Date: Mon, 2 Dec 2013 10:56:39 +0100 Subject: [PATCH 0608/2237] Update with master --- src/pygit2.c | 7 +++++ src/repository.c | 37 +++++++++++++++++++++++ test/test_repository.py | 66 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 107 insertions(+), 3 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index 64ff4b6db..4bd37c1df 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -304,6 +304,13 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_SORT_TIME) ADD_CONSTANT_INT(m, GIT_SORT_REVERSE) + /* + * Reset + */ + ADD_CONSTANT_INT(m, GIT_RESET_SOFT) + ADD_CONSTANT_INT(m, GIT_RESET_MIXED) + ADD_CONSTANT_INT(m, GIT_RESET_HARD) + /* * References */ diff --git a/src/repository.c b/src/repository.c index 7b6595aa4..a5094bca4 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1503,6 +1503,42 @@ PyObject* Repository_blame(Repository *self, PyObject *args, PyObject *kwds) return wrap_blame(blame, self); } +PyDoc_STRVAR(Repository_reset__doc__, + "reset(oid, reset_type)\n" + "\n" + "Resets current head to the provided oid.\n" + "reset_type:\n" + "GIT_RESET_SOFT: resets head to point to oid, but does not modfy working copy, and leaves the changes in the index.\n" + "GIT_RESET_MIXED: resets head to point to oid, but does not modfy working copy. It empties the index too.\n" + "GIT_RESET_HARD: resets head to point to oid, and resets too the working copy and the content of the index.\n"); + +PyObject * +Repository_reset(Repository *self, PyObject* args) +{ + PyObject *py_oid; + git_oid oid; + git_object *target = NULL; + int err, reset_type; + size_t len; + + if (!PyArg_ParseTuple(args, "Oi", + &py_oid, + &reset_type + )) + return NULL; + + len = py_oid_to_git_oid(py_oid, &oid); + if (len == 0) + return NULL; + + err = git_object_lookup_prefix(&target, self->repo, &oid, len, + GIT_OBJ_ANY); + err = err < 0 ? err : git_reset(self->repo, target, reset_type); + git_object_free(target); + if (err < 0) + return Error_set_oid(err, &oid, len); + Py_RETURN_NONE; +} PyMethodDef Repository_methods[] = { METHOD(Repository, create_blob, METH_VARARGS), @@ -1534,6 +1570,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, listall_branches, METH_VARARGS), METHOD(Repository, create_branch, METH_VARARGS), METHOD(Repository, blame, METH_VARARGS | METH_KEYWORDS), + METHOD(Repository, reset, METH_VARARGS), {NULL} }; diff --git a/test/test_repository.py b/test/test_repository.py index e783f80c6..43e196be8 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -238,6 +238,69 @@ def test_merge_base(self): self.assertEqual(commit.hex, 'acecd5ea2924a4b900e7e149496e1f4b57976e51') + def test_reset_hard(self): + ref = "5ebeeebb320790caf276b9fc8b24546d63316533" + with open(os.path.join(self.repo.workdir, "hello.txt")) as f: + lines = f.readlines() + self.assertTrue("hola mundo\n" in lines) + self.assertTrue("bonjour le monde\n" in lines) + + self.repo.reset( + ref, + pygit2.GIT_RESET_HARD) + self.assertEqual(self.repo.head.target.hex, ref) + + with open(os.path.join(self.repo.workdir, "hello.txt")) as f: + lines = f.readlines() + #Hard reset will reset the working copy too + self.assertFalse("hola mundo\n" in lines) + self.assertFalse("bonjour le monde\n" in lines) + + def test_reset_soft(self): + ref = "5ebeeebb320790caf276b9fc8b24546d63316533" + with open(os.path.join(self.repo.workdir, "hello.txt")) as f: + lines = f.readlines() + self.assertTrue("hola mundo\n" in lines) + self.assertTrue("bonjour le monde\n" in lines) + + self.repo.reset( + ref, + pygit2.GIT_RESET_SOFT) + self.assertEqual(self.repo.head.target.hex, ref) + with open(os.path.join(self.repo.workdir, "hello.txt")) as f: + lines = f.readlines() + #Soft reset will not reset the working copy + self.assertTrue("hola mundo\n" in lines) + self.assertTrue("bonjour le monde\n" in lines) + + #soft reset will keep changes in the index + diff = self.repo.diff(cached=True) + self.assertRaises(KeyError, lambda: diff[0]) + + def test_reset_mixed(self): + ref = "5ebeeebb320790caf276b9fc8b24546d63316533" + with open(os.path.join(self.repo.workdir, "hello.txt")) as f: + lines = f.readlines() + self.assertTrue("hola mundo\n" in lines) + self.assertTrue("bonjour le monde\n" in lines) + + self.repo.reset( + ref, + pygit2.GIT_RESET_MIXED) + + self.assertEqual(self.repo.head.target.hex, ref) + + with open(os.path.join(self.repo.workdir, "hello.txt")) as f: + lines = f.readlines() + #mixed reset will not reset the working copy + self.assertTrue("hola mundo\n" in lines) + self.assertTrue("bonjour le monde\n" in lines) + + #mixed reset will set the index to match working copy + diff = self.repo.diff(cached=True) + self.assertTrue("hola mundo\n" in diff.patch) + self.assertTrue("bonjour le monde\n" in diff.patch) + class NewRepositoryTest(utils.NoRepoTestCase): @@ -274,7 +337,6 @@ def test_keyword_arg_true(self): self.assertTrue(repo.is_bare) - class DiscoverRepositoryTest(utils.NoRepoTestCase): def test_discover_repo(self): @@ -284,7 +346,6 @@ def test_discover_repo(self): self.assertEqual(repo.path, discover_repository(subdir)) - class EmptyRepositoryTest(utils.EmptyRepoTestCase): def test_is_empty(self): @@ -298,7 +359,6 @@ def test_head(self): self.assertFalse(self.repo.head_is_detached) - class CloneRepositoryTest(utils.NoRepoTestCase): def test_clone_repository(self): From cb3c28fc6a1786d89511e6c3f5af839653ab3d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 2 Dec 2013 11:07:36 +0100 Subject: [PATCH 0609/2237] Remove trailing whitespace --- src/repository.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repository.c b/src/repository.c index a5094bca4..c9b3f816d 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1537,7 +1537,7 @@ Repository_reset(Repository *self, PyObject* args) git_object_free(target); if (err < 0) return Error_set_oid(err, &oid, len); - Py_RETURN_NONE; + Py_RETURN_NONE; } PyMethodDef Repository_methods[] = { From 05798fc5aa97202f077170e733896912b136ad90 Mon Sep 17 00:00:00 2001 From: Jose Plana Date: Mon, 2 Dec 2013 13:40:38 +0100 Subject: [PATCH 0610/2237] Fixed some error exceptions, docs --- docs/remotes.rst | 4 ++++ src/remote.c | 54 +++++++++++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/docs/remotes.rst b/docs/remotes.rst index 4b9692fcd..12a1755b2 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -13,6 +13,10 @@ The Remote type .. autoattribute:: pygit2.Remote.name .. autoattribute:: pygit2.Remote.url .. autoattribute:: pygit2.Remote.refspec_count +.. automethod:: pygit2.Remote.get_push_refspec +.. automethod:: pygit2.Remote.get_pull_refspec +.. automethod:: pygit2.Remote.set_push_refspec +.. automethod:: pygit2.Remote.set_pull_refspec .. automethod:: pygit2.Remote.get_refspec .. automethod:: pygit2.Remote.fetch .. automethod:: pygit2.Remote.push diff --git a/src/remote.c b/src/remote.c index fbfd1fc26..818bb145c 100644 --- a/src/remote.c +++ b/src/remote.c @@ -102,6 +102,10 @@ PyObject * get_pylist_from_git_strarray(git_strarray *strarray) PyObject *new_list; new_list = PyList_New(strarray->count); + + if (new_list == NULL) + return Error_set(GITERR_NOMEMORY); + for (index = 0; index < strarray->count; (index)++ ) { PyList_SET_ITEM( new_list, @@ -124,10 +128,8 @@ Remote_get_fetch_refspecs(Remote *self) err = git_remote_get_fetch_refspecs(&refspecs, self->remote); - if (err != 0) { - Error_set(err); - return NULL; - } + if (err != GIT_OK) + return Error_set(err); new_list = get_pylist_from_git_strarray(&refspecs); @@ -148,12 +150,11 @@ Remote_get_push_refspecs(Remote *self) err = git_remote_get_push_refspecs(&refspecs, self->remote); - if (err != 0) { - Error_set(err); - return NULL; - } + if (err != GIT_OK) + return Error_set(err); new_list = get_pylist_from_git_strarray(&refspecs); + git_strarray_free(&refspecs); return new_list; } @@ -167,13 +168,13 @@ get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) n = PyObject_Length(pylist); if (n < 0) - return -1; + goto error; - // allocate new git_strarray + /* allocate new git_strarray */ void *ptr = calloc(n, sizeof(char *)); if (!ptr) - return -1; + goto error; array->strings = ptr; array->count = n; @@ -182,7 +183,12 @@ get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) item = PyList_GetItem(pylist, index); array->strings[index] = py_str_to_c_str(item, NULL); } - return 0; + + return GIT_OK; + + error: + Error_set(GITERR_NOMEMORY); + return -1; } @@ -199,21 +205,20 @@ Remote_set_fetch_refspecs(Remote *self, PyObject *args) git_strarray fetch_refspecs; if (! PyArg_Parse(args, "O", &pyrefspecs)) - return NULL; + return Error_set(GITERR_INVALID); - if (get_strarraygit_from_pylist(&fetch_refspecs , pyrefspecs) != 0) { + if (get_strarraygit_from_pylist(&fetch_refspecs , pyrefspecs) != GIT_OK) { return NULL; } err = git_remote_set_fetch_refspecs(self->remote, &fetch_refspecs); - if (err != 0) { - Error_set(err); - return NULL; - } - git_strarray_free(&fetch_refspecs); + if (err != GIT_OK) { + return Error_set(err); + } + Py_RETURN_NONE; } @@ -226,13 +231,12 @@ PyDoc_STRVAR(Remote_set_push_refspecs__doc__, PyObject * Remote_set_push_refspecs(Remote *self, PyObject *args) { - int err; PyObject *pyrefspecs; git_strarray push_refspecs; if (! PyArg_Parse(args, "O", &pyrefspecs)) - return NULL; + return Error_set(GITERR_INVALID); if (get_strarraygit_from_pylist(&push_refspecs, pyrefspecs) != 0) { return NULL; @@ -240,13 +244,11 @@ Remote_set_push_refspecs(Remote *self, PyObject *args) err = git_remote_set_push_refspecs(self->remote, &push_refspecs); - if (err != 0) { - Error_set(err); - return NULL; - } - git_strarray_free(&push_refspecs); + if (err != GIT_OK) + return Error_set(err); + Py_RETURN_NONE; } From 6050ae021dad0bc1af053b5fd1b6a431bf160df0 Mon Sep 17 00:00:00 2001 From: Jose Plana Date: Mon, 2 Dec 2013 16:17:51 +0100 Subject: [PATCH 0611/2237] Fix last minute typo --- src/remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index 55539af8f..b3fb89ee3 100644 --- a/src/remote.c +++ b/src/remote.c @@ -207,7 +207,7 @@ Remote_set_fetch_refspecs(Remote *self, PyObject *args) if (! PyArg_Parse(args, "O", &pyrefspecs)) return Error_set(GITERR_INVALID); - if (get_strarraygit_from_pylist(&fetch_refspecs , pyrefspecs) != GIT_OK) { + if (get_strarraygit_from_pylist(&fetch_refspecs , pyrefspecs) != GIT_OK) return NULL; err = git_remote_set_fetch_refspecs(self->remote, &fetch_refspecs); From d7196e7794dd8c0413396cfb3761e99fa04b0dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 2 Dec 2013 16:34:30 +0100 Subject: [PATCH 0612/2237] PEP-7: function name in column 1 http://www.python.org/dev/peps/pep-0007/#code-lay-out --- src/branch.c | 15 ++++++++++----- src/error.c | 15 ++++++++++----- src/reference.c | 3 ++- src/remote.c | 3 ++- src/repository.c | 6 ++++-- 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/branch.c b/src/branch.c index 57c51a3ae..ae547fe30 100644 --- a/src/branch.c +++ b/src/branch.c @@ -89,7 +89,8 @@ PyDoc_STRVAR(Branch_rename__doc__, "checked for validity.\n" "Returns the new branch."); -PyObject* Branch_rename(Branch *self, PyObject *args) +PyObject * +Branch_rename(Branch *self, PyObject *args) { int err, force = 0; git_reference *c_out; @@ -111,7 +112,8 @@ PyObject* Branch_rename(Branch *self, PyObject *args) PyDoc_STRVAR(Branch_branch_name__doc__, "The name of the local or remote branch."); -PyObject* Branch_branch_name__get__(Branch *self) +PyObject * +Branch_branch_name__get__(Branch *self) { int err; const char *c_name; @@ -129,7 +131,8 @@ PyObject* Branch_branch_name__get__(Branch *self) PyDoc_STRVAR(Branch_remote_name__doc__, "The name of the remote that the remote tracking branch belongs to."); -PyObject* Branch_remote_name__get__(Branch *self) +PyObject * +Branch_remote_name__get__(Branch *self) { int err; const char *branch_name; @@ -168,7 +171,8 @@ PyDoc_STRVAR(Branch_upstream__doc__, "The branch supporting the remote tracking branch or None if this is not a " "remote tracking branch. Set to None to unset."); -PyObject* Branch_upstream__get__(Branch *self) +PyObject * +Branch_upstream__get__(Branch *self) { int err; git_reference *c_reference; @@ -218,7 +222,8 @@ int Branch_upstream__set__(Branch *self, Reference *py_ref) PyDoc_STRVAR(Branch_upstream_name__doc__, "The name of the reference supporting the remote tracking branch."); -PyObject* Branch_upstream_name__get__(Branch *self) +PyObject * +Branch_upstream_name__get__(Branch *self) { int err; const char *branch_name; diff --git a/src/error.c b/src/error.c index 730c5263b..66587143a 100644 --- a/src/error.c +++ b/src/error.c @@ -29,7 +29,8 @@ PyObject *GitError; -PyObject * Error_type(int type) +PyObject * +Error_type(int type) { const git_error* error; /* Expected */ @@ -79,14 +80,16 @@ PyObject * Error_type(int type) } -PyObject* Error_set(int err) +PyObject * +Error_set(int err) { assert(err < 0); return Error_set_exc(Error_type(err)); } -PyObject* Error_set_exc(PyObject* exception) +PyObject * +Error_set_exc(PyObject* exception) { const git_error* error = giterr_last(); char* message = (error == NULL) ? @@ -97,7 +100,8 @@ PyObject* Error_set_exc(PyObject* exception) } -PyObject* Error_set_str(int err, const char *str) +PyObject * +Error_set_str(int err, const char *str) { const git_error* error; if (err == GIT_ENOTFOUND) { @@ -113,7 +117,8 @@ PyObject* Error_set_str(int err, const char *str) return PyErr_Format(Error_type(err), "%s: %s", str, error->message); } -PyObject* Error_set_oid(int err, const git_oid *oid, size_t len) +PyObject * +Error_set_oid(int err, const git_oid *oid, size_t len) { char hex[GIT_OID_HEXSZ + 1]; diff --git a/src/reference.c b/src/reference.c index 70108af6b..df5e9a6b2 100644 --- a/src/reference.c +++ b/src/reference.c @@ -49,7 +49,8 @@ void RefLogIter_dealloc(RefLogIter *self) PyObject_Del(self); } -PyObject* RefLogIter_iternext(RefLogIter *self) +PyObject * +RefLogIter_iternext(RefLogIter *self) { const git_reflog_entry *entry; RefLogEntry *py_entry; diff --git a/src/remote.c b/src/remote.c index a357fa4e1..c4db92c2d 100644 --- a/src/remote.c +++ b/src/remote.c @@ -96,7 +96,8 @@ Remote_name__set__(Remote *self, PyObject* py_name) } -PyObject * get_pylist_from_git_strarray(git_strarray *strarray) +PyObject * +get_pylist_from_git_strarray(git_strarray *strarray) { int index; PyObject *new_list; diff --git a/src/repository.c b/src/repository.c index c9b3f816d..20e2a7c50 100644 --- a/src/repository.c +++ b/src/repository.c @@ -866,7 +866,8 @@ PyDoc_STRVAR(Repository_create_branch__doc__, "\n" " repo.create_branch('foo', repo.head.hex, force=False)"); -PyObject* Repository_create_branch(Repository *self, PyObject *args) +PyObject * +Repository_create_branch(Repository *self, PyObject *args) { Commit *py_commit; git_reference *c_reference; @@ -1467,7 +1468,8 @@ PyDoc_STRVAR(Repository_blame__doc__, "\n" " repo.blame('foo.c', flags=GIT_BLAME_TRACK_COPIES_SAME_FILE)"); -PyObject* Repository_blame(Repository *self, PyObject *args, PyObject *kwds) +PyObject * +Repository_blame(Repository *self, PyObject *args, PyObject *kwds) { git_blame_options opts = GIT_BLAME_OPTIONS_INIT; git_blame *blame; From 82ac7c83af5329e580262f93043fb59b7d10a3cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 2 Dec 2013 16:49:12 +0100 Subject: [PATCH 0613/2237] Python API already sets exceptions on error --- src/remote.c | 46 ++++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/src/remote.c b/src/remote.c index c4db92c2d..b5d705998 100644 --- a/src/remote.c +++ b/src/remote.c @@ -103,16 +103,13 @@ get_pylist_from_git_strarray(git_strarray *strarray) PyObject *new_list; new_list = PyList_New(strarray->count); - if (new_list == NULL) - return Error_set(GITERR_NOMEMORY); + return NULL; + + for (index = 0; index < strarray->count; index++) + PyList_SET_ITEM(new_list, index, + to_unicode(strarray->strings[index], NULL, NULL)); - for (index = 0; index < strarray->count; (index)++ ) { - PyList_SET_ITEM( - new_list, - index, - to_unicode(strarray->strings[index], NULL, NULL)); - } return new_list; } @@ -128,7 +125,6 @@ Remote_get_fetch_refspecs(Remote *self) PyObject *new_list; err = git_remote_get_fetch_refspecs(&refspecs, self->remote); - if (err != GIT_OK) return Error_set(err); @@ -150,7 +146,6 @@ Remote_get_push_refspecs(Remote *self) PyObject *new_list; err = git_remote_get_push_refspecs(&refspecs, self->remote); - if (err != GIT_OK) return Error_set(err); @@ -166,16 +161,18 @@ get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) { long index, n; PyObject *item; + void *ptr; n = PyObject_Length(pylist); if (n < 0) - goto error; + return -1; /* allocate new git_strarray */ - void *ptr = calloc(n, sizeof(char *)); - - if (!ptr) - goto error; + ptr = calloc(n, sizeof(char *)); + if (!ptr) { + PyErr_SetNone(PyExc_MemoryError); + return -1; + } array->strings = ptr; array->count = n; @@ -186,10 +183,6 @@ get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) } return GIT_OK; - -error: - Error_set(GITERR_NOMEMORY); - return -1; } @@ -206,26 +199,24 @@ Remote_set_fetch_refspecs(Remote *self, PyObject *args) git_strarray fetch_refspecs; if (! PyArg_Parse(args, "O", &pyrefspecs)) - return Error_set(GITERR_INVALID); + return NULL; - if (get_strarraygit_from_pylist(&fetch_refspecs , pyrefspecs) != GIT_OK) + if (get_strarraygit_from_pylist(&fetch_refspecs, pyrefspecs) != GIT_OK) return NULL; err = git_remote_set_fetch_refspecs(self->remote, &fetch_refspecs); - git_strarray_free(&fetch_refspecs); - if (err != GIT_OK) { + if (err != GIT_OK) return Error_set(err); - } Py_RETURN_NONE; } PyDoc_STRVAR(Remote_set_push_refspecs__doc__, - "set_push_refspecs([str])\n" - "\n"); + "set_push_refspecs([str])\n" + "\n"); PyObject * @@ -236,13 +227,12 @@ Remote_set_push_refspecs(Remote *self, PyObject *args) git_strarray push_refspecs; if (! PyArg_Parse(args, "O", &pyrefspecs)) - return Error_set(GITERR_INVALID); + return NULL; if (get_strarraygit_from_pylist(&push_refspecs, pyrefspecs) != 0) return NULL; err = git_remote_set_push_refspecs(self->remote, &push_refspecs); - git_strarray_free(&push_refspecs); if (err != GIT_OK) From 1f5ec810adbdb5d94da7adcc24490106d686bfa2 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Wed, 4 Dec 2013 18:17:10 +0100 Subject: [PATCH 0614/2237] implementing merge with default options --- src/mergeresult.c | 161 ++++++++++++++++++++++++++++++++++++++++ src/mergeresult.h | 37 +++++++++ src/pygit2.c | 5 ++ src/repository.c | 56 +++++++++++++- src/repository.h | 4 +- src/types.h | 9 +++ test/test_repository.py | 60 ++++++++++++++- test/utils.py | 5 ++ 8 files changed, 333 insertions(+), 4 deletions(-) create mode 100644 src/mergeresult.c create mode 100644 src/mergeresult.h diff --git a/src/mergeresult.c b/src/mergeresult.c new file mode 100644 index 000000000..c0ffddd6e --- /dev/null +++ b/src/mergeresult.c @@ -0,0 +1,161 @@ +/* + * Copyright 2010-2013 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 "utils.h" +#include "types.h" +#include "oid.h" +#include "repository.h" +#include "mergeresult.h" + +extern PyTypeObject MergeResultType; +extern PyTypeObject IndexType; + +PyObject * +git_merge_result_to_python(git_merge_result *merge_result, Repository *repo) +{ + git_oid fastforward_oid; + MergeResult *py_merge_result; + + py_merge_result = PyObject_New(MergeResult, &MergeResultType); + + py_merge_result->is_uptodate = git_merge_result_is_uptodate(merge_result) == GIT_CVAR_TRUE; + + if (git_merge_result_is_fastforward(merge_result) == GIT_CVAR_TRUE) + { + py_merge_result->is_fastforward = GIT_CVAR_TRUE; + git_merge_result_fastforward_oid(&fastforward_oid, merge_result); + py_merge_result->fastforward_oid = git_oid_to_python((const git_oid *)&fastforward_oid); + } + else + { + py_merge_result->is_fastforward = GIT_CVAR_FALSE; + py_merge_result->fastforward_oid = NULL; + } + + py_merge_result->status = Repository_status(repo); + + return (PyObject*) py_merge_result; +} + +PyDoc_STRVAR(MergeResult_is_uptodate__doc__, "Is up to date"); + +PyObject * +MergeResult_is_uptodate__get__(MergeResult *self) +{ + if (self->is_uptodate) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +PyDoc_STRVAR(MergeResult_is_fastforward__doc__, "Is fastforward"); + +PyObject * +MergeResult_is_fastforward__get__(MergeResult *self) +{ + if (self->is_fastforward) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +PyDoc_STRVAR(MergeResult_fastforward_oid__doc__, "Fastforward Oid"); + +PyObject * +MergeResult_fastforward_oid__get__(MergeResult *self) +{ + if (self->is_fastforward == 1) + { + Py_INCREF(self->fastforward_oid); + return self->fastforward_oid; + } + else + Py_RETURN_NONE; +} + +PyDoc_STRVAR(MergeResult_status__doc__, "Merge repository status"); + +PyObject * +MergeResult_status__get__(MergeResult *self) +{ + Py_INCREF(self->status); + return self->status; +} + +PyGetSetDef MergeResult_getseters[] = { + GETTER(MergeResult, is_uptodate), + GETTER(MergeResult, is_fastforward), + GETTER(MergeResult, fastforward_oid), + GETTER(MergeResult, status), + {NULL}, +}; + +PyDoc_STRVAR(MergeResult__doc__, "MergeResult object."); + +PyTypeObject MergeResultType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.MergeResult", /* tp_name */ + sizeof(MergeResult), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* 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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + MergeResult__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 */ + MergeResult_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/mergeresult.h b/src/mergeresult.h new file mode 100644 index 000000000..7aadac4aa --- /dev/null +++ b/src/mergeresult.h @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2013 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. + */ + +#ifndef INCLUDE_pygit2_merge_result_h +#define INCLUDE_pygit2_merge_result_h + +#define PY_SSIZE_T_CLEAN +#include +#include + +PyObject* git_merge_result_to_python(git_merge_result *merge_result, Repository *repo); + +#endif diff --git a/src/pygit2.c b/src/pygit2.c index 4bd37c1df..e1766d26e 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -67,6 +67,7 @@ extern PyTypeObject NoteIterType; extern PyTypeObject BlameType; extern PyTypeObject BlameIterType; extern PyTypeObject BlameHunkType; +extern PyTypeObject MergeResultType; @@ -428,6 +429,10 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES) ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES) + /* Merge */ + INIT_TYPE(MergeResultType, NULL, NULL) + ADD_TYPE(m, MergeResult) + /* Global initialization of libgit2 */ git_threads_init(); diff --git a/src/repository.c b/src/repository.c index 20e2a7c50..3d4889c65 100644 --- a/src/repository.c +++ b/src/repository.c @@ -38,6 +38,7 @@ #include "remote.h" #include "branch.h" #include "blame.h" +#include "mergeresult.h" #include extern PyObject *GitError; @@ -578,6 +579,58 @@ Repository_merge_base(Repository *self, PyObject *args) return git_oid_to_python(&oid); } +PyDoc_STRVAR(Repository_merge__doc__, + "merge(oid) -> MergeResult\n" + "\n" + "Merges the given oid and returns the MergeResult.\n" + "\n" + "If the merge is fastforward the MergeResult will contain the new\n" + "fastforward oid.\n" + "If the branch is uptodate, nothing to merge, the MergeResult will\n" + "have the fastforward oid as None.\n" + "If the merge is not fastforward the MergeResult will have the status\n" + "produced by the merge, even if there are conflicts."); + +PyObject * +Repository_merge(Repository *self, PyObject *py_oid) +{ + git_merge_result *merge_result; + git_merge_head *oid_merge_head; + git_oid oid; + const git_merge_opts default_opts = GIT_MERGE_OPTS_INIT; + int err; + size_t len; + PyObject *py_merge_result; + + len = py_oid_to_git_oid(py_oid, &oid); + if (len == 0) + return NULL; + + err = git_merge_head_from_oid(&oid_merge_head, self->repo, &oid); + if (err < 0) + goto error; + + err = git_merge(&merge_result, self->repo, + (const git_merge_head **)&oid_merge_head, 1, + &default_opts); + if (err < 0) + { + git_merge_result_free(merge_result); + goto error; + } + + py_merge_result = git_merge_result_to_python(merge_result, self); + + git_merge_head_free(oid_merge_head); + git_merge_result_free(merge_result); + + return py_merge_result; + +error: + git_merge_head_free(oid_merge_head); + return Error_set(err); +} + PyDoc_STRVAR(Repository_walk__doc__, "walk(oid, sort_mode) -> iterator\n" "\n" @@ -1093,7 +1146,7 @@ PyDoc_STRVAR(Repository_status__doc__, "paths as keys and status flags as values. See pygit2.GIT_STATUS_*."); PyObject * -Repository_status(Repository *self, PyObject *args) +Repository_status(Repository *self) { PyObject *dict; int err; @@ -1551,6 +1604,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, TreeBuilder, METH_VARARGS), METHOD(Repository, walk, METH_VARARGS), METHOD(Repository, merge_base, METH_VARARGS), + METHOD(Repository, merge, METH_O), METHOD(Repository, read, METH_O), METHOD(Repository, write, METH_VARARGS), METHOD(Repository, create_reference_direct, METH_VARARGS), diff --git a/src/repository.h b/src/repository.h index 3c6094872..735f77480 100644 --- a/src/repository.h +++ b/src/repository.h @@ -63,10 +63,12 @@ PyObject* Repository_create_reference(Repository *self, PyObject *args, PyObject* kw); PyObject* Repository_packall_references(Repository *self, PyObject *args); -PyObject* Repository_status(Repository *self, PyObject *args); +PyObject* Repository_status(Repository *self); PyObject* Repository_status_file(Repository *self, PyObject *value); PyObject* Repository_TreeBuilder(Repository *self, PyObject *args); PyObject* Repository_blame(Repository *self, PyObject *args, PyObject *kwds); +PyObject* Repository_merge(Repository *self, PyObject *py_oid); + #endif diff --git a/src/types.h b/src/types.h index 46062a456..06504fd36 100644 --- a/src/types.h +++ b/src/types.h @@ -217,5 +217,14 @@ typedef struct { char boundary; } BlameHunk; +/* git_merge */ +typedef struct { + PyObject_HEAD + int is_uptodate; + int is_fastforward; + PyObject* fastforward_oid; + PyObject* status; + +} MergeResult; #endif diff --git a/test/test_repository.py b/test/test_repository.py index 43e196be8..bcbb48fa3 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -302,6 +302,63 @@ def test_reset_mixed(self): self.assertTrue("bonjour le monde\n" in diff.patch) +class RepositoryTest_III(utils.RepoTestCaseForMerging): + + def test_merge_uptodate(self): + branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533' + branch_oid = self.repo.get(branch_head_hex).oid + merge_result = self.repo.merge(branch_oid) + self.assertTrue(merge_result.is_uptodate) + self.assertFalse(merge_result.is_fastforward) + self.assertEquals(None, merge_result.fastforward_oid) + self.assertEquals({}, merge_result.status) + + def test_merge_fastforward(self): + branch_head_hex = 'e97b4cfd5db0fb4ebabf4f203979ca4e5d1c7c87' + branch_oid = self.repo.get(branch_head_hex).oid + merge_result = self.repo.merge(branch_oid) + self.assertFalse(merge_result.is_uptodate) + self.assertTrue(merge_result.is_fastforward) + # Asking twice to assure the reference counting is correct + self.assertEquals(branch_head_hex, merge_result.fastforward_oid.hex) + self.assertEquals(branch_head_hex, merge_result.fastforward_oid.hex) + self.assertEquals({}, merge_result.status) + + def test_merge_no_fastforward_no_conflicts(self): + branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' + branch_oid = self.repo.get(branch_head_hex).oid + merge_result = self.repo.merge(branch_oid) + self.assertFalse(merge_result.is_uptodate) + self.assertFalse(merge_result.is_fastforward) + self.assertEquals(None, merge_result.fastforward_oid) + # Asking twice to assure the reference counting is correct + self.assertEquals({'bye.txt': 1}, merge_result.status) + self.assertEquals({'bye.txt': 1}, merge_result.status) + + def test_merge_no_fastforward_conflicts(self): + branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' + branch_oid = self.repo.get(branch_head_hex).oid + merge_result = self.repo.merge(branch_oid) + self.assertFalse(merge_result.is_uptodate) + self.assertFalse(merge_result.is_fastforward) + self.assertEquals(None, merge_result.fastforward_oid) + # Asking twice to assure the reference counting is correct + self.assertEquals({'.gitignore': 132}, merge_result.status) + self.assertEquals({'.gitignore': 132}, merge_result.status) + + def test_merge_invalid_hex(self): + branch_head_hex = '12345678' + self.assertRaises(KeyError, self.repo.merge, branch_head_hex) + + def test_merge_already_something_in_index(self): + branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' + branch_oid = self.repo.get(branch_head_hex).oid + with open(os.path.join(self.repo.workdir, 'inindex.txt'), 'w') as f: + f.write('new content') + self.repo.index.add('inindex.txt') + self.assertRaises(pygit2.GitError, self.repo.merge, branch_oid) + + class NewRepositoryTest(utils.NoRepoTestCase): def test_new_repo(self): @@ -376,8 +433,7 @@ def test_clone_bare_repository(self): def test_clone_remote_name(self): repo_path = "./test/data/testrepo.git/" repo = clone_repository( - repo_path, self._temp_dir, remote_name="custom_remote" - ) + repo_path, self._temp_dir, remote_name="custom_remote") self.assertFalse(repo.is_empty) self.assertEqual(repo.remotes[0].name, "custom_remote") diff --git a/test/utils.py b/test/utils.py index fb7ca58f9..4d941e5c3 100644 --- a/test/utils.py +++ b/test/utils.py @@ -146,6 +146,11 @@ class RepoTestCase(AutoRepoTestCase): repo_spec = 'tar', 'testrepo' +class RepoTestCaseForMerging(AutoRepoTestCase): + + repo_spec = 'tar', 'testrepoformerging' + + class DirtyRepoTestCase(AutoRepoTestCase): repo_spec = 'tar', 'dirtyrepo' From 9955b3e67add52465d7201eccf8a7c973f7d5420 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Wed, 4 Dec 2013 18:23:13 +0100 Subject: [PATCH 0615/2237] implementing merge with default options --- test/data/testrepoformerging.tar | Bin 0 -> 92160 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/data/testrepoformerging.tar diff --git a/test/data/testrepoformerging.tar b/test/data/testrepoformerging.tar new file mode 100644 index 0000000000000000000000000000000000000000..c2b145803d58e7d03505b6b7bbfc8c542ef366ad GIT binary patch literal 92160 zcmeHQ2Yg%A)i+_)0s>{VlmfSk!FFQVZ)iG;Bo4uuki?9T!P6VI5-lV-u}K1@K-nn} zAhZoFWtL4DrIb;2Ll}Vqp@p*A1}LQLJwCqwx$jAq99w$w0@|lv6hFOp$GKV zzKovEr1WIMNTgzVsx=aCZKxRNkAP3W=c~XUG2jp6|D{LtsSqW}8}Lh#DETV{QT9oJ z3g#PVKzeGgJ)Kcfj8z!X#Il~AXYZAIo-Ow+`F*!eI;)m6=>D^f`ED&BBN5cGg9dPCljUl$E;*cS)| z{JKwMQ|noivNWQx=CE@A@APBYn8h0XJ}{C|lw@bUnuv{Kq7({)W zkffkj4VDFDNGK$HHy{s6>2bBqWxMM^p?XM`vWV=F>O^azxk-KdRQKs&UDv~5SrP&v zK~)SX;17okH5it>KCfT%%d+VA$#QuJ4S2J_T2!B)X^c#{NNYTi(wPy7>Mq+|OJN-| zaR`&L<~4$n8juAkEUE%RqMp|m?8;Mf*^^Z;xKlR-)^t=HJaTPnT>HsLXInX#I^0dpzM-*GUzcP9eUdSSOnP{ z5)9EF7JZ5k3W>6=g=IzX2NYRT{Q)B!4r*S}5ZPf7HIqoODN0I>C~QnPr7YDKC!!Jk zz}A=&i53t?7D8DDvJ@T|n`^l&x4UfjYn!{u0)Bw`y9+Xc%4S9o?SyKHxBdmv@-ErynZuSczr!((4~!1ml@8)DHf3bxm4cDuJf+1YK;K8w(R0>v#6 z+nr(tH+kNpCgMgE^KBXPxEPkgitfYOG~|N|K$i`y-{o-7563_tBuRcv(6NRogZnWw zNz4t+n#V0y02v#p#L~nPohfhwn9#ax!!YooK)}kyMWH)x^CY6ABkJoZP}KjM(v5*| z0+WBKr2oIb|Lezc)A9cf1xJ*of19po{o?{#^f%*wqTf&c|A0*XUnl-I_#9xD{Ov^-u?dqLcam{|H@>6hOb>6aq< zU+@MbGyW^evR^=+5VC**UPu3zkLmJj*wOt|hS^?X+HgpVIgdh*YM;^$>@Tu+KWyYioN^7LU4#6QL4-zRu2`S(eJB$51!67s*C{Et2$yUZ#?8#^C1@ctA* zfU`F1{OM5NYv$Pbnz zpDa4^Uk;A@TGo#2QyKg7zirv#e(mgXphx*H&k(Tf8ZHOIGTqaCCBxTN&#wHNs<>}p z68{vF{{WJ`#eDy-gaD2+|Mv~qW!56v*!c{C|7TG0KW%5YmE!^m@IY`{{u(jRoBV5f zT1`cgnMk6~VV~TnXnrX+|BE8_f8@{qhyu!fNB(mN9K?&3cpN#f8uG4_iF5?nmz|@T zu3;xerY({-(v@&x9sMwfL>k1Vdz1f2+(>+#`~PGo{y&IR?`paz z{q+tt+OFwcrTcbOO8+D1(_Q}u5C(GWzrLZntXd-&wg|Ck8ae$@JMN2PWi#ZKyUIt zK&~GScq#c8c>Z7h`7b^k0prO3;8DG+Wr%en)^W)5Hf7rp(JtKlFI@E||A{c!YW>Um z&f<^czhwS@e*9PT3pfVK$^XtGa1j5NqQ9p1-wR-y{@MIL7=#r6bMpTOk?>a2Hs~J; z3$jns1Cmz=>mp(_K~~rG3 zMKC&Wk2H&1>_zhm*O z`y>uLU%TwpfBoykJv=SJe?9-%=d;vnCu^VFG0Gji@{;HNu+@jLn3)!CCDJ^0d3_WB}v(yj+DJ>RFPo%J`JFn3Kl)H=N7 z_3KXh;Xa2we0lWwL)M+vaqaJ(z5ZO`^Rq6w{oNz<(!ve^}kPj9BTge`ETud z#RU_SwLg95wM!4%aqflDOHR7}cZphKarntSs_Oo>{JyC_ed)D-H@>{7V#@y=@q;lt ze6`}u?~Pge{6)XM;NcaYk9qpdP2XMH5IR39-}hqUlE*$;ctPmHKi$>&%)3{x6Q4cy zColY??S-E&Iw#t)`#C$)Fs8Zg6LzooIP~E2J8as2#_H{w=KtZz*sOQXz4M7vzdYp9 zLlT?zy7R&hUG=RO*6tubeZ@2L(x+bFKJ=`sC*LsrfdkhHjSZ_mJZJi@vS-Ym7vFg6 zPODQ>SAR789M81>+#=Vl{^+dsjmZ()j-5K*lf3HXrVT%QW%Iw^fAxEtZ+Q35f1ZEp ztGBwIN>*I;`-gwNao%5l)3p09_es@$@pePwyx+X`eC#J;bkyHp(VK>f4}7oPXXOQY)M@BiZd+cZtM>C~4m{^JREzk1hWPwn`j zcEPx<8h4vrap9N)CQV&E;kCowyyc=h&pLDD;`^3dyyuB`y>i=+)WN zUi{f=edOL}?0m=8d%bfu;bRryW0wPgkImaQU$MG*Yj=Iq6W3LLe|7D3A51yzCois- zJ4B%4r4zTmq3-H?p1#q0epCGHv)84X9{u5L*S|)bGxryNKY87wH@$wuk0)=l*ZFsC z_}gyB&RulJ$YdzFqzLLBSOp<}4YvYQ@19{Co!h zdgcVzFmcC%Mbie~oYL-nZ$AMVG}Ma6K~hte^bf`oJgR9>>3Y%y!1LS4}+i z+^UX!h8a(vcUrJ*cm0xkS8q7E`So3%c=(xB=@VCOzxVCW47;Q1hAYP%-8pj6!ozn9 zy>!QzS)*Pb`?D|Coin2D#wnToMz_dc-nQ)d=jyln(^<>6-gt3n-Zk5Ftd&he=!t0? z>K>lH;gN=^`Y!c5fB5`un_Jh+Y*yd@)o&x$&D-zR;d@P-tCKPG=J|8a6Y6@jhxV`B z=W&0>+Nb_9s=ndnVHMkLz13ZR7}l}&k%J$5_~FBAHZ-hner5f-4b5}cc^;eg@M}}o zZ+K*VgJ<@-=9YDjZJ5@)X;_80|L~Lh3epkN#_aLOyH@;amVEz$Rqelg^YhdHxcu2+ z72j*z=Z!IiFbu2MH?>Xc<$%l@`Z@yeS+W9;xH`G+whur4^+QLTvF@E^hd2Fo=Hy$C z*}CGB$A|yC$MPMw{R(4D(p3H-b&Hn@W z*$Ryf=9l2L``sF4yMMRAy@C2}i?;gn-46A8`fiW=`QPv0H+?I6_)=W^zwx^R#?{+* zM?Cvq{O*Kt57c)!+T!2cmbJ{h`-)Fbugjcp;+^v@IC6mk7KmAEvdf4yo zS?yW1$TfBNh5nP8Ha~ma)k4h<2RyOPg^WIHu*IZG5*p9>RlrMPw`qjP>b$@FdfAr$352)7{JTr0j zrW3}jn)leUAOELvWukJ<@yYK*-ha{)YI$Jx!P?#D-1N80*?luk{``v*76gwx{H>>V z-){Z9pYCw&=4alht-9>?iq~FgZjSFa?eI16L*KY`;=V8Z>-bT#esF1naOTZxhP`vn zwO_7o-~Ex!^LKyx4}0GnnvcJ&_H{LN!rH)&;flldlcydv z>t6q+i|-k6=;tTg+tje|ik-zZznB}?xAV2B*Q`12^5d(Zaukz4w`s)nhfs(A#CIjs z4&-#3Gw|#w+`k0hBvjd%xVISBJNIPNJbm&Nk%bNMJ%78$rBiP2tlanCpFepH>JP}L z)ch|?{q_D-(7Eq6;y>p3FMgZ_Ac%fR#{PfD|34rkF4~9<`pbr*3$o$$3w~V*;rv$3 z2!?S~aab}`B@puIes9nqX7BA&!v4cayZ+qyKb-!O9Pm5#UvKCf(EA-*Oka{sZ*CMGjHQPqpx{y;TJpH@zTogEldon zc!P-#4d57@ruvS7Qu40_hEV_G{eS%Y577@tv}6DGovQY0+MqvP_5!0{@%h4vX7~(E zmh^yO;GX0|;t!H{y`fNmoQS=BO6vc-{9p6@7uo0N|K6}Uu=lh2KcH`^{$F2o_ajX= zAN|mj&WHD!ztvO0#VgmYx%$9T>VK<~L;HL=us}Mk4H^bY$-f>NLj9k$|DgZr{0GPX zGiYSbHEe_a_Fj3BYjMkqQu-e&zi#%w*UA6c5{-j$r(-LmhAiG%+-2LlHrCz}I4S<+ zw)b|twzu}ux${;%x^6e`P3bRR-|@&j#{725rn^4&U;dZ3yxZUW!S!b=yG5_se~;Fw ztDZdeJ1w6NyYTz!eZN?|$9V52w?FW}Mz!gh)32JK-!NhSM|Zh(FK? z@!~08EbgrTd)vPbdQaZz*!R_!ez556i=O-FoR5z?=eH+red_QN$80_9)LXA#|L|8I zKG=|W^F;l%e_lNLjPaSProB5TV|N-Yo&OgaGW$R8{CD#IIQ##Hggr~?f6!l+!+sqm zzdtOiVL`#$Dt$q};-&YLhNYm0SHgsYVXX+XrS!k(>wf+}P6TlL|3xr5aF4R~zgIB2 zZ2@@%Z4Cp~pVhes1$Yi)YRpcf^ykPo8x5b$|NG`_R0m zpZ@6%_kBx8UVYZySJyP$y7u06$L;g}nv?E4U)GP`{P|t~-tgX48^#>BRmB4@2^6k( zKHnq^l#+j+cc|?DLj5m0?|&Z>HnBl}d+(|(0&OY%FVXog1@C`>{>S|9oc~t@sRQ(= zW2?(oi8EpUS6ubw>HirqbKy}0_2-48u!)vE0`KX*p3_IFP{c;I`>o9;d^@YI=C zeDHqfKD!uS34ZyA)R|L_PcQQB`Qfh8W7E$S_S|^SZ^P^7y>Ml8<91b}YTt8jKCM0=RSOX`1a|L4(Pl6`m$oTLAX;B=rKk^g7L z;`@bN4!ZfHT4TSOzjS=C{pFc8duPU9aL`%1J}*uD@Y8E1&pi8??RUQW&u6qhKXv=d zlK-Bu&AE@9KIw_*qxaSRa;>rZn)i=x+ot(9m+mru?eO?b%h+k&&+l1yWTLtHA?>X9 zBZpqLb*F3ilXE`U?W+%$$KII}-Y@;nJ$JbOwZD9F-joGrtoSNEf8)=NxUJ!dn{Rt= z@zdK~KXsIEr^blfqIn9|G!DAd3asj zVKo~befZFtn)MBjY?#?{@Z5tR-!Scwho{%SGOXgA$veI{P@OvMd}ADX6PkrT<*EW6d)km+q`qDdp37qoTn`=Im@89po7fl_KP|yIP^;^?a zDftf!S^j5s|F6jR|2q4>hK7Y~&_5Jb6a!nn3>@($E0UyRLxABmB-K!5zZ#VBvL>Gq zECOyR{g1^#F8=GsUNG4Ie&_t}A_yJ8M_Kz{IvNLmwwPy9O|5IGnd*6D1MLPr@%2hN zAI|ywmtvrl{HsH{|2O;oN5AaEe}{(QY|!7QNs_FEf{LO91Cl?Clb>Z@NHHW`3JAI` zdxMH5_2>Kt?E1^v|K|HYq5lP(|1l&+Upu|Kh_MO-uOKRh@>0)>)%^xP%jJKH+Ljx@ zdOY`VLd*UwSG+-o9?pGy;fJ@r{Lk%2UHMdL0SzxJuM#%Eh!T{Xg2UA^D6ZCBm* zqn9?mbo>ugkNf_Y7JOG3y~i^T?bLGKnt6{Ot^H}$r_(0Bxc=uijsNJ&7e8qI^y$`p z|5OqDZ2Y8eFslA5K`SNy@{r~KTjxKB=J`KP{{PUhn+^IKYS1UEim3Vm{-7XeAsqUn z={`db$~XlmsB40bvw(_#Thji=WS=|#f#^@?|2Y2tA{ZUGN0$A6qgFHPvF67&%xL-7 zunNzy+wL-OJvc3WlQB?A{)Hj)|B(Kdx#O44fAcx}zlMZuY|tOMTYgzF1iy;6GI%w= zqyz+2(?r>)_`^y-lfqt6>zDrrFZ<2<|H<(W{SOC_WB(5Y17!U_-aP-$unPa!ZEqfm z-kmo0X|HpfD+5Vs8O{W~Wa$Rln?N_uUKf2`K zH!ZmJxmAISr%JQasrT+Z{eqD{d@uIpmsg4#-O?8??D5^-&p9;q_ljYUcupRYfjOPn z!a#5G-OF*=1H5@uP-tB;Js2bB%C~U=!j@@8E=HqySi2O+}I!mPxRs zdMaXcvSn>bhDFj$2`Ae5{jknV2Fd7JJzAKgM5DUK!kx_B8p&AC+-zAS)5hWnrleZi zV|qN3u4im+n{M^emS~S^sH}IOid8i3IaW_47@{+ocJyat^b|Tyw@2cwOo=lknM@^; zsfd!%Sxir-l~$ca475QlDCmHIPG=HHmO(cb5A!k3O)x#Kgrhp1U?eooI=lfpuEeZ6 zMueUvn7fPn-SsY4#9%a{l)luHR8nc3c{~!=LT}H$OWHcMBGttfwLL&lg63ak_xJOB= zkqD!a(zw$st%I6`j-?84%8`=*1nqjgi&sJ&US(`mjY-zH5ozVPOV<|^sHJWvr4s2h zOGcH9kx0cDD8*kU%`$pi<8UlZM6{mZ7>%;}Qt$^F0Rm&pNF`z{taGAk8W;~I~7lZ+-n9C~CZDqVn2@eA#MN#Yqkq?8f|mr%S9>|s75N1f3ztn zR0E4CfDlloO*YI#q8d;D(jUd#RRsfevmcFRZbFfpEno*6K!lx{05@j4X6@jSgu%jS zd5KOvrV{OFT>)O12@3EHA#2l9dL65YWUABD2#p`(JElF7(zQAU46_X(nj(u1cC2FlbJ1&Y>QUs;F%!;qegDcoOkwC$8z42b=`#jh(GB$nEq6yQdkDWxL_Gqm7DCRkC!F-{*)@_N1uC_teCZ|$~R3n>^$q@H*Cea92 zh0MyR<9fHNk}8@1GxuELTNQGnDui-y8~8VthB_!<{$+{O600V2b0w416PVIiBF+-P z0nltQY}>L~ruQg}AE@DOiy!2fagAd0kGwNL-UuDj$-;Uy??LX*?XlGK%sBL|<0ZZ6_f?w>u|5NnCCE(=Pqh$p50(Bne&WBZWVAnTdYvka&eTq>Ol8OSL4^9V~fkbX*YXf z>DIo=KCy5K`4{mf=`GiPPW)#Gq`#uVuKb&Z0hx*>Pq1jBHBX?6uKN;kJ-6_yNOQBfO~JaYs}E~ox8+)~2W^2S%3CY;s4dWnwN}hqpOUdhD<^aLp=;Jc z!$L%M^-1HfRGaM;W|w@Ch8e+H;&cb=AC85vPRlh~w{cT=4lHzC^CVzyL(N7bz^b(x z%HjzvG;wQ^FXdp`E2fI20Wh52&mx+xkjdYw$MuvFtz%(Y#RK8cT3FO(ojoM*Xp5_7 z)Qu%zreftys{`~1XAsxmb7oDnmV;O{GSY$*TIL=+Yxbh%rpYtsvK3fVVyzHo)#-+X z^K0ryj+J>TzSxkgu0?OH0D*aqNi+AtDm3keW)0Zib~BRyNdQ^}>RZjjY=07>k!Q)?FA! zdpvRs^QhSo>8!e8;Zdw|!AOz*Z_u!oAvl*?3zBgMNUW(i9F^SDA-Yz64H~~ttrXY) zG?}IJu#(mXPyfU7hr=EV{eMCLM*=$bUm>Xm;rXsCv8CIj#G6~1F)Y1Pz zHWf0EV3XLvN=%6-at0iCk(mtQ&Z&!3dz>vsS~b?pa9-%9BQRUY^1kpzFN8P(zsTZ5 zxQ_c>X#H#Xe{!<``D3^f2(89xp$yeW!b6v|h({abBFOU%s3N}R=QB=% zv^li^od<^kJQwLkKBg)WYhd1Ydom3NoD$`fY)@jvvgZx8Tlvq6p+J9 zA{kMwIsl2!n&d@LqJUQG1V}MG)oLp0>bTyKskRE6SZviZ9Ca3em;iLsYr-cu0xd$m zw`Jsz1-R+mNI7h4dd75IaW5=+MpZxuHP3r+qlzmJfbruSM%KD2M~`;VpW$;&xD7(op{z{T?-x7 zm}zoCPk_Pf!+>YIGmp7UD95{i^F@*PBJ<~1PAzhggF7-14X|w{(>WFvAh%Z*n#FP; z9i8I~_zm4v1#K1h2Wd{V=taJ_o`D$+q{4p)@2Vb4EG0i3cfxU>m&GJ1XKc>wc$~9; zKr6J0_(T%s6U-aSd!8LTT(YUWp%a6T2YCz_2;6Z8NhNt9_Jf}=$9H5Xk&|>0Rahi; zLrxAC3$uJ!wcHY+m1Jtb_BcAPy3|W`mizQjy-Vuy;4rP9t{V_I=pzIxEcA1Fck2x{ z8NcnexbtXNWwz~n%9&;0JLc_iFD>gvx~v9iPkJ&5k4B~%SIj17z*A}2M&%K#(u}QG z;_9jd0nIzsn1}-1hO{Mf<{>bv8!*nCgI$j=Wl231Wgb|7fHT&~Or$`cRZW;Z<*=iK z1*5I|FrdlC9GNXa3mener?muE)p-oX$)45fjq~GftC^bp^T4k~gE1vZW@cq&!@`=Q z1W!ou7z;+#vIZ(&S%cYY>^LUY(yx_>Y#?4x(=b2YP-lTBF5r(SqCl|g@RGBdT$A0} zguz~0Wj0)Sl-Y8X#K+fT+tPyCS`#$zMa_@6DoV4gL=*a#^A0`ZfTfjIQR1PRD&Q*n zyOIZM*qpgdlV{In^n{PBvJ!z4X#TDnD`~)mrQPO;(1EzXLSZEp!A4!chv-pDm6p(btM&zklw~jq%6_XurX1Y zjHxaQih58XjkL0bVM%zQ`C8!(V2msi;ZrhQW`X102-L$r@D7{~sR&0xTvsU$o$BPX ziZy~5^-M$2VfXED_Em z{xOF{J(@E)jg_O7?93QhLP?DRAWJ&oz|=$*(E*;0qqh0&8c( z)PU3^W_A<^qeI)7(POS|jP?{!oYDkH%ijgwuS9H}B|IKa$*(1) zyha1D9X{E=TU=8qdZ?gf`i9vADpObig{Vw^BR!O%Tli#4s%S0uWfRSZ^qjH;=#wp2 z*zA1sAw9PW=9dd|5GV06*^*Yd{9-*d*}$=m`Xv#VEt8u9TrSsyIt+p4uStBRmp`fK zgqI{^}p1P{1rjYx-?nL&(0UIa_21ua~__RbqNcmY= zCD6Mn929@uKsQ>#|C>l>;6_mO$kKjzfJ?>y1UmmMKmS+4MnEV2H_)W+vAsh7?}VHU zXnJ$4{7+16d}eba=`P0)Zl0-T#ey*9BVV%Msa2P>5FEm00;k1FNT5VM6A7rIs(O+p zX@#Fd4Tlry5Sp&LGW@y}V0)7M1(sSw!%3`osBSi?6s9ifBF(Oq{KDyshTr@aR4@_| zagU8jL^UX!acm6M0_Fwu7n!u<@c1AMG&jYfw4!)@vLyO7F)Smml3s#mgo0k2cc}RM zA+KN4RL!qQ0`murfD$&mI-W~XPzVHrA;TwyRlzIkswRu!K*$J+UQ(7>Pp7G2xZgRA zR8JVgFrLF!8UfGyQ!H0-9j&n13u1#@mvNsO5C+J*C!mo&FQ5k$`iOcnTQCjtUT@G6 zY6E37V9BI66n36p(W6uD#@8OF>XCV!oBhT>as6)!Tz_JJ1YimM9}u>T|4E{=|Eu4$ zwbOtt{Z9s8G=Xv7**!FO2HzFJCP8^LWC^dN)fSc8vyb(zO@Y?r>+}G8}d{T zXe7}C&5W2AE})ss2&3&h3X@NfLqVBklXx;LE?~FggA1OSW#*~p4FC@)$piC9IFaD% zcw}~Az1F7l#a>=XmZ708iy&VbYjSP|W7^?<6=x;J4*ur2aAG?AFRBA0$wRXQ!n3_!Ch#P zM7%z4F%&Q{G4-SOG@+=1)3i3G#!ApK%u}=Mu$Vj95G0;1x#oWh%rZR z=GOPl8el#Av-pXa7uCSA;NTd->>AHnq0l7Bh#5vTtruJ)Xk!{MxR%}7igVyr#vTEl ztYz+6;L#Z?LlGMlMiZhC>M?U_nmBF9JmX87thGmeVHBgaS`LhQHwALC6|q~PE4kkE z;+T=ObUS4W^JE~TMDota{41=?ssuI$!-5gk-5NtCjoD3CqB?rQo&gFET9|M%Pr|%m z*6#XkVq}Y9w$SdN#v;9L*=d#zlvZ!$7i0cm0nshA z$dkhmNE45t=u9?;Hm|(9HXEkTtH^`(?v^-71SKfIQsujV5NTV%+^Ax4_Q;VV*&*h( zqkI_5%^6sDnEDuQN~ruo>gR{@xh(R%7n#nao`!US8LLb#BgrMh2L5OIL+$`L>l7|D zP!^k?)2$2l1jD1S&*hQ9a3*aU^ zxv9(|tFf_F%dM*sBf8q4h#Z$_Ydck*tQ731$|@>^gZcg-BIsP`r0%^Uwpq|C=wP~i zx@Kupx#jVSsbcb5Zh`ZtntV|-$3fdD;3iL+EvV+pFdDq+JGa6QV8|rA#~Exz%C%)G ztV)VJR}v2u;LRkDi8qTa&}cVQm=AEjBqEx~r%nceIVqPh;-XNZYZJuobj>GNPS^{e zM5;UAOhmpsb9ZD`TeESj+s??!YZf_D~Mf7KgqEPU}91JY{b_ zDxhRf@aFN`K*4KTQr&e*hW1JlVsbF&B&n-H4@2CtUOvm370MrLw^sW_B+4a&(wq?Q z0cq-ea_FA~tiyIh?(0{ap5C$8xJ|&BpD<7I}pkrB9g6Ih^ zVa*?wI_IX9m0x3Osch03&2$G@Q3n@Kfe;**kDc}Nx4lep{||Qr_S*?W22YXm9|Q@9 z{dDvHOM>J79}?EG>;FL_Cv6b1{PYF3H@A`aW_L;&8i73+u`sqBEAcc1`VkD}+x=|& za`JeNHpS%jDVuDF-FGQ;sx0)^;HLykbj)^8t5kflGp3Re@cn&fIPP=|JnIJ>lcQvq>LTF zREr0C^7j0~EyI%{`4R{sX>vF6k@#UwbnuNOqe#r9`gyKFzPzy<)O^2^RS6EbG?I^0 z9WfYk7E!DP_mb@?7!{NfP3xp?`*+cE!46)YXiWX_JZ%H9Z!3k*Jc}l8qgfrQQb7xF zw2o7;Qk0)iuEk^8R(Vop?gr+uDGmls^@+9v0qBoTg|bo+*4 z(+lX_6bi$(sZ6@jO?%yt7fPA3W^S+vwgoGOk=Bs}KvAt+S6E=SXOOK$wfJdX)-gat zNsqPJ+LLmlqiiTFi^{r!4S<9sUf-Ux2%Ul{j4+47S`!JN4Ewxbsan2NsSW`dU<7G+9MN^PJkhX+Z6uNU zu(AX@q~UwS7U_l&3_CS&F@lX~7|}qk=7xni0yZGgnc+5_kOgd8o0Q=l0i1O!=+Goe zc12$ljO@KHV6vJQK`C>ug&TV%SUuw#w=DV=9`lqV^wF*nl_pex4*tF>&Ud3mv9V*B zkOvTBBWqdShPbbDpZI|2$p7X>O3eS1(Pb%$z8?TtV*VE-?-u!gVj$qy|3krd_U3=m ziBNmiMrt$YkL0v=WbdWZ$bf}PF}Z|F#S}U-uMdlUvk98}8tEuK%HnsH_=C&@y~+Qi zSuL>FFg_j8U8;y|J#o@!9nnG{=3&%|Mw5) zc57Q=3%eLW&866t2crgdAh#MF;j@dyZ*RHYfBdw$=4 zo;HS^bfMXv^M8La(3||z91v;kZ|i3LQt~egIRBHs|HBJK;UoF?`JD4V2L|~iS~-fg ze=MMErEDr4?UYe6h_R)Sw1+b%1UgLvM@3*eIDS!9i>oqBp}?_pf;63I4o4vT>Bv7Y zLv-nEv7^kK4dzbZOsPoRjc(d;e8yPH6l!ds%_nW`;d)pg4bey#zodp_C;cH!f=+d8 z=0o98JbBE@&S~U7vbi8;yA2~9L!|_jdw<-U{Bu0_UahPrB>%gBrHGi;n!4h1PFPVH@&K=Z*6dLi;)>`||IT1ibhU z@-M<9aPt59itsY(>7hW_s~Vb5!~0yrUK~)2Q!ET9;7}l>Dqh{EiE2O%1|0b>1F7Gl zqBi8;6n>-uSh+-HkIuIIXXAf3{Q<9mcI3ZoOqX9l3`=1}_xThxGx;u_gcc=YJtfKz989{lK{Ws!~|@1;h}r zuX&B2qy}U`3X7@`k|iyu`wT(xDuNygI`VHHr{AWMMdhD1z9U-O-w(-udH#RD6oC9Y z{{Q}BzI6Q_=-(B1FJ0YMiPHLi%lHqrggW-0E$sHM5GOVcX-eS*+--QTFpJ?}`GhN+ zh%cri%5h{!47&n!NB#E?&EMGCJ;?u-i9G#;zNGwT^FOfuv+}=uq7(n=C)Vv%A6cJX zmUN`s9Hv4h9?yI2iZ_Vc`D(C517i literal 0 HcmV?d00001 From 1f2fdca2c145179085933ca180f44bd3774f6fd8 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Wed, 4 Dec 2013 20:04:02 +0100 Subject: [PATCH 0616/2237] implementing merge with default options - new test --- test/test_repository.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_repository.py b/test/test_repository.py index bcbb48fa3..c44c3df31 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -304,6 +304,9 @@ def test_reset_mixed(self): class RepositoryTest_III(utils.RepoTestCaseForMerging): + def test_merge_none(self): + self.assertRaises(TypeError, self.repo.merge, None) + def test_merge_uptodate(self): branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533' branch_oid = self.repo.get(branch_head_hex).oid From 6855a599e4faa534e0ce24215519d6099c744b57 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Wed, 4 Dec 2013 23:49:26 +0100 Subject: [PATCH 0617/2237] implementing merge with default options - minor change --- src/mergeresult.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mergeresult.c b/src/mergeresult.c index c0ffddd6e..dfad78bc4 100644 --- a/src/mergeresult.c +++ b/src/mergeresult.c @@ -90,7 +90,7 @@ PyDoc_STRVAR(MergeResult_fastforward_oid__doc__, "Fastforward Oid"); PyObject * MergeResult_fastforward_oid__get__(MergeResult *self) { - if (self->is_fastforward == 1) + if (self->is_fastforward) { Py_INCREF(self->fastforward_oid); return self->fastforward_oid; @@ -123,7 +123,7 @@ PyTypeObject MergeResultType = { "_pygit2.MergeResult", /* tp_name */ sizeof(MergeResult), /* tp_basicsize */ 0, /* tp_itemsize */ - 0, /* tp_dealloc */ + 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ From 749810ac77601c8b2010ed0c2063be3880ceaf7f Mon Sep 17 00:00:00 2001 From: Adam Spiers Date: Mon, 9 Dec 2013 11:42:13 +0000 Subject: [PATCH 0618/2237] fix blame argument handling The argument handling for the new Repository_blame had several problems: - The call to PyArg_ParseTupleAndKeywords was missing &path, so none of the optional arguments got parsed correctly. - newest_commit and oldest_commit were missing type validation against OidType. - The opts structure was discarded rather than passed to git_blame_file. This commit fixes these issues and adds a test case. --- src/repository.c | 10 ++++++---- test/test_blame.py | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/repository.c b/src/repository.c index 20e2a7c50..60a687443 100644 --- a/src/repository.c +++ b/src/repository.c @@ -46,6 +46,7 @@ extern PyTypeObject IndexType; extern PyTypeObject WalkerType; extern PyTypeObject SignatureType; extern PyTypeObject ObjectType; +extern PyTypeObject OidType; extern PyTypeObject CommitType; extern PyTypeObject TreeType; extern PyTypeObject TreeBuilderType; @@ -1477,13 +1478,14 @@ Repository_blame(Repository *self, PyObject *args, PyObject *kwds) PyObject *value1 = NULL; PyObject *value2 = NULL; int err; - char *keywords[] = {"flags", "min_match_characters", "newest_commit", + char *keywords[] = {"path", "flags", "min_match_characters", "newest_commit", "oldest_commit", "min_line", "max_line", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|IHOOII", keywords, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|IHO!O!II", keywords, &path, &opts.flags, &opts.min_match_characters, - &value1, &value2, + &OidType, &value1, + &OidType, &value2, &opts.min_line, &opts.max_line)) return NULL; @@ -1498,7 +1500,7 @@ Repository_blame(Repository *self, PyObject *args, PyObject *kwds) return NULL; } - err = git_blame_file(&blame, self->repo, path, NULL); + err = git_blame_file(&blame, self->repo, path, &opts); if (err < 0) return Error_set(err); diff --git a/test/test_blame.py b/test/test_blame.py index 9c04e799b..39dcd863f 100644 --- a/test/test_blame.py +++ b/test/test_blame.py @@ -111,5 +111,32 @@ def test(): self.assertRaises(IndexError, test) + def test_blame_newest(self): + repo = self.repo + + revs = [ + ( 'master^2', 3 ), + ( 'master^2^', 2 ), + ( 'master^2^^', 1 ), + ] + + for rev, num_commits in revs: + commit = repo.revparse_single(rev) + blame = repo.blame(PATH, newest_commit=commit.oid) + + self.assertEqual(len(blame), num_commits) + + for i, hunk in enumerate(tuple(blame)[:num_commits]): + self.assertEqual(hunk.lines_in_hunk, 1) + self.assertEqual(HUNKS[i][0], hunk.final_commit_id) + self.assertEqual(HUNKS[i][1], hunk.final_start_line_number) + self.assertEqualSignature(HUNKS[i][2], hunk.final_committer) + self.assertEqual(hunk.orig_commit_id, + '0000000000000000000000000000000000000000') + self.assertEqual(hunk.orig_path, PATH) + self.assertEqual(HUNKS[i][1], hunk.orig_start_line_number) + self.assertTrue(hunk.orig_committer is None) + self.assertEqual(HUNKS[i][3], hunk.boundary) + if __name__ == '__main__': unittest.main() From 3cd8fed386a3cde6612abc1bd9280991912333a5 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 9 Dec 2013 12:45:51 +0100 Subject: [PATCH 0619/2237] implementing merge - some fixes for the pull request: MergeRestul typedef, pep7 fixes and some error checks --- src/repository.c | 6 ++---- src/types.h | 9 +-------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/repository.c b/src/repository.c index 3d4889c65..81396f9ae 100644 --- a/src/repository.c +++ b/src/repository.c @@ -613,9 +613,8 @@ Repository_merge(Repository *self, PyObject *py_oid) err = git_merge(&merge_result, self->repo, (const git_merge_head **)&oid_merge_head, 1, &default_opts); - if (err < 0) - { - git_merge_result_free(merge_result); + if (err < 0) { + git_merge_head_free(oid_merge_head); goto error; } @@ -627,7 +626,6 @@ Repository_merge(Repository *self, PyObject *py_oid) return py_merge_result; error: - git_merge_head_free(oid_merge_head); return Error_set(err); } diff --git a/src/types.h b/src/types.h index 06504fd36..e69226b73 100644 --- a/src/types.h +++ b/src/types.h @@ -218,13 +218,6 @@ typedef struct { } BlameHunk; /* git_merge */ -typedef struct { - PyObject_HEAD - int is_uptodate; - int is_fastforward; - PyObject* fastforward_oid; - PyObject* status; - -} MergeResult; +SIMPLE_TYPE(MergeResult, git_merge_result, result) #endif From 7d9d2667e59a81514ae93192bc301374f8151fc1 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 9 Dec 2013 15:36:10 +0100 Subject: [PATCH 0620/2237] implementing merge: merge.rst doc and implementing MergeResult as a simple type that maps git_merge_result to it --- docs/merge.rst | 33 +++++++++++++++++++++++ src/mergeresult.c | 60 +++++++++++++++++++++-------------------- src/repository.c | 1 - test/test_repository.py | 26 ++++++++++++------ 4 files changed, 82 insertions(+), 38 deletions(-) diff --git a/docs/merge.rst b/docs/merge.rst index 55e411ac1..f2ee278af 100644 --- a/docs/merge.rst +++ b/docs/merge.rst @@ -2,4 +2,37 @@ Merge ********************************************************************** +.. contents:: + .. automethod:: pygit2.Repository.merge_base +.. automethod:: pygit2.Repository.merge + +The merge method +================= + +The method does a merge over the current working copy. +It gets an Oid object as a parameter and returns a MergeResult object. + +As its name says, it only does the merge, does not commit nor update the +branch reference in the case of a fastforward. + +For the moment, the merge does not support options, it will perform the +merge with the default ones defined in GIT_MERGE_OPTS_INIT libgit2 constant. + +Example:: + + >>> branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533' + >>> branch_oid = self.repo.get(branch_head_hex).oid + >>> merge_result = self.repo.merge(branch_oid) + +The MergeResult object +====================== + +Represents the result of a merge and contains this fields: + +- is_uptodate: bool, if there wasn't any merge because the repo was already + up to date +- is_fastforward: bool, whether the merge was fastforward or not +- fastforward_oid: Oid, in the case it was a fastforward, this is the + forwarded Oid. +- index: represents the repository index after the merge diff --git a/src/mergeresult.c b/src/mergeresult.c index dfad78bc4..1493d6a95 100644 --- a/src/mergeresult.c +++ b/src/mergeresult.c @@ -39,26 +39,14 @@ extern PyTypeObject IndexType; PyObject * git_merge_result_to_python(git_merge_result *merge_result, Repository *repo) { - git_oid fastforward_oid; MergeResult *py_merge_result; py_merge_result = PyObject_New(MergeResult, &MergeResultType); + if (!py_merge_result) + return NULL; - py_merge_result->is_uptodate = git_merge_result_is_uptodate(merge_result) == GIT_CVAR_TRUE; - - if (git_merge_result_is_fastforward(merge_result) == GIT_CVAR_TRUE) - { - py_merge_result->is_fastforward = GIT_CVAR_TRUE; - git_merge_result_fastforward_oid(&fastforward_oid, merge_result); - py_merge_result->fastforward_oid = git_oid_to_python((const git_oid *)&fastforward_oid); - } - else - { - py_merge_result->is_fastforward = GIT_CVAR_FALSE; - py_merge_result->fastforward_oid = NULL; - } - - py_merge_result->status = Repository_status(repo); + py_merge_result->result = merge_result; + py_merge_result->repo = repo; return (PyObject*) py_merge_result; } @@ -68,7 +56,7 @@ PyDoc_STRVAR(MergeResult_is_uptodate__doc__, "Is up to date"); PyObject * MergeResult_is_uptodate__get__(MergeResult *self) { - if (self->is_uptodate) + if (git_merge_result_is_uptodate(self->result)) Py_RETURN_TRUE; else Py_RETURN_FALSE; @@ -79,7 +67,7 @@ PyDoc_STRVAR(MergeResult_is_fastforward__doc__, "Is fastforward"); PyObject * MergeResult_is_fastforward__get__(MergeResult *self) { - if (self->is_fastforward) + if (git_merge_result_is_fastforward(self->result)) Py_RETURN_TRUE; else Py_RETURN_FALSE; @@ -90,29 +78,43 @@ PyDoc_STRVAR(MergeResult_fastforward_oid__doc__, "Fastforward Oid"); PyObject * MergeResult_fastforward_oid__get__(MergeResult *self) { - if (self->is_fastforward) - { - Py_INCREF(self->fastforward_oid); - return self->fastforward_oid; + if (git_merge_result_is_fastforward(self->result)) { + git_oid fastforward_oid; + git_merge_result_fastforward_oid(&fastforward_oid, self->result); + return git_oid_to_python((const git_oid *)&fastforward_oid); } - else - Py_RETURN_NONE; + else Py_RETURN_NONE; } -PyDoc_STRVAR(MergeResult_status__doc__, "Merge repository status"); +PyDoc_STRVAR(MergeResult_index__doc__, "Merge repository index"); PyObject * -MergeResult_status__get__(MergeResult *self) +MergeResult_index__get__(MergeResult *self) { - Py_INCREF(self->status); - return self->status; + git_index *index; + Index *py_index; + int err; + + err = git_repository_index(&index, self->repo->repo); + if (err < 0) + return NULL; + + py_index = PyObject_GC_New(Index, &IndexType); + if (!py_index) { + return NULL; + } + + py_index->repo = self->repo; + py_index->index = index; + Py_INCREF(py_index); + return (PyObject*) py_index; } PyGetSetDef MergeResult_getseters[] = { GETTER(MergeResult, is_uptodate), GETTER(MergeResult, is_fastforward), GETTER(MergeResult, fastforward_oid), - GETTER(MergeResult, status), + GETTER(MergeResult, index), {NULL}, }; diff --git a/src/repository.c b/src/repository.c index 81396f9ae..f590507d8 100644 --- a/src/repository.c +++ b/src/repository.c @@ -621,7 +621,6 @@ Repository_merge(Repository *self, PyObject *py_oid) py_merge_result = git_merge_result_to_python(merge_result, self); git_merge_head_free(oid_merge_head); - git_merge_result_free(merge_result); return py_merge_result; diff --git a/test/test_repository.py b/test/test_repository.py index c44c3df31..0ba57663f 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -314,7 +314,7 @@ def test_merge_uptodate(self): self.assertTrue(merge_result.is_uptodate) self.assertFalse(merge_result.is_fastforward) self.assertEquals(None, merge_result.fastforward_oid) - self.assertEquals({}, merge_result.status) + self.assertEquals({}, self.repo.status()) def test_merge_fastforward(self): branch_head_hex = 'e97b4cfd5db0fb4ebabf4f203979ca4e5d1c7c87' @@ -325,7 +325,7 @@ def test_merge_fastforward(self): # Asking twice to assure the reference counting is correct self.assertEquals(branch_head_hex, merge_result.fastforward_oid.hex) self.assertEquals(branch_head_hex, merge_result.fastforward_oid.hex) - self.assertEquals({}, merge_result.status) + self.assertEquals({}, self.repo.status()) def test_merge_no_fastforward_no_conflicts(self): branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' @@ -333,10 +333,15 @@ def test_merge_no_fastforward_no_conflicts(self): merge_result = self.repo.merge(branch_oid) self.assertFalse(merge_result.is_uptodate) self.assertFalse(merge_result.is_fastforward) - self.assertEquals(None, merge_result.fastforward_oid) # Asking twice to assure the reference counting is correct - self.assertEquals({'bye.txt': 1}, merge_result.status) - self.assertEquals({'bye.txt': 1}, merge_result.status) + self.assertEquals(None, merge_result.fastforward_oid) + self.assertEquals(None, merge_result.fastforward_oid) + self.assertEquals({'bye.txt': 1}, self.repo.status()) + self.assertEquals({'bye.txt': 1}, self.repo.status()) + # Checking the index works as expected + merge_result.index.remove('bye.txt') + merge_result.index.write() + self.assertEquals({'bye.txt': 128}, self.repo.status()) def test_merge_no_fastforward_conflicts(self): branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' @@ -344,10 +349,15 @@ def test_merge_no_fastforward_conflicts(self): merge_result = self.repo.merge(branch_oid) self.assertFalse(merge_result.is_uptodate) self.assertFalse(merge_result.is_fastforward) - self.assertEquals(None, merge_result.fastforward_oid) # Asking twice to assure the reference counting is correct - self.assertEquals({'.gitignore': 132}, merge_result.status) - self.assertEquals({'.gitignore': 132}, merge_result.status) + self.assertEquals(None, merge_result.fastforward_oid) + self.assertEquals(None, merge_result.fastforward_oid) + self.assertEquals({'.gitignore': 132}, self.repo.status()) + self.assertEquals({'.gitignore': 132}, self.repo.status()) + # Checking the index works as expected + merge_result.index.add('.gitignore') + merge_result.index.write() + self.assertEquals({'.gitignore': 2}, self.repo.status()) def test_merge_invalid_hex(self): branch_head_hex = '12345678' From 1cf6e748e539fa3f510e39cbde28174431940855 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 9 Dec 2013 15:51:29 +0100 Subject: [PATCH 0621/2237] implementing merge: some small fixes in merge.rst --- docs/merge.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/merge.rst b/docs/merge.rst index f2ee278af..ec04c09ff 100644 --- a/docs/merge.rst +++ b/docs/merge.rst @@ -28,11 +28,11 @@ Example:: The MergeResult object ====================== -Represents the result of a merge and contains this fields: +Represents the result of a merge and contains these fields: - is_uptodate: bool, if there wasn't any merge because the repo was already - up to date + up to date - is_fastforward: bool, whether the merge was fastforward or not - fastforward_oid: Oid, in the case it was a fastforward, this is the - forwarded Oid. + forwarded Oid. - index: represents the repository index after the merge From b8e72fc2721f71605421761f09abe5fff788aed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 10 Dec 2013 13:41:51 +0100 Subject: [PATCH 0622/2237] Issue#298: Fix compilation on Windows --- src/branch.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/branch.c b/src/branch.c index ae547fe30..483906b82 100644 --- a/src/branch.c +++ b/src/branch.c @@ -137,6 +137,7 @@ Branch_remote_name__get__(Branch *self) int err; const char *branch_name; char *c_name = NULL; + PyObject *py_name; CHECK_REFERENCE(self); @@ -160,7 +161,7 @@ Branch_remote_name__get__(Branch *self) return Error_set(err); } - PyObject *py_name = to_unicode(c_name, NULL, NULL); + py_name = to_unicode(c_name, NULL, NULL); free(c_name); return py_name; @@ -228,6 +229,7 @@ Branch_upstream_name__get__(Branch *self) int err; const char *branch_name; char *c_name = NULL; + PyObject *py_name; CHECK_REFERENCE(self); @@ -251,7 +253,7 @@ Branch_upstream_name__get__(Branch *self) return Error_set(err); } - PyObject *py_name = to_unicode(c_name, NULL, NULL); + py_name = to_unicode(c_name, NULL, NULL); free(c_name); return py_name; From 0556ab0cd8828d2876307d0cd889dcfa9797d39b Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 10 Dec 2013 16:01:48 +0100 Subject: [PATCH 0623/2237] implementing merge: removing index field from MergeResult --- src/mergeresult.c | 25 ------------------------- test/test_repository.py | 8 ++++---- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/src/mergeresult.c b/src/mergeresult.c index 1493d6a95..1ce4497e0 100644 --- a/src/mergeresult.c +++ b/src/mergeresult.c @@ -86,35 +86,10 @@ MergeResult_fastforward_oid__get__(MergeResult *self) else Py_RETURN_NONE; } -PyDoc_STRVAR(MergeResult_index__doc__, "Merge repository index"); - -PyObject * -MergeResult_index__get__(MergeResult *self) -{ - git_index *index; - Index *py_index; - int err; - - err = git_repository_index(&index, self->repo->repo); - if (err < 0) - return NULL; - - py_index = PyObject_GC_New(Index, &IndexType); - if (!py_index) { - return NULL; - } - - py_index->repo = self->repo; - py_index->index = index; - Py_INCREF(py_index); - return (PyObject*) py_index; -} - PyGetSetDef MergeResult_getseters[] = { GETTER(MergeResult, is_uptodate), GETTER(MergeResult, is_fastforward), GETTER(MergeResult, fastforward_oid), - GETTER(MergeResult, index), {NULL}, }; diff --git a/test/test_repository.py b/test/test_repository.py index 0ba57663f..11ebc4de3 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -339,8 +339,8 @@ def test_merge_no_fastforward_no_conflicts(self): self.assertEquals({'bye.txt': 1}, self.repo.status()) self.assertEquals({'bye.txt': 1}, self.repo.status()) # Checking the index works as expected - merge_result.index.remove('bye.txt') - merge_result.index.write() + self.repo.index.remove('bye.txt') + self.repo.index.write() self.assertEquals({'bye.txt': 128}, self.repo.status()) def test_merge_no_fastforward_conflicts(self): @@ -355,8 +355,8 @@ def test_merge_no_fastforward_conflicts(self): self.assertEquals({'.gitignore': 132}, self.repo.status()) self.assertEquals({'.gitignore': 132}, self.repo.status()) # Checking the index works as expected - merge_result.index.add('.gitignore') - merge_result.index.write() + self.repo.index.add('.gitignore') + self.repo.index.write() self.assertEquals({'.gitignore': 2}, self.repo.status()) def test_merge_invalid_hex(self): From 1938147b3dfc1103ee88ea26af24441ff63019c3 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 10 Dec 2013 16:02:37 +0100 Subject: [PATCH 0624/2237] implementing merge: removing index field from merge.rst --- docs/merge.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/merge.rst b/docs/merge.rst index ec04c09ff..82c243a7d 100644 --- a/docs/merge.rst +++ b/docs/merge.rst @@ -35,4 +35,3 @@ Represents the result of a merge and contains these fields: - is_fastforward: bool, whether the merge was fastforward or not - fastforward_oid: Oid, in the case it was a fastforward, this is the forwarded Oid. -- index: represents the repository index after the merge From 7e2cfd7a70def3ac824513ec2d9843dd2c9790a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 10 Dec 2013 21:00:44 +0100 Subject: [PATCH 0625/2237] Remove trailing whitespace --- test/test_repository.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_repository.py b/test/test_repository.py index 11ebc4de3..53dc00d15 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -341,7 +341,7 @@ def test_merge_no_fastforward_no_conflicts(self): # Checking the index works as expected self.repo.index.remove('bye.txt') self.repo.index.write() - self.assertEquals({'bye.txt': 128}, self.repo.status()) + self.assertEquals({'bye.txt': 128}, self.repo.status()) def test_merge_no_fastforward_conflicts(self): branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' @@ -357,7 +357,7 @@ def test_merge_no_fastforward_conflicts(self): # Checking the index works as expected self.repo.index.add('.gitignore') self.repo.index.write() - self.assertEquals({'.gitignore': 2}, self.repo.status()) + self.assertEquals({'.gitignore': 2}, self.repo.status()) def test_merge_invalid_hex(self): branch_head_hex = '12345678' From 937e2822a834a50160b8ca4e56ed4163bd803b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 10 Dec 2013 21:05:52 +0100 Subject: [PATCH 0626/2237] merge: remove unused reference to the repository --- src/mergeresult.c | 3 +-- src/mergeresult.h | 2 +- src/repository.c | 15 ++++----------- src/types.h | 5 ++++- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/mergeresult.c b/src/mergeresult.c index 1ce4497e0..31699c1ca 100644 --- a/src/mergeresult.c +++ b/src/mergeresult.c @@ -37,7 +37,7 @@ extern PyTypeObject MergeResultType; extern PyTypeObject IndexType; PyObject * -git_merge_result_to_python(git_merge_result *merge_result, Repository *repo) +git_merge_result_to_python(git_merge_result *merge_result) { MergeResult *py_merge_result; @@ -46,7 +46,6 @@ git_merge_result_to_python(git_merge_result *merge_result, Repository *repo) return NULL; py_merge_result->result = merge_result; - py_merge_result->repo = repo; return (PyObject*) py_merge_result; } diff --git a/src/mergeresult.h b/src/mergeresult.h index 7aadac4aa..770a4bd40 100644 --- a/src/mergeresult.h +++ b/src/mergeresult.h @@ -32,6 +32,6 @@ #include #include -PyObject* git_merge_result_to_python(git_merge_result *merge_result, Repository *repo); +PyObject* git_merge_result_to_python(git_merge_result *merge_result); #endif diff --git a/src/repository.c b/src/repository.c index 2b15993d2..7cc746b75 100644 --- a/src/repository.c +++ b/src/repository.c @@ -609,24 +609,17 @@ Repository_merge(Repository *self, PyObject *py_oid) err = git_merge_head_from_oid(&oid_merge_head, self->repo, &oid); if (err < 0) - goto error; + return Error_set(err); err = git_merge(&merge_result, self->repo, (const git_merge_head **)&oid_merge_head, 1, &default_opts); - if (err < 0) { - git_merge_head_free(oid_merge_head); - goto error; - } - - py_merge_result = git_merge_result_to_python(merge_result, self); - git_merge_head_free(oid_merge_head); + if (err < 0) + return Error_set(err); + py_merge_result = git_merge_result_to_python(merge_result); return py_merge_result; - -error: - return Error_set(err); } PyDoc_STRVAR(Repository_walk__doc__, diff --git a/src/types.h b/src/types.h index e69226b73..c86495992 100644 --- a/src/types.h +++ b/src/types.h @@ -218,6 +218,9 @@ typedef struct { } BlameHunk; /* git_merge */ -SIMPLE_TYPE(MergeResult, git_merge_result, result) +typedef struct { + PyObject_HEAD + git_merge_result *result; +} MergeResult; #endif From c6cdbba53dccc50ba801c24c73ebf2261eaa838e Mon Sep 17 00:00:00 2001 From: Eric Schrijver Date: Sat, 14 Dec 2013 16:06:40 +0100 Subject: [PATCH 0627/2237] =?UTF-8?q?Typo=20in=20README.rst:=20Blog=20?= =?UTF-8?q?=E2=86=92=20Blob?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 4345acb22..ed0a3b103 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,3 @@ - ###################################################################### pygit2 - libgit2 bindings in Python ###################################################################### @@ -107,7 +106,7 @@ New API: - ``Reference.log_append(...)`` - ``Reference.shorthand`` - - ``Blog.is_binary`` + - ``Blob.is_binary`` - ``len(Diff)`` - ``Patch.additions`` - ``Patch.deletions`` From fafb4b90725bbd86ccb8780819f4679dff257db9 Mon Sep 17 00:00:00 2001 From: XTao Date: Mon, 16 Dec 2013 16:30:33 +0800 Subject: [PATCH 0628/2237] Fix no patch diff. --- src/diff.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/diff.c b/src/diff.c index b7bacd58d..75df6acd9 100644 --- a/src/diff.c +++ b/src/diff.c @@ -290,6 +290,8 @@ Diff_patch__get__(Diff *self) PyObject *py_patch = NULL; num = git_diff_num_deltas(self->list); + if (num == 0) + Py_RETURN_NONE; MALLOC(strings, num * sizeof(char*), cleanup); for (i = 0, len = 1; i < num ; ++i) { From 19b15ed928a26b4323d699dd1a048be30b626b57 Mon Sep 17 00:00:00 2001 From: Petr Hosek Date: Mon, 16 Dec 2013 11:14:06 +0000 Subject: [PATCH 0629/2237] Provide Walker simplify_first_parent method --- src/walker.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/walker.c b/src/walker.c index a02b2f45c..79cf389bf 100644 --- a/src/walker.c +++ b/src/walker.c @@ -122,6 +122,18 @@ Walker_reset(Walker *self) Py_RETURN_NONE; } +PyDoc_STRVAR(Walker_simplify_first_parent__doc__, + "simplify_first_parent()\n" + "\n" + "Simplify the history by first-parent."); + +PyObject * +Walker_simplify_first_parent(Walker *self) +{ + git_revwalk_simplify_first_parent(self->walk); + Py_RETURN_NONE; +} + PyObject * Walker_iter(Walker *self) { @@ -158,6 +170,7 @@ PyMethodDef Walker_methods[] = { METHOD(Walker, hide, METH_O), METHOD(Walker, push, METH_O), METHOD(Walker, reset, METH_NOARGS), + METHOD(Walker, simplify_first_parent, METH_NOARGS), METHOD(Walker, sort, METH_O), {NULL} }; From 3cc0662defac69964751bad7fa273157c32affcf Mon Sep 17 00:00:00 2001 From: Petr Hosek Date: Fri, 20 Dec 2013 13:28:20 +0000 Subject: [PATCH 0630/2237] Test case for Walker simplify_first_parent --- test/test_revwalk.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test_revwalk.py b/test/test_revwalk.py index 119f22253..654ef47a8 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -102,6 +102,10 @@ def test_sort(self): walker.sort(GIT_SORT_TIME | GIT_SORT_REVERSE) self.assertEqual([x.hex for x in walker], list(reversed(log))) + def test_simplify_first_parent(self): + walker = self.repo.walk(log[0], GIT_SORT_TIME) + walker.simplify_first_parent() + self.assertEqual(len(list(walker)), 3) if __name__ == '__main__': unittest.main() From 46543a85d32f42de3f73555d5660478f7a13a3ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 20 Dec 2013 19:00:58 +0100 Subject: [PATCH 0631/2237] Docs: Fix and add missing stuff --- docs/remotes.rst | 8 ++++---- docs/repository.rst | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/remotes.rst b/docs/remotes.rst index 12a1755b2..3542f699b 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -13,10 +13,10 @@ The Remote type .. autoattribute:: pygit2.Remote.name .. autoattribute:: pygit2.Remote.url .. autoattribute:: pygit2.Remote.refspec_count -.. automethod:: pygit2.Remote.get_push_refspec -.. automethod:: pygit2.Remote.get_pull_refspec -.. automethod:: pygit2.Remote.set_push_refspec -.. automethod:: pygit2.Remote.set_pull_refspec +.. automethod:: pygit2.Remote.get_push_refspecs +.. automethod:: pygit2.Remote.get_fetch_refspecs +.. automethod:: pygit2.Remote.set_push_refspecs +.. automethod:: pygit2.Remote.set_fetch_refspecs .. automethod:: pygit2.Remote.get_refspec .. automethod:: pygit2.Remote.fetch .. automethod:: pygit2.Remote.push diff --git a/docs/repository.rst b/docs/repository.rst index 034ed5692..ca08f1f4d 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -61,3 +61,4 @@ Below there are some general attributes and methods: .. autoattribute:: pygit2.Repository.is_empty .. automethod:: pygit2.Repository.read .. automethod:: pygit2.Repository.write +.. automethod:: pygit2.Repository.reset From 9a5184ba35148b3e31f0b43e53fa4bde79812ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 23 Dec 2013 17:26:02 +0100 Subject: [PATCH 0632/2237] Add documentation for the Walker type --- docs/log.rst | 7 +++++++ src/pygit2.c | 1 + 2 files changed, 8 insertions(+) diff --git a/docs/log.rst b/docs/log.rst index c63bc41c6..440d54b8c 100644 --- a/docs/log.rst +++ b/docs/log.rst @@ -3,3 +3,10 @@ Commit log ********************************************************************** .. automethod:: pygit2.Repository.walk + + +.. automethod:: pygit2.Walker.hide +.. automethod:: pygit2.Walker.push +.. automethod:: pygit2.Walker.reset +.. automethod:: pygit2.Walker.sort +.. automethod:: pygit2.Walker.simplify_first_parent diff --git a/src/pygit2.c b/src/pygit2.c index e1766d26e..e640ea8e0 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -300,6 +300,7 @@ moduleinit(PyObject* m) * Log */ INIT_TYPE(WalkerType, NULL, PyType_GenericNew) + ADD_TYPE(m, Walker); ADD_CONSTANT_INT(m, GIT_SORT_NONE) ADD_CONSTANT_INT(m, GIT_SORT_TOPOLOGICAL) ADD_CONSTANT_INT(m, GIT_SORT_TIME) From 4bbc59c574869a6dc24276a61fa50b070cbbd5b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 24 Dec 2013 10:08:36 +0100 Subject: [PATCH 0633/2237] Update changelog --- README.rst | 88 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/README.rst b/README.rst index ed0a3b103..bd2481384 100644 --- a/README.rst +++ b/README.rst @@ -77,40 +77,84 @@ Authors Changelog ============== +0.20.1 (201X-XX-XX) +------------------- + +- New remote ref-specs API: + `#290 `_ + +- New ``Repository.reset(...)``: + `#292 `_, + `#294 `_ + +- Export ``GIT_DIFF_MINIMAL``: + `#293 `_ + +- New ``Repository.merge(...)``: + `#295 `_ + +- Fix ``Repository.blame`` argument handling: + `#297 `_ + +- Fix build error on Windows: + `#298 `_ + +- Fix typo in the README file, Blog → Blob: + `#301 `_ + +- Now ``Diff.patch`` returns ``None`` if no patch: + `#232 `_, + `#303 `_ + +- New ``Walker.simplify_first_parent()``: + `#304 `_ + 0.20.0 (2013-11-24) ------------------- -API changes: +- Upgrade to libgit2 v0.20.0: + `#288 `_ + + Rename ``Repository.head_is_orphaned`` to ``Repository.head_is_unborn`` + + Prototype of ``pygit2.clone_repository(...)`` changed:: + + # Before + pygit2.clone_repository(url, path, bare=False, remote_name='origin', + push_url=None, fetch_spec=None, push_spec=None, + checkout_branch=None) + + # Now + pygit2.clone_repository(url, path, bare=False, ignore_cert_errors=False, + remote_name='origin', checkout_branch=None) -- Renamed ``Repository.head_is_orphaned`` to ``Repository.head_is_unborn`` +- New ``Patch.additions`` and ``Patch.deletions``: + `#275 `_ -- ``Repository.listall_references`` and ``Repository.listall_branches`` now - return a list, instead of a tuple +- New ``Patch.is_binary``: + `#276 `_ -- The prototype of ``clone_repository`` changed from:: +- New ``Reference.log_append(...)``: + `#277 `_ - # Before - pygit2.clone_repository(url, path, bare=False, remote_name='origin', - push_url=None, fetch_spec=None, push_spec=None, - checkout_branch=None) +- New ``Blob.is_binary``: + `#278 `_ - # Now - pygit2.clone_repository(url, path, bare=False, ignore_cert_errors=False, - remote_name='origin', checkout_branch=None) +- New ``len(Diff)`` shows the number of patches: + `#281 `_ -New API: +- Rewrite ``Repository.status()``: + `#283 `_ -- Added support for blame +- New ``Reference.shorthand``: + `#284 `_ -- New: +- New ``Repository.blame(...)``: + `#285 `_ - - ``Reference.log_append(...)`` - - ``Reference.shorthand`` - - ``Blob.is_binary`` - - ``len(Diff)`` - - ``Patch.additions`` - - ``Patch.deletions`` - - ``Patch.is_binary`` +- Now ``Repository.listall_references()`` and + ``Repository.listall_branches()`` return a list, not a tuple: + `#289 `_ License From d04823c3c87ae50f76842402ad04dc0cd1863029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 24 Dec 2013 10:48:39 +0100 Subject: [PATCH 0634/2237] Get ready to release 0.20.1 --- .mailmap | 5 +++++ README.rst | 44 +++++++++++++++++++++++--------------------- docs/conf.py | 2 +- docs/install.rst | 2 +- pygit2/version.py | 2 +- 5 files changed, 31 insertions(+), 24 deletions(-) diff --git a/.mailmap b/.mailmap index 453de866a..710e4ef05 100644 --- a/.mailmap +++ b/.mailmap @@ -1,9 +1,14 @@ Carlos Martín Nieto Christian Boos +Gustavo Di Pietro J. David Ibáñez Martin Lenders +Óscar San José Richo Healey Xavier Delannoy Xu Tao Xu Tao + + + diff --git a/README.rst b/README.rst index bd2481384..5d116a7e0 100644 --- a/README.rst +++ b/README.rst @@ -44,40 +44,42 @@ for the topic), send a pull request. Authors ============== -52 developers have contributed at least 1 commit to pygit2:: +56 developers have contributed at least 1 commit to pygit2:: J. David Ibáñez Andrey Devyatkin Nico von Geyso Ben Davis - Carlos Martín Nieto Hervé Cauwelier - W. Trevor King Huang Huang - Dave Borowitz Jared Flatow - Daniel Rodríguez Troitiño Jiunn Haur Lim - Richo Healey Sarath Lakshman - Christian Boos Vicent Marti - Julien Miotte Zoran Zaric - Martin Lenders Andrew Chin + Carlos Martín Nieto Eric Schrijver + W. Trevor King Hervé Cauwelier + Dave Borowitz Huang Huang + Daniel Rodríguez Troitiño Jared Flatow + Richo Healey Jiunn Haur Lim + Christian Boos Sarath Lakshman + Julien Miotte Vicent Marti + Jose Plana Zoran Zaric + Martin Lenders Adam Spiers + Victor Garcia Andrew Chin Xavier Delannoy András Veres-Szentkirályi Yonggang Luo Benjamin Kircher - Valentin Haenel Benjamin Pollack - Xu Tao Bryan O'Sullivan - Bernardo Heynemann David Fischer - John Szakmeister David Sanders - Brodie Rao Eric Davis - Petr Hosek Eric Schrijver - David Versmisse Erik van Zijst - Rémi Duraffort Ferengee + Petr Hosek Benjamin Pollack + Valentin Haenel Bryan O'Sullivan + Xu Tao David Fischer + Bernardo Heynemann David Sanders + John Szakmeister Eric Davis + Brodie Rao Erik van Zijst + David Versmisse Ferengee + Rémi Duraffort Gustavo Di Pietro Sebastian Thiel Hugh Cole-Baker Fraser Tweedale Josh Bleecher Snyder Han-Wen Nienhuys Jun Omae - Petr Viktorin Ridge Kennedy - Alex Chamberlain Rui Abreu Ferreira - Amit Bakshi pistacchio + Petr Viktorin Óscar San José + Alex Chamberlain Ridge Kennedy + Amit Bakshi Rui Abreu Ferreira Changelog ============== -0.20.1 (201X-XX-XX) +0.20.1 (2013-12-24) ------------------- - New remote ref-specs API: diff --git a/docs/conf.py b/docs/conf.py index 92253aaf7..7ec185961 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,7 +50,7 @@ # The short X.Y version. version = '0.20' # The full version, including alpha/beta/rc tags. -release = '0.20.0' +release = '0.20.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/install.rst b/docs/install.rst index 95cba2b9a..e237037a2 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -27,7 +27,7 @@ When those are installed, you can install pygit2: .. note:: A minor version of pygit2 must be used with the corresponding minor version of libgit2. For example, pygit2 v0.20.x must be used with libgit2 - v0.20.0. + v0.20.1. Building on \*nix (including OS X) =================================== diff --git a/pygit2/version.py b/pygit2/version.py index 428045a6d..9469822bf 100644 --- a/pygit2/version.py +++ b/pygit2/version.py @@ -23,4 +23,4 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -__version__ = '0.20.0' +__version__ = '0.20.1' From cde2456327197ff479add30a40576f427e12618a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 24 Dec 2013 10:51:02 +0100 Subject: [PATCH 0635/2237] tests: fix deprecation warning --- test/test_repository.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/test_repository.py b/test/test_repository.py index 53dc00d15..281630d1e 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -313,8 +313,8 @@ def test_merge_uptodate(self): merge_result = self.repo.merge(branch_oid) self.assertTrue(merge_result.is_uptodate) self.assertFalse(merge_result.is_fastforward) - self.assertEquals(None, merge_result.fastforward_oid) - self.assertEquals({}, self.repo.status()) + self.assertEqual(None, merge_result.fastforward_oid) + self.assertEqual({}, self.repo.status()) def test_merge_fastforward(self): branch_head_hex = 'e97b4cfd5db0fb4ebabf4f203979ca4e5d1c7c87' @@ -323,9 +323,9 @@ def test_merge_fastforward(self): self.assertFalse(merge_result.is_uptodate) self.assertTrue(merge_result.is_fastforward) # Asking twice to assure the reference counting is correct - self.assertEquals(branch_head_hex, merge_result.fastforward_oid.hex) - self.assertEquals(branch_head_hex, merge_result.fastforward_oid.hex) - self.assertEquals({}, self.repo.status()) + self.assertEqual(branch_head_hex, merge_result.fastforward_oid.hex) + self.assertEqual(branch_head_hex, merge_result.fastforward_oid.hex) + self.assertEqual({}, self.repo.status()) def test_merge_no_fastforward_no_conflicts(self): branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' @@ -334,14 +334,14 @@ def test_merge_no_fastforward_no_conflicts(self): self.assertFalse(merge_result.is_uptodate) self.assertFalse(merge_result.is_fastforward) # Asking twice to assure the reference counting is correct - self.assertEquals(None, merge_result.fastforward_oid) - self.assertEquals(None, merge_result.fastforward_oid) - self.assertEquals({'bye.txt': 1}, self.repo.status()) - self.assertEquals({'bye.txt': 1}, self.repo.status()) + self.assertEqual(None, merge_result.fastforward_oid) + self.assertEqual(None, merge_result.fastforward_oid) + self.assertEqual({'bye.txt': 1}, self.repo.status()) + self.assertEqual({'bye.txt': 1}, self.repo.status()) # Checking the index works as expected self.repo.index.remove('bye.txt') self.repo.index.write() - self.assertEquals({'bye.txt': 128}, self.repo.status()) + self.assertEqual({'bye.txt': 128}, self.repo.status()) def test_merge_no_fastforward_conflicts(self): branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' @@ -350,14 +350,14 @@ def test_merge_no_fastforward_conflicts(self): self.assertFalse(merge_result.is_uptodate) self.assertFalse(merge_result.is_fastforward) # Asking twice to assure the reference counting is correct - self.assertEquals(None, merge_result.fastforward_oid) - self.assertEquals(None, merge_result.fastforward_oid) - self.assertEquals({'.gitignore': 132}, self.repo.status()) - self.assertEquals({'.gitignore': 132}, self.repo.status()) + self.assertEqual(None, merge_result.fastforward_oid) + self.assertEqual(None, merge_result.fastforward_oid) + self.assertEqual({'.gitignore': 132}, self.repo.status()) + self.assertEqual({'.gitignore': 132}, self.repo.status()) # Checking the index works as expected self.repo.index.add('.gitignore') self.repo.index.write() - self.assertEquals({'.gitignore': 2}, self.repo.status()) + self.assertEqual({'.gitignore': 2}, self.repo.status()) def test_merge_invalid_hex(self): branch_head_hex = '12345678' From 78d134c016f88921dc54c3f237107fbd8534934d Mon Sep 17 00:00:00 2001 From: Brodie Rao Date: Wed, 8 Jan 2014 15:14:27 -0800 Subject: [PATCH 0636/2237] repository: add listall_reference_objects() method This allows for efficient reading of many references and their targets, without incurring the overhead of lookup_reference() (which stats for a loose ref and then reads packed-refs) which can be expensive on NFS with thousands of refs. --- docs/references.rst | 1 + src/repository.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ src/repository.h | 2 ++ test/test_refs.py | 11 ++++++++++ 4 files changed, 65 insertions(+) diff --git a/docs/references.rst b/docs/references.rst index 339139230..0682f0ecd 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -4,6 +4,7 @@ References .. contents:: +.. automethod:: pygit2.Repository.listall_reference_objects .. automethod:: pygit2.Repository.listall_references .. automethod:: pygit2.Repository.lookup_reference diff --git a/src/repository.c b/src/repository.c index f8d42a7af..e83302c48 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1123,6 +1123,56 @@ Repository_listall_references(Repository *self, PyObject *args) } +PyDoc_STRVAR(Repository_listall_reference_objects__doc__, + "listall_reference_objects() -> [Reference, ...]\n" + "\n" + "Return a list with all the reference objects in the repository."); + +PyObject * +Repository_listall_reference_objects(Repository *self, PyObject *args) +{ + git_reference_iterator *iter; + git_reference *ref = NULL; + int err; + PyObject *list; + + list = PyList_New(0); + if (list == NULL) + return NULL; + + if ((err = git_reference_iterator_new(&iter, self->repo)) < 0) + return Error_set(err); + + while ((err = git_reference_next(&ref, iter)) == 0) { + PyObject *py_ref = wrap_reference(ref, self); + if (py_ref == NULL) + goto error; + + err = PyList_Append(list, py_ref); + Py_DECREF(py_ref); + + if (err < 0) + goto error; + } + + git_reference_iterator_free(iter); + if (err == GIT_ITEROVER) + err = 0; + + if (err < 0) { + Py_CLEAR(list); + return Error_set(err); + } + + return list; + +error: + git_reference_iterator_free(iter); + Py_CLEAR(list); + return NULL; +} + + PyDoc_STRVAR(Repository_listall_branches__doc__, "listall_branches([flag]) -> [str, ...]\n" "\n" @@ -1643,6 +1693,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, create_reference_direct, METH_VARARGS), METHOD(Repository, create_reference_symbolic, METH_VARARGS), METHOD(Repository, listall_references, METH_NOARGS), + METHOD(Repository, listall_reference_objects, METH_NOARGS), METHOD(Repository, listall_submodules, METH_NOARGS), METHOD(Repository, lookup_reference, METH_O), METHOD(Repository, revparse_single, METH_O), diff --git a/src/repository.h b/src/repository.h index 694726982..bc6cc38b3 100644 --- a/src/repository.h +++ b/src/repository.h @@ -58,6 +58,8 @@ PyObject* Repository_create_commit(Repository *self, PyObject *args); PyObject* Repository_create_tag(Repository *self, PyObject *args); PyObject* Repository_create_branch(Repository *self, PyObject *args); PyObject* Repository_listall_references(Repository *self, PyObject *args); +PyObject* Repository_listall_reference_objects(Repository *self, + PyObject *args); PyObject* Repository_listall_branches(Repository *self, PyObject *args); PyObject* Repository_lookup_reference(Repository *self, PyObject *py_name); diff --git a/test/test_refs.py b/test/test_refs.py index 53de205e3..9429146c4 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -42,6 +42,17 @@ class ReferencesTest(utils.RepoTestCase): + def test_list_all_reference_objects(self): + repo = self.repo + + refs = [(ref.name, ref.target.hex) + for ref in repo.listall_reference_objects()] + self.assertEqual(sorted(refs), + [('refs/heads/i18n', + '5470a671a80ac3789f1a6a8cefbcf43ce7af0563'), + ('refs/heads/master', + '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98')]) + def test_list_all_references(self): repo = self.repo From dcc9051a8c9978b2351995e5fe90fb20fa1e571e Mon Sep 17 00:00:00 2001 From: Petr Hosek Date: Wed, 15 Jan 2014 15:40:04 +0000 Subject: [PATCH 0637/2237] Support diff for blobs --- src/blob.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++- src/diff.c | 74 ++++++++++++++++++++---------------- src/diff.h | 1 + test/test_blob.py | 11 ++++++ 4 files changed, 149 insertions(+), 34 deletions(-) diff --git a/src/blob.c b/src/blob.c index 83c7ece55..1b2840f71 100644 --- a/src/blob.c +++ b/src/blob.c @@ -27,10 +27,105 @@ #define PY_SSIZE_T_CLEAN #include +#include "diff.h" +#include "error.h" #include "utils.h" #include "object.h" #include "blob.h" +extern PyObject *GitError; + +extern PyTypeObject BlobType; + +PyDoc_STRVAR(Blob_diff__doc__, + "diff([blob, flag, old_as_path, new_as_path] -> Patch\n" + "\n" + "Directly generate a :py:class:`pygit2.Patch` from the difference\n" + " between two blobs.\n" + "\n" + "Arguments:\n" + "\n" + "blob: the :py:class:`~pygit2.Blob` to diff.\n" + "\n" + "flag: a GIT_DIFF_* constant.\n" + "\n" + "old_as_path: treat old blob as if it had this filename.\n" + "\n" + "new_as_path: treat new blob as if it had this filename.\n"); + +PyObject * +Blob_diff(Blob *self, PyObject *args, PyObject *kwds) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_patch *patch; + char *old_as_path = NULL, *new_as_path = NULL; + Blob *py_blob = NULL; + int err; + char *keywords[] = {"blob", "flag", "old_as_path", "new_as_path", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!ssI", keywords, + &BlobType, &py_blob, &opts.flags, + &old_as_path, &new_as_path)) + return NULL; + + err = git_patch_from_blobs(&patch, self->blob, old_as_path, + py_blob ? py_blob->blob : NULL, new_as_path, + &opts); + if (err < 0) + return Error_set(err); + + return wrap_patch(patch); +} + + +PyDoc_STRVAR(Blob_diff_to_buffer__doc__, + "diff_to_buffer([buffer, flag, old_as_path, buffer_as_path] -> Patch\n" + "\n" + "Directly generate a :py:class:`~pygit2.Patch` from the difference\n" + " between a blob and a buffer.\n" + "\n" + "Arguments:\n" + "\n" + "buffer: Raw data for new side of diff.\n" + "\n" + "flag: a GIT_DIFF_* constant.\n" + "\n" + "old_as_path: treat old blob as if it had this filename.\n" + "\n" + "buffer_as_path: treat buffer as if it had this filename.\n"); + +PyObject * +Blob_diff_to_buffer(Blob *self, PyObject *args, PyObject *kwds) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_patch *patch; + char *old_as_path = NULL, *buffer_as_path = NULL; + const char *buffer = NULL; + Py_ssize_t buffer_len; + int err; + char *keywords[] = {"buffer", "flag", "old_as_path", "buffer_as_path", + NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s#ssI", keywords, + &buffer, &buffer_len, &opts.flags, + &old_as_path, &buffer_as_path)) + return NULL; + + err = git_patch_from_blob_and_buffer(&patch, self->blob, old_as_path, + buffer, buffer_len, buffer_as_path, + &opts); + if (err < 0) + return Error_set(err); + + return wrap_patch(patch); +} + +static PyMethodDef Blob_methods[] = { + METHOD(Blob, diff, METH_VARARGS | METH_KEYWORDS), + METHOD(Blob, diff_to_buffer, METH_VARARGS | METH_KEYWORDS), + {NULL} +}; + PyDoc_STRVAR(Blob_size__doc__, "Size."); @@ -94,7 +189,7 @@ PyTypeObject BlobType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + Blob_methods, /* tp_methods */ 0, /* tp_members */ Blob_getseters, /* tp_getset */ 0, /* tp_base */ diff --git a/src/diff.c b/src/diff.c index 75df6acd9..1609a7d65 100644 --- a/src/diff.c +++ b/src/diff.c @@ -57,27 +57,24 @@ wrap_diff(git_diff *diff, Repository *repo) return (PyObject*) py_diff; } -PyObject* -diff_get_patch_byindex(git_diff* diff, size_t idx) +PyObject * +wrap_patch(git_patch *patch) { - const git_diff_delta* delta; - const git_diff_hunk *hunk; - const git_diff_line *line; - git_patch* patch = NULL; - size_t i, j, hunk_amounts, lines_in_hunk, additions, deletions; - int err; - Hunk *py_hunk = NULL; - Patch *py_patch = NULL; - PyObject *py_line_origin=NULL, *py_line=NULL; - - err = git_patch_from_diff(&patch, diff, idx); - if (err < 0) - return Error_set(err); + Patch *py_patch; - delta = git_patch_get_delta(patch); + if (!patch) + Py_RETURN_NONE; py_patch = PyObject_New(Patch, &PatchType); - if (py_patch != NULL) { + if (py_patch) { + size_t i, j, hunk_amounts, lines_in_hunk, additions, deletions; + const git_diff_delta *delta; + const git_diff_hunk *hunk; + const git_diff_line *line; + int err; + + delta = git_patch_get_delta(patch); + py_patch->old_file_path = delta->old_file.path; py_patch->new_file_path = delta->new_file.path; py_patch->status = git_diff_status_char(delta->status); @@ -92,11 +89,12 @@ diff_get_patch_byindex(git_diff* diff, size_t idx) hunk_amounts = git_patch_num_hunks(patch); py_patch->hunks = PyList_New(hunk_amounts); - for (i=0; i < hunk_amounts; ++i) { - err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, i); + for (i = 0; i < hunk_amounts; ++i) { + Hunk *py_hunk = NULL; + err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, i); if (err < 0) - goto cleanup; + return Error_set(err); py_hunk = PyObject_New(Hunk, &HunkType); if (py_hunk != NULL) { @@ -106,20 +104,20 @@ diff_get_patch_byindex(git_diff* diff, size_t idx) py_hunk->new_lines = hunk->new_lines; py_hunk->lines = PyList_New(lines_in_hunk); - for (j=0; j < lines_in_hunk; ++j) { - err = git_patch_get_line_in_hunk(&line, patch, i, j); + for (j = 0; j < lines_in_hunk; ++j) { + PyObject *py_line_origin = NULL, *py_line = NULL; + err = git_patch_get_line_in_hunk(&line, patch, i, j); if (err < 0) - goto cleanup; + return Error_set(err); - py_line_origin = to_unicode_n(&line->origin, 1, NULL, NULL); - py_line = to_unicode_n(line->content, line->content_len, NULL, NULL); + py_line_origin = to_unicode_n(&line->origin, 1, + NULL, NULL); + py_line = to_unicode_n(line->content, line->content_len, + NULL, NULL); PyList_SetItem(py_hunk->lines, j, - Py_BuildValue("OO", - py_line_origin, - py_line - ) - ); + Py_BuildValue("OO", py_line_origin, py_line)); + Py_DECREF(py_line_origin); Py_DECREF(py_line); } @@ -130,10 +128,20 @@ diff_get_patch_byindex(git_diff* diff, size_t idx) } } -cleanup: - git_patch_free(patch); + return (PyObject*) py_patch; +} + +PyObject* +diff_get_patch_byindex(git_diff *diff, size_t idx) +{ + git_patch *patch = NULL; + int err; + + err = git_patch_from_diff(&patch, diff, idx); + if (err < 0) + return Error_set(err); - return (err < 0) ? Error_set(err) : (PyObject*) py_patch; + return (PyObject*) wrap_patch(patch); } static void diff --git a/src/diff.h b/src/diff.h index f32c93090..9ba3a9ea0 100644 --- a/src/diff.h +++ b/src/diff.h @@ -42,5 +42,6 @@ PyObject* Diff_changes(Diff *self); PyObject* Diff_patch(Diff *self); PyObject* wrap_diff(git_diff *diff, Repository *repo); +PyObject* wrap_patch(git_patch *patch); #endif diff --git a/test/test_blob.py b/test/test_blob.py index 4e7a6cdc9..30faee4f8 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -105,5 +105,16 @@ def test_create_blob_fromdisk(self): self.assertTrue(isinstance(blob, pygit2.Blob)) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) + def test_diff_blob(self): + blob = self.repo[BLOB_SHA] + old_blob = self.repo['3b18e512dba79e4c8300dd08aeb37f8e728b8dad'] + patch = blob.diff(old_blob, old_as_path="hello.txt") + self.assertEqual(len(patch.hunks), 1) + + def test_diff_blob_to_buffer(self): + blob = self.repo[BLOB_SHA] + patch = blob.diff_to_buffer("hello world") + self.assertEqual(len(patch.hunks), 1) + if __name__ == '__main__': unittest.main() From bf26aaf180ccecad53deebadf9b165b93a8eb164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 19 Jan 2014 15:26:29 +0100 Subject: [PATCH 0638/2237] Repository: allow retrieving the default signature Make it easier to grab the default signature for a repository by adding a getter at the Repository level. --- src/repository.c | 15 +++++++++++++++ test/test_repository.py | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/repository.c b/src/repository.c index 7cc746b75..fa0dd7ee1 100644 --- a/src/repository.c +++ b/src/repository.c @@ -39,6 +39,7 @@ #include "branch.h" #include "blame.h" #include "mergeresult.h" +#include "signature.h" #include extern PyObject *GitError; @@ -1324,6 +1325,19 @@ Repository_remotes__get__(Repository *self) return (PyObject*) py_list; } +PyDoc_STRVAR(Repository_default_signature__doc__, "Return the signature according to the repository's configuration"); + +PyObject * +Repository_default_signature__get__(Repository *self) +{ + git_signature *sig; + int err; + + if ((err = git_signature_default(&sig, self->repo)) < 0) + return Error_set(err); + + return build_signature((Object*) self, sig, "utf-8"); +} PyDoc_STRVAR(Repository_checkout_head__doc__, "checkout_head(strategy)\n" @@ -1633,6 +1647,7 @@ PyGetSetDef Repository_getseters[] = { GETTER(Repository, config), GETTER(Repository, workdir), GETTER(Repository, remotes), + GETTER(Repository, default_signature), {NULL} }; diff --git a/test/test_repository.py b/test/test_repository.py index 281630d1e..081e792b8 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -371,6 +371,16 @@ def test_merge_already_something_in_index(self): self.repo.index.add('inindex.txt') self.assertRaises(pygit2.GitError, self.repo.merge, branch_oid) +class RepositorySignatureTest(utils.RepoTestCase): + + def test_default_signature(self): + config = self.repo.config + config['user.name'] = 'Random J Hacker' + config['user.email'] ='rjh@example.com' + + sig = self.repo.default_signature + self.assertEqual('Random J Hacker', sig.name) + self.assertEqual('rjh@example.com', sig.email) class NewRepositoryTest(utils.NoRepoTestCase): From 5a80091c2d42f2c5598d8e699ce3d1bb9669afcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 19 Jan 2014 15:43:53 +0100 Subject: [PATCH 0639/2237] Commit: allow retrieval of the tree's ID Let the user retrieve the ID of the commit's tree instead of having to load the tree just to retrieve this information. --- src/commit.c | 10 +++++++++- test/test_commit.py | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/commit.c b/src/commit.c index 725a3c44e..ef821be5d 100644 --- a/src/commit.c +++ b/src/commit.c @@ -32,6 +32,7 @@ #include "signature.h" #include "commit.h" #include "object.h" +#include "oid.h" extern PyTypeObject TreeType; @@ -120,7 +121,6 @@ Commit_author__get__(Commit *self) return build_signature((Object*)self, signature, encoding); } - PyDoc_STRVAR(Commit_tree__doc__, "The tree object attached to the commit."); PyObject * @@ -146,6 +146,13 @@ Commit_tree__get__(Commit *commit) return (PyObject*)py_tree; } +PyDoc_STRVAR(Commit_tree_id__doc__, "The id of the tree attached to the commit."); + +PyObject * +Commit_tree_id__get__(Commit *commit) +{ + return git_oid_to_python(git_commit_tree_id(commit->commit)); +} PyDoc_STRVAR(Commit_parents__doc__, "The list of parent commits."); @@ -201,6 +208,7 @@ PyGetSetDef Commit_getseters[] = { GETTER(Commit, committer), GETTER(Commit, author), GETTER(Commit, tree), + GETTER(Commit, tree_id), GETTER(Commit, parents), {NULL} }; diff --git a/test/test_commit.py b/test/test_commit.py index 85f3d2350..c9e8fbaa1 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -31,7 +31,7 @@ from __future__ import unicode_literals import unittest -from pygit2 import GIT_OBJ_COMMIT, Signature +from pygit2 import GIT_OBJ_COMMIT, Signature, Oid from . import utils @@ -92,6 +92,7 @@ def test_new_commit(self): self.assertEqualSignature(committer, commit.committer) self.assertEqualSignature(author, commit.author) self.assertEqual(tree, commit.tree.hex) + self.assertEqual(Oid(hex=tree), commit.tree_id) self.assertEqual(1, len(commit.parents)) self.assertEqual(COMMIT_SHA, commit.parents[0].hex) @@ -118,6 +119,7 @@ def test_new_commit_encoding(self): self.assertEqualSignature(committer, commit.committer) self.assertEqualSignature(author, commit.author) self.assertEqual(tree, commit.tree.hex) + self.assertEqual(Oid(hex=tree), commit.tree_id) self.assertEqual(1, len(commit.parents)) self.assertEqual(COMMIT_SHA, commit.parents[0].hex) From 1b473b718313ab81f2d5c1a955f1f4d9d9c85500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 19 Jan 2014 15:52:01 +0100 Subject: [PATCH 0640/2237] Commit: allow retrieval of the parents' ids Don't force the user to load the parents in order to get their ids, but expose a list of the ids directly. --- src/commit.c | 23 +++++++++++++++++++++++ test/test_commit.py | 2 ++ 2 files changed, 25 insertions(+) diff --git a/src/commit.c b/src/commit.c index ef821be5d..258f082ba 100644 --- a/src/commit.c +++ b/src/commit.c @@ -199,6 +199,28 @@ Commit_parents__get__(Commit *self) return list; } +PyDoc_STRVAR(Commit_parent_ids__doc__, "The list of parent commits' ids."); + +PyObject * +Commit_parent_ids__get__(Commit *self) +{ + unsigned int i, parent_count; + const git_oid *id; + PyObject *list; + + parent_count = git_commit_parentcount(self->commit); + list = PyList_New(parent_count); + if (!list) + return NULL; + + for (i=0; i < parent_count; i++) { + id = git_commit_parent_id(self->commit, i); + PyList_SET_ITEM(list, i, git_oid_to_python(id)); + } + + return list; +} + PyGetSetDef Commit_getseters[] = { GETTER(Commit, message_encoding), GETTER(Commit, message), @@ -210,6 +232,7 @@ PyGetSetDef Commit_getseters[] = { GETTER(Commit, tree), GETTER(Commit, tree_id), GETTER(Commit, parents), + GETTER(Commit, parent_ids), {NULL} }; diff --git a/test/test_commit.py b/test/test_commit.py index c9e8fbaa1..a944c40d6 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -95,6 +95,7 @@ def test_new_commit(self): self.assertEqual(Oid(hex=tree), commit.tree_id) self.assertEqual(1, len(commit.parents)) self.assertEqual(COMMIT_SHA, commit.parents[0].hex) + self.assertEqual(Oid(hex=COMMIT_SHA), commit.parent_ids[0]) def test_new_commit_encoding(self): repo = self.repo @@ -122,6 +123,7 @@ def test_new_commit_encoding(self): self.assertEqual(Oid(hex=tree), commit.tree_id) self.assertEqual(1, len(commit.parents)) self.assertEqual(COMMIT_SHA, commit.parents[0].hex) + self.assertEqual(Oid(hex=COMMIT_SHA), commit.parent_ids[0]) def test_modify_commit(self): message = 'New commit.\n\nMessage.\n' From 4dc90f78a9a3e248b016b4743e41babe5d6b423c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 19 Jan 2014 17:10:40 +0100 Subject: [PATCH 0641/2237] TreeEntry: add rich comparison function Allow direct comparisons between TreeEntry objects, which also allows us to use assertEqual in the sanity check test. This fixes #305. --- src/tree.c | 44 +++++++++++++++++++++++++++++++++++++++++++- test/test_tree.py | 2 +- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/tree.c b/src/tree.c index 7599d659b..a3bfe2132 100644 --- a/src/tree.c +++ b/src/tree.c @@ -36,6 +36,7 @@ #include "diff.h" extern PyTypeObject TreeType; +extern PyTypeObject TreeEntryType; extern PyTypeObject DiffType; extern PyTypeObject TreeIterType; extern PyTypeObject IndexType; @@ -77,6 +78,47 @@ TreeEntry_oid__get__(TreeEntry *self) return git_oid_to_python(oid); } +PyObject * +TreeEntry_richcompare(PyObject *a, PyObject *b, int op) +{ + PyObject *res; + int cmp; + + /* We only support comparing to another tree entry */ + if (!PyObject_TypeCheck(b, &TreeEntryType)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + cmp =git_tree_entry_cmp(((TreeEntry*)a)->entry, ((TreeEntry*)b)->entry); + switch (op) { + case Py_LT: + res = (cmp <= 0) ? Py_True: Py_False; + break; + case Py_LE: + res = (cmp < 0) ? Py_True: Py_False; + break; + case Py_EQ: + res = (cmp == 0) ? Py_True: Py_False; + break; + case Py_NE: + res = (cmp != 0) ? Py_True: Py_False; + break; + case Py_GT: + res = (cmp > 0) ? Py_True: Py_False; + break; + case Py_GE: + res = (cmp >= 0) ? Py_True: Py_False; + break; + default: + PyErr_Format(PyExc_RuntimeError, "Unexpected '%d' op", op); + return NULL; + } + + Py_INCREF(res); + return res; +} + PyDoc_STRVAR(TreeEntry_hex__doc__, "Hex oid."); @@ -122,7 +164,7 @@ PyTypeObject TreeEntryType = { TreeEntry__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + (richcmpfunc)TreeEntry_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ diff --git a/test/test_tree.py b/test/test_tree.py index 16674ec32..d1fc0debe 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -118,7 +118,7 @@ def test_iterate_tree(self): """ tree = self.repo[TREE_SHA] for tree_entry in tree: - self.assertEqual(tree_entry.hex, tree[tree_entry.name].hex) + self.assertEqual(tree_entry, tree[tree_entry.name]) if __name__ == '__main__': From 35386cbec25c6e74a5843266a2f26695fe6f02fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 19 Jan 2014 16:34:53 +0100 Subject: [PATCH 0642/2237] Config: switch from foreach iterator An iterator is much more natural in python, so let's use that. --- docs/config.rst | 10 +++- src/config.c | 140 +++++++++++++++++++++++--------------------- src/pygit2.c | 3 + src/types.h | 5 ++ test/test_config.py | 15 ++--- 5 files changed, 96 insertions(+), 77 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index e69fbca83..ef3a1a54f 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -10,9 +10,17 @@ The Config type .. automethod:: pygit2.Config.get_system_config .. automethod:: pygit2.Config.get_global_config -.. automethod:: pygit2.Config.foreach .. automethod:: pygit2.Config.add_file .. automethod:: pygit2.Config.get_multivar .. automethod:: pygit2.Config.set_multivar The :class:`Config` Mapping interface. + +Iterator +========= + +The :class:`Config` class has an iterator which can be used to loop +through all the entries in the configuration. Each element is a tuple +containing the name and the value of each configuration variable. Be +aware that this may return multiple versions of each entry if they are +set multiple times in the configuration files. diff --git a/src/config.c b/src/config.c index 873ba7363..f7675587c 100644 --- a/src/config.c +++ b/src/config.c @@ -33,6 +33,7 @@ #include "config.h" extern PyTypeObject ConfigType; +extern PyTypeObject ConfigIterType; PyObject * @@ -239,70 +240,6 @@ Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) return 0; } -int -Config_foreach_callback_wrapper(const git_config_entry *entry, void *c_payload) -{ - PyObject *args = (PyObject *)c_payload; - PyObject *py_callback = NULL; - PyObject *py_payload = NULL; - PyObject *py_result = NULL; - int c_result; - - if (!PyArg_ParseTuple(args, "O|O", &py_callback, &py_payload)) - return -1; - - if (py_payload) - args = Py_BuildValue("ssO", entry->name, entry->value, py_payload); - else - args = Py_BuildValue("ss", entry->name, entry->value); - if (!args) - return -1; - - if (!(py_result = PyObject_CallObject(py_callback, args))) - return -1; - - if ((c_result = PyLong_AsLong(py_result)) == -1) - return -1; - - Py_CLEAR(args); - - return c_result; -} - - -PyDoc_STRVAR(Config_foreach__doc__, - "foreach(callback[, payload]) -> int\n" - "\n" - "Perform an operation on each config variable.\n" - "\n" - "The callback must be of type Callable and receives the normalized name\n" - "and value of each variable in the config backend, and an optional payload\n" - "passed to this method. As soon as one of the callbacks returns an integer\n" - "other than 0, this function returns that value."); - -PyObject * -Config_foreach(Config *self, PyObject *args) -{ - int ret; - PyObject *py_callback; - PyObject *py_payload = NULL; - - if (!PyArg_ParseTuple(args, "O|O", &py_callback, &py_payload)) - return NULL; - - if (!PyCallable_Check(py_callback)) { - PyErr_SetString(PyExc_TypeError, - "Argument 'callback' is not callable"); - return NULL; - } - - ret = git_config_foreach(self->config, Config_foreach_callback_wrapper, - (void *)args); - - return PyLong_FromLong((long)ret); -} - - PyDoc_STRVAR(Config_add_file__doc__, "add_file(path, level=0, force=0)\n" "\n" @@ -407,10 +344,28 @@ Config_set_multivar(Config *self, PyObject *args) Py_RETURN_NONE; } +PyObject * +Config_iter(Config *self) +{ + ConfigIter *iter; + int err; + + iter = PyObject_New(ConfigIter, &ConfigIterType); + if (!iter) + return NULL; + + if ((err = git_config_iterator_new(&iter->iter, self->config)) < 0) + return Error_set(err); + + Py_INCREF(self); + iter->owner = self; + + return (PyObject*)iter; +} + PyMethodDef Config_methods[] = { METHOD(Config, get_system_config, METH_NOARGS | METH_STATIC), METHOD(Config, get_global_config, METH_NOARGS | METH_STATIC), - METHOD(Config, foreach, METH_VARARGS), METHOD(Config, add_file, METH_VARARGS | METH_KEYWORDS), METHOD(Config, get_multivar, METH_VARARGS), METHOD(Config, set_multivar, METH_VARARGS), @@ -463,7 +418,7 @@ PyTypeObject ConfigType = { 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ + (getiterfunc)Config_iter, /* tp_iter */ 0, /* tp_iternext */ Config_methods, /* tp_methods */ 0, /* tp_members */ @@ -477,3 +432,56 @@ PyTypeObject ConfigType = { 0, /* tp_alloc */ 0, /* tp_new */ }; + +void +ConfigIter_dealloc(ConfigIter *self) +{ + Py_CLEAR(self->owner); + git_config_iterator_free(self->iter); + PyObject_Del(self); +} + +PyObject * +ConfigIter_iternext(ConfigIter *self) +{ + int err; + git_config_entry *entry; + + if ((err = git_config_next(&entry, self->iter)) < 0) + return Error_set(err); + + return Py_BuildValue("ss", entry->name, entry->value); +} + +PyDoc_STRVAR(ConfigIter__doc__, "Configuration iterator."); + +PyTypeObject ConfigIterType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.ConfigIter", /* tp_name */ + sizeof(ConfigIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)ConfigIter_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + ConfigIter__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)ConfigIter_iternext, /* tp_iternext */ + +}; diff --git a/src/pygit2.c b/src/pygit2.c index e640ea8e0..97eb421ee 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -56,6 +56,7 @@ extern PyTypeObject IndexEntryType; extern PyTypeObject IndexIterType; extern PyTypeObject WalkerType; extern PyTypeObject ConfigType; +extern PyTypeObject ConfigIterType; extern PyTypeObject ReferenceType; extern PyTypeObject RefLogIterType; extern PyTypeObject RefLogEntryType; @@ -412,7 +413,9 @@ moduleinit(PyObject* m) /* Config */ INIT_TYPE(ConfigType, NULL, PyType_GenericNew) + INIT_TYPE(ConfigIterType, NULL, PyType_GenericNew) ADD_TYPE(m, Config) + ADD_TYPE(m, ConfigIter) /* Remotes */ INIT_TYPE(RemoteType, NULL, NULL) diff --git a/src/types.h b/src/types.h index c86495992..94ce79057 100644 --- a/src/types.h +++ b/src/types.h @@ -77,6 +77,11 @@ typedef struct { git_config* config; } Config; +typedef struct { + PyObject_HEAD + Config *owner; + git_config_iterator *iter; +} ConfigIter; /* git_note */ typedef struct { diff --git a/test/test_config.py b/test/test_config.py index 8740a64c9..4a22f407e 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -36,13 +36,6 @@ CONFIG_FILENAME = "test_config" - -def foreach_test_wrapper(key, name, lst): - lst[key] = name - return 0 -foreach_test_wrapper.__test__ = False - - class ConfigTest(utils.RepoTestCase): def tearDown(self): @@ -175,13 +168,15 @@ def test_write(self): for i in l: self.assertEqual(i, 'foo-123456') - def test_foreach(self): + def test_iterator(self): config = self.repo.config lst = {} - config.foreach(foreach_test_wrapper, lst) + + for name, value in config: + lst[name] = value + self.assertTrue('core.bare' in lst) self.assertTrue(lst['core.bare']) - if __name__ == '__main__': unittest.main() From e681a47245a60bfbef83bfe5eeeeaa4ef4d84dba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 19 Jan 2014 23:05:41 +0100 Subject: [PATCH 0643/2237] Refspec: create the type to wrap refspecs --- src/pygit2.c | 6 ++ src/remote.c | 178 +++++++++++++++++++++++++++++++++++++++++++- src/types.h | 6 ++ test/test_remote.py | 13 +++- 4 files changed, 197 insertions(+), 6 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index 97eb421ee..7c72e509c 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -63,6 +63,7 @@ extern PyTypeObject RefLogEntryType; extern PyTypeObject BranchType; extern PyTypeObject SignatureType; extern PyTypeObject RemoteType; +extern PyTypeObject RefspecType; extern PyTypeObject NoteType; extern PyTypeObject NoteIterType; extern PyTypeObject BlameType; @@ -420,6 +421,11 @@ moduleinit(PyObject* m) /* Remotes */ INIT_TYPE(RemoteType, NULL, NULL) ADD_TYPE(m, Remote) + INIT_TYPE(RefspecType, NULL, NULL) + ADD_TYPE(m, Refspec) + /* Direction for the refspec */ + ADD_CONSTANT_INT(m, GIT_DIRECTION_FETCH) + ADD_CONSTANT_INT(m, GIT_DIRECTION_PUSH) /* Blame */ INIT_TYPE(BlameType, NULL, NULL) diff --git a/src/remote.c b/src/remote.c index b5d705998..265828dd1 100644 --- a/src/remote.c +++ b/src/remote.c @@ -36,6 +36,181 @@ extern PyObject *GitError; extern PyTypeObject RepositoryType; +extern PyTypeObject RefspecType; + +Refspec * +wrap_refspec(const Remote *owner, const git_refspec *refspec) +{ + Refspec *spec; + + spec = PyObject_New(Refspec, &RefspecType); + if (!spec) + return NULL; + + Py_INCREF(owner); + spec->owner = owner; + spec->refspec = refspec; + + return spec; +} + +PyDoc_STRVAR(Refspec_direction__doc__, + "The direction of this refspec (fetch or push)"); + +PyObject * +Refspec_direction__get__(Refspec *self) +{ + return Py_BuildValue("i", git_refspec_direction(self->refspec)); +} + +PyDoc_STRVAR(Refspec_src__doc__, "Source or lhs of the refspec"); + +PyObject * +Refspec_src__get__(Refspec *self) +{ + return to_unicode(git_refspec_src(self->refspec), NULL, NULL); +} + +PyDoc_STRVAR(Refspec_dst__doc__, "Destination or rhs of the refspec"); + +PyObject * +Refspec_dst__get__(Refspec *self) +{ + return to_unicode(git_refspec_dst(self->refspec), NULL, NULL); +} + +PyDoc_STRVAR(Refspec_string__doc__, "String used to create this refspec"); + +PyObject * +Refspec_string__get__(Refspec *self) +{ + return to_unicode(git_refspec_string(self->refspec), NULL, NULL); +} + +PyDoc_STRVAR(Refspec_force__doc__, + "Whether this refspec allows non-fast-forward updates"); + +PyObject * +Refspec_force__get__(Refspec *self) +{ + if (git_refspec_force(self->refspec)) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +PyDoc_STRVAR(Refspec_src_matches__doc__, + "src_matches(str) -> Bool\n" + "\n" + "Returns whether the string matches the source refspec\n"); + +PyObject * +Refspec_src_matches(Refspec *self, PyObject *py_str) +{ + char *str; + int res; + + str = py_str_to_c_str(py_str, NULL); + if (!str) + return NULL; + + res = git_refspec_src_matches(self->refspec, str); + free(str); + + if (res) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +PyDoc_STRVAR(Refspec_dst_matches__doc__, + "dst_matches(str) -> Bool\n" + "\n" + "Returns whether the string matches the destination refspec\n"); + +PyObject * +Refspec_dst_matches(Refspec *self, PyObject *py_str) +{ + char *str; + int res; + + str = py_str_to_c_str(py_str, NULL); + if (!str) + return NULL; + + res = git_refspec_dst_matches(self->refspec, str); + free(str); + + if (res) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +PyMethodDef Refspec_methods[] = { + METHOD(Refspec, src_matches, METH_O), + METHOD(Refspec, dst_matches, METH_O), + {NULL} +}; + +PyGetSetDef Refspec_getseters[] = { + GETTER(Refspec, direction), + GETTER(Refspec, src), + GETTER(Refspec, dst), + GETTER(Refspec, string), + GETTER(Refspec, force), + {NULL} +}; + +static void +Refspec_dealloc(Refspec *self) +{ + Py_CLEAR(self->owner); + PyObject_Del(self); +} + +PyDoc_STRVAR(Refspec__doc__, "Refspec object."); + +PyTypeObject RefspecType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.Refspec", /* tp_name */ + sizeof(Remote), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Refspec_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Refspec__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Refspec_methods, /* tp_methods */ + 0, /* tp_members */ + Refspec_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 */ +}; PyObject * Remote_init(Remote *self, PyObject *args, PyObject *kwds) @@ -306,8 +481,7 @@ Remote_get_refspec(Remote *self, PyObject *value) return NULL; } - return Py_BuildValue("(ss)", git_refspec_src(refspec), - git_refspec_dst(refspec)); + return (PyObject*) wrap_refspec(self, refspec); } diff --git a/src/types.h b/src/types.h index 94ce79057..63a5672d5 100644 --- a/src/types.h +++ b/src/types.h @@ -198,6 +198,12 @@ typedef struct { /* git_remote */ SIMPLE_TYPE(Remote, git_remote, remote) +/* git_refspec */ +typedef struct { + PyObject_HEAD + const Remote *owner; + const git_refspec *refspec; +} Refspec; /* git_blame */ SIMPLE_TYPE(Blame, git_blame, blame) diff --git a/test/test_remote.py b/test/test_remote.py index 741cd504d..981e3bd3e 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -39,6 +39,7 @@ REMOTE_REPO_OBJECTS = 30 REMOTE_REPO_BYTES = 2758 +ORIGIN_REFSPEC = '+refs/heads/*:refs/remotes/origin/*' class RepositoryTest(utils.RepoTestCase): def test_remote_create(self): @@ -80,13 +81,17 @@ def test_refspec(self): self.assertEqual(remote.refspec_count, 1) refspec = remote.get_refspec(0) - self.assertEqual(refspec[0], REMOTE_FETCHSPEC_SRC) - self.assertEqual(refspec[1], REMOTE_FETCHSPEC_DST) + self.assertEqual(refspec.src, REMOTE_FETCHSPEC_SRC) + self.assertEqual(refspec.dst, REMOTE_FETCHSPEC_DST) + self.assertEqual(True, refspec.force) + self.assertEqual(ORIGIN_REFSPEC, refspec.string) self.assertEqual(list, type(remote.get_fetch_refspecs())) self.assertEqual(1, len(remote.get_fetch_refspecs())) - self.assertEqual('+refs/heads/*:refs/remotes/origin/*', - remote.get_fetch_refspecs()[0]) + self.assertEqual(ORIGIN_REFSPEC, remote.get_fetch_refspecs()[0]) + + self.assertTrue(refspec.src_matches('refs/heads/master')) + self.assertTrue(refspec.dst_matches('refs/remotes/origin/master')) self.assertEqual(list, type(remote.get_push_refspecs())) self.assertEqual(0, len(remote.get_push_refspecs())) From 17a49bb417182fb4e0735bf614e916f61fb615cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 19 Jan 2014 23:53:56 +0100 Subject: [PATCH 0644/2237] Refspec: implement transform functions --- src/remote.c | 82 +++++++++++++++++++++++++++++++++++++++++++++ test/test_remote.py | 2 ++ 2 files changed, 84 insertions(+) diff --git a/src/remote.c b/src/remote.c index 265828dd1..524d01138 100644 --- a/src/remote.c +++ b/src/remote.c @@ -147,9 +147,91 @@ Refspec_dst_matches(Refspec *self, PyObject *py_str) Py_RETURN_FALSE; } +PyDoc_STRVAR(Refspec_transform__doc__, + "transform(str) -> str\n" + "\n" + "Transform a reference according to the refspec\n"); + +PyObject * +Refspec_transform(Refspec *self, PyObject *py_str) +{ + char *str, *trans; + int err, len, alen; + PyObject *py_trans; + + str = py_str_to_c_str(py_str, NULL); + alen = len = strlen(str); + + do { + alen *= alen; + trans = malloc(alen); + if (!trans) { + free(str); + return PyErr_NoMemory(); + } + + err = git_refspec_transform(trans, alen, self->refspec, str); + } while(err == GIT_EBUFS); + free(str); + + if (err < 0) { + free(trans); + Error_set(err); + return NULL; + } + + py_trans = to_unicode(trans, NULL, NULL); + + free(trans); + + return py_trans; +} + +PyDoc_STRVAR(Refspec_rtransform__doc__, + "rtransform(str) -> str\n" + "\n" + "Transform a reference according to the refspec in reverse\n"); + +PyObject * +Refspec_rtransform(Refspec *self, PyObject *py_str) +{ + char *str, *trans; + int err, len, alen; + PyObject *py_trans; + + str = py_str_to_c_str(py_str, NULL); + alen = len = strlen(str); + + do { + alen *= alen; + trans = malloc(alen); + if (!trans) { + free(str); + return PyErr_NoMemory(); + } + + err = git_refspec_rtransform(trans, alen, self->refspec, str); + } while(err == GIT_EBUFS); + free(str); + + if (err < 0) { + free(trans); + Error_set(err); + return NULL; + } + + py_trans = to_unicode(trans, NULL, NULL); + + free(trans); + + return py_trans; +} + PyMethodDef Refspec_methods[] = { METHOD(Refspec, src_matches, METH_O), METHOD(Refspec, dst_matches, METH_O), + METHOD(Refspec, transform, METH_O), + METHOD(Refspec, rtransform, METH_O), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index 981e3bd3e..59ebcdc18 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -92,6 +92,8 @@ def test_refspec(self): self.assertTrue(refspec.src_matches('refs/heads/master')) self.assertTrue(refspec.dst_matches('refs/remotes/origin/master')) + self.assertEqual('refs/remotes/origin/master', refspec.transform('refs/heads/master')) + self.assertEqual('refs/heads/master', refspec.rtransform('refs/remotes/origin/master')) self.assertEqual(list, type(remote.get_push_refspecs())) self.assertEqual(0, len(remote.get_push_refspecs())) From de5244d7c78b3d13b28e8084c17972d364a1e4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 19 Jan 2014 23:58:33 +0100 Subject: [PATCH 0645/2237] Refspec: add documentation --- docs/remotes.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/remotes.rst b/docs/remotes.rst index 3542f699b..9a9d7bfaa 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -21,3 +21,16 @@ The Remote type .. automethod:: pygit2.Remote.fetch .. automethod:: pygit2.Remote.push .. automethod:: pygit2.Remote.save + +The Refspec type +=================== + +.. autoattribute:: pygit2.Refspec.direction +.. autoattribute:: pygit2.Refspec.src +.. autoattribute:: pygit2.Refspec.dst +.. autoattribute:: pygit2.Refspec.force +.. autoattribute:: pygit2.Refspec.string +.. automethod:: pygit2.Refspec.src_matches +.. automethod:: pygit2.Refspec.dst_matches +.. automethod:: pygit2.Refspec.transform +.. automethod:: pygit2.Refspec.rtransform From 7ef23780cd3d0d19c506831f89be75935980f0cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 20 Jan 2014 03:15:56 +0100 Subject: [PATCH 0646/2237] Remote: handle the case of a missing url --- src/remote.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index b5d705998..a1a5eac2e 100644 --- a/src/remote.c +++ b/src/remote.c @@ -248,7 +248,13 @@ PyDoc_STRVAR(Remote_url__doc__, "Url of the remote"); PyObject * Remote_url__get__(Remote *self) { - return to_unicode(git_remote_url(self->remote), NULL, NULL); + const char *url; + + url = git_remote_url(self->remote); + if (!url) + Py_RETURN_NONE; + + return to_unicode(url, NULL, NULL); } From e28c06fc1de1fe826d28f508d3354aa23477854e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 20 Jan 2014 03:17:34 +0100 Subject: [PATCH 0647/2237] Remote: allow access to the push url --- src/remote.c | 37 +++++++++++++++++++++++++++++++++++++ test/test_remote.py | 5 +++++ 2 files changed, 42 insertions(+) diff --git a/src/remote.c b/src/remote.c index a1a5eac2e..4d32de157 100644 --- a/src/remote.c +++ b/src/remote.c @@ -278,6 +278,42 @@ Remote_url__set__(Remote *self, PyObject* py_url) return -1; } +PyDoc_STRVAR(Remote_push_url__doc__, "Push url of the remote"); + + +PyObject * +Remote_push_url__get__(Remote *self) +{ + const char *url; + + url = git_remote_pushurl(self->remote); + if (!url) + Py_RETURN_NONE; + + return to_unicode(url, NULL, NULL); +} + + +int +Remote_push_url__set__(Remote *self, PyObject* py_url) +{ + int err; + char* url = NULL; + + url = py_str_to_c_str(py_url, NULL); + if (url != NULL) { + err = git_remote_set_pushurl(self->remote, url); + free(url); + + if (err == GIT_OK) + return 0; + + Error_set(err); + } + + return -1; +} + PyDoc_STRVAR(Remote_refspec_count__doc__, "Number of refspecs."); @@ -452,6 +488,7 @@ PyMethodDef Remote_methods[] = { PyGetSetDef Remote_getseters[] = { GETSET(Remote, name), GETSET(Remote, url), + GETSET(Remote, push_url), GETTER(Remote, refspec_count), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index 741cd504d..85478c843 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -50,6 +50,7 @@ def test_remote_create(self): self.assertEqual(type(remote), pygit2.Remote) self.assertEqual(name, remote.name) self.assertEqual(url, remote.url) + self.assertEqual(None, remote.push_url) self.assertRaises(ValueError, self.repo.create_remote, *(name, url)) @@ -74,6 +75,10 @@ def test_remote_set_url(self): self.assertRaisesAssign(ValueError, remote, 'url', '') + remote.push_url = new_url + self.assertEqual(new_url, remote.push_url) + self.assertRaisesAssign(ValueError, remote, 'push_url', '') + def test_refspec(self): remote = self.repo.remotes[0] From 5baaf287d20cb7ddb8b9269a5a308ba95e97baaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 20 Jan 2014 03:37:59 +0100 Subject: [PATCH 0648/2237] Tree: let contains look in subtrees Allow looking in subtrees as a convenience in the 'contains' function. As slashes are not allowed to be part of the name of an entry, there is no ambiguity in what they mean. --- src/tree.c | 18 +++++++++++++++--- test/test_tree.py | 7 +++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/tree.c b/src/tree.c index a3bfe2132..ca91cc04f 100644 --- a/src/tree.c +++ b/src/tree.c @@ -191,14 +191,26 @@ Tree_len(Tree *self) int Tree_contains(Tree *self, PyObject *py_name) { - int result = 0; + int err; + git_tree_entry *entry; char *name = py_path_to_c_str(py_name); if (name == NULL) return -1; - result = git_tree_entry_byname(self->tree, name) ? 1 : 0; + err = git_tree_entry_bypath(&entry, self->tree, name); free(name); - return result; + + if (err == GIT_ENOTFOUND) + return 0; + + if (err < 0) { + Error_set(err); + return -1; + } + + git_tree_entry_free(entry); + + return 1; } TreeEntry * diff --git a/test/test_tree.py b/test/test_tree.py index d1fc0debe..507a22bdd 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -120,6 +120,13 @@ def test_iterate_tree(self): for tree_entry in tree: self.assertEqual(tree_entry, tree[tree_entry.name]) + def test_deep_contains(self): + tree = self.repo[TREE_SHA] + self.assertTrue('a' in tree) + self.assertTrue('c' in tree) + self.assertTrue('c/d' in tree) + self.assertFalse('c/e' in tree) + self.assertFalse('d' in tree) if __name__ == '__main__': unittest.main() From 9ef75d846eabe575c53653fc9aeb109f5b2d7010 Mon Sep 17 00:00:00 2001 From: XTao Date: Mon, 20 Jan 2014 18:18:22 +0800 Subject: [PATCH 0649/2237] Add fetch & push refspec. --- src/remote.c | 49 +++++++++++++++++++++++++++++++++++++++++++++ test/test_remote.py | 9 +++++++++ 2 files changed, 58 insertions(+) diff --git a/src/remote.c b/src/remote.c index 823ec6818..d77996626 100644 --- a/src/remote.c +++ b/src/remote.c @@ -729,11 +729,60 @@ Remote_push(Remote *self, PyObject *args) } +PyDoc_STRVAR(Remote_add_push__doc__, + "add_push(refspec)\n" + "\n" + "Add a push refspec to the remote."); + +PyObject * +Remote_add_push(Remote *self, PyObject *args) +{ + git_remote *remote; + char *refspec = NULL; + int err = 0; + + if (!PyArg_ParseTuple(args, "s", &refspec)) + return NULL; + + remote = self->remote; + err = git_remote_add_push(remote, refspec); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + + +PyDoc_STRVAR(Remote_add_fetch__doc__, + "add_fetch(refspec)\n" + "\n" + "Add a fetch refspec to the remote."); + +PyObject * +Remote_add_fetch(Remote *self, PyObject *args) +{ + git_remote *remote; + char *refspec = NULL; + int err = 0; + + if (!PyArg_ParseTuple(args, "s", &refspec)) + return NULL; + + remote = self->remote; + err = git_remote_add_fetch(remote, refspec); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + PyMethodDef Remote_methods[] = { METHOD(Remote, fetch, METH_NOARGS), METHOD(Remote, save, METH_NOARGS), METHOD(Remote, get_refspec, METH_O), METHOD(Remote, push, METH_VARARGS), + METHOD(Remote, add_push, METH_VARARGS), + METHOD(Remote, add_fetch, METH_VARARGS), METHOD(Remote, get_fetch_refspecs, METH_NOARGS), METHOD(Remote, set_fetch_refspecs, METH_O), METHOD(Remote, get_push_refspecs, METH_NOARGS), diff --git a/test/test_remote.py b/test/test_remote.py index 69b980203..75fdb4fe8 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -151,6 +151,15 @@ def test_remote_save(self): self.assertEqual('http://example.com/test.git', self.repo.remotes[0].url) + def test_add_refspec(self): + remote = self.repo.create_remote('test_add_refspec', REMOTE_URL) + remote.add_push('refs/heads/*:refs/heads/test_refspec/*') + self.assertEqual('refs/heads/*:refs/heads/test_refspec/*', + remote.get_push_refspecs()[0]) + remote.add_fetch('+refs/heads/*:refs/remotes/test_refspec/*') + self.assertEqual('+refs/heads/*:refs/remotes/test_refspec/*', + remote.get_fetch_refspecs()[1]) + class EmptyRepositoryTest(utils.EmptyRepoTestCase): def test_fetch(self): From 1040a6330ac474e51b7b4808b922dfcb6597c53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 22 Jan 2014 23:06:52 +0100 Subject: [PATCH 0650/2237] Remote: make fetch/push_refspecs attributes This is a lot more pythonic than two sets of getter-setter functions. The old ones are left for backwards compatibility but they should be removed in the next release. --- src/remote.c | 142 ++++++++++++++++++++++++++++---------------- test/test_remote.py | 9 +++ 2 files changed, 101 insertions(+), 50 deletions(-) diff --git a/src/remote.c b/src/remote.c index d77996626..14cd8f410 100644 --- a/src/remote.c +++ b/src/remote.c @@ -370,12 +370,39 @@ get_pylist_from_git_strarray(git_strarray *strarray) return new_list; } +int +get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) +{ + long index, n; + PyObject *item; + void *ptr; -PyDoc_STRVAR(Remote_get_fetch_refspecs__doc__, "Fetch refspecs"); + n = PyObject_Length(pylist); + if (n < 0) + return -1; + /* allocate new git_strarray */ + ptr = calloc(n, sizeof(char *)); + if (!ptr) { + PyErr_SetNone(PyExc_MemoryError); + return -1; + } + + array->strings = ptr; + array->count = n; + + for (index = 0; index < n; index++) { + item = PyList_GetItem(pylist, index); + array->strings[index] = py_str_to_c_str(item, NULL); + } + + return GIT_OK; +} + +PyDoc_STRVAR(Remote_fetch_refspecs__doc__, "Fetch refspecs"); PyObject * -Remote_get_fetch_refspecs(Remote *self) +Remote_fetch_refspecs__get__(Remote *self) { int err; git_strarray refspecs; @@ -391,12 +418,34 @@ Remote_get_fetch_refspecs(Remote *self) return new_list; } +int +Remote_fetch_refspecs__set__(Remote *self, PyObject *args) +{ + int err; + PyObject *pyrefspecs; + git_strarray fetch_refspecs; -PyDoc_STRVAR(Remote_get_push_refspecs__doc__, "Push refspecs"); + if (! PyArg_Parse(args, "O", &pyrefspecs)) + return -1; + + if (get_strarraygit_from_pylist(&fetch_refspecs, pyrefspecs) < 0) + return -1; + + err = git_remote_set_fetch_refspecs(self->remote, &fetch_refspecs); + git_strarray_free(&fetch_refspecs); + + if (err < 0) { + Error_set(err); + return -1; + } + return 0; +} + +PyDoc_STRVAR(Remote_push_refspecs__doc__, "Push refspecs"); PyObject * -Remote_get_push_refspecs(Remote *self) +Remote_push_refspecs__get__(Remote *self) { int err; git_strarray refspecs; @@ -412,89 +461,80 @@ Remote_get_push_refspecs(Remote *self) return new_list; } - int -get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) +Remote_push_refspecs__set__(Remote *self, PyObject *args) { - long index, n; - PyObject *item; - void *ptr; + int err; + PyObject *pyrefspecs; + git_strarray push_refspecs; - n = PyObject_Length(pylist); - if (n < 0) + if (! PyArg_Parse(args, "O", &pyrefspecs)) return -1; - /* allocate new git_strarray */ - ptr = calloc(n, sizeof(char *)); - if (!ptr) { - PyErr_SetNone(PyExc_MemoryError); + if (get_strarraygit_from_pylist(&push_refspecs, pyrefspecs) != 0) return -1; - } - array->strings = ptr; - array->count = n; + err = git_remote_set_push_refspecs(self->remote, &push_refspecs); + git_strarray_free(&push_refspecs); - for (index = 0; index < n; index++) { - item = PyList_GetItem(pylist, index); - array->strings[index] = py_str_to_c_str(item, NULL); + if (err < 0) { + Error_set(err); + return -1; } - return GIT_OK; + return 0; } +PyDoc_STRVAR(Remote_get_fetch_refspecs__doc__, + "Fetch refspecs.\n" + "This function is deprecated, please use the fetch_refspecs attribute" + "\n"); + + +PyObject * +Remote_get_fetch_refspecs(Remote *self) +{ + return Remote_fetch_refspecs__get__(self); +} + + +PyDoc_STRVAR(Remote_get_push_refspecs__doc__, "Push refspecs"); + + +PyObject * +Remote_get_push_refspecs(Remote *self) +{ + return Remote_push_refspecs__get__(self); +} PyDoc_STRVAR(Remote_set_fetch_refspecs__doc__, "set_fetch_refspecs([str])\n" + "This function is deprecated, please use the push_refspecs attribute" "\n"); PyObject * Remote_set_fetch_refspecs(Remote *self, PyObject *args) { - int err; - PyObject *pyrefspecs; - git_strarray fetch_refspecs; - - if (! PyArg_Parse(args, "O", &pyrefspecs)) + if (Remote_fetch_refspecs__set__(self, args) < 0) return NULL; - if (get_strarraygit_from_pylist(&fetch_refspecs, pyrefspecs) != GIT_OK) - return NULL; - - err = git_remote_set_fetch_refspecs(self->remote, &fetch_refspecs); - git_strarray_free(&fetch_refspecs); - - if (err != GIT_OK) - return Error_set(err); - Py_RETURN_NONE; } PyDoc_STRVAR(Remote_set_push_refspecs__doc__, "set_push_refspecs([str])\n" + "This function is deprecated, please use the push_refspecs attribute" "\n"); PyObject * Remote_set_push_refspecs(Remote *self, PyObject *args) { - int err; - PyObject *pyrefspecs; - git_strarray push_refspecs; - - if (! PyArg_Parse(args, "O", &pyrefspecs)) + if (Remote_push_refspecs__set__(self, args) < 0) return NULL; - if (get_strarraygit_from_pylist(&push_refspecs, pyrefspecs) != 0) - return NULL; - - err = git_remote_set_push_refspecs(self->remote, &push_refspecs); - git_strarray_free(&push_refspecs); - - if (err != GIT_OK) - return Error_set(err); - Py_RETURN_NONE; } @@ -795,6 +835,8 @@ PyGetSetDef Remote_getseters[] = { GETSET(Remote, url), GETSET(Remote, push_url), GETTER(Remote, refspec_count), + GETSET(Remote, fetch_refspecs), + GETSET(Remote, push_refspecs), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index 75fdb4fe8..dc1a85025 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -103,10 +103,19 @@ def test_refspec(self): self.assertEqual(list, type(remote.get_push_refspecs())) self.assertEqual(0, len(remote.get_push_refspecs())) + push_specs = remote.push_refspecs + self.assertEqual(list, type(push_specs)) + self.assertEqual(0, len(push_specs)) + remote.set_fetch_refspecs(['+refs/*:refs/remotes/*']) self.assertEqual('+refs/*:refs/remotes/*', remote.get_fetch_refspecs()[0]) + fetch_specs = remote.fetch_refspecs + self.assertEqual(list, type(fetch_specs)) + self.assertEqual(1, len(fetch_specs)) + self.assertEqual('+refs/*:refs/remotes/*', fetch_specs[0]) + remote.set_fetch_refspecs([ '+refs/*:refs/remotes/*', '+refs/test/*:refs/test/remotes/*' From 0c3a700e30e132b4a9e8c87d5635965d44ac510e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 22 Jan 2014 23:30:02 +0100 Subject: [PATCH 0651/2237] Remote: harden the resfpec setters The object passed must be a list of strings, so make sure we fail by raising an error instead of segfaulting. --- src/remote.c | 42 +++++++++++++++++++++++++----------------- test/test_remote.py | 4 ++++ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/remote.c b/src/remote.c index 14cd8f410..cafa64819 100644 --- a/src/remote.c +++ b/src/remote.c @@ -373,13 +373,16 @@ get_pylist_from_git_strarray(git_strarray *strarray) int get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) { - long index, n; + Py_ssize_t index, n; PyObject *item; void *ptr; - n = PyObject_Length(pylist); - if (n < 0) + if (!PyList_Check(pylist)) { + PyErr_SetString(PyExc_TypeError, "Value must be a list"); return -1; + } + + n = PyList_Size(pylist); /* allocate new git_strarray */ ptr = calloc(n, sizeof(char *)); @@ -393,10 +396,23 @@ get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) for (index = 0; index < n; index++) { item = PyList_GetItem(pylist, index); - array->strings[index] = py_str_to_c_str(item, NULL); + char *str = py_str_to_c_str(item, NULL); + if (!str) + goto on_error; + + array->strings[index] = str; } - return GIT_OK; + return 0; + +on_error: + n = index; + for (index = 0; index < n; index++) { + free(array->strings[index]); + } + free(array->strings); + + return -1; } PyDoc_STRVAR(Remote_fetch_refspecs__doc__, "Fetch refspecs"); @@ -419,16 +435,12 @@ Remote_fetch_refspecs__get__(Remote *self) } int -Remote_fetch_refspecs__set__(Remote *self, PyObject *args) +Remote_fetch_refspecs__set__(Remote *self, PyObject *py_list) { int err; - PyObject *pyrefspecs; git_strarray fetch_refspecs; - if (! PyArg_Parse(args, "O", &pyrefspecs)) - return -1; - - if (get_strarraygit_from_pylist(&fetch_refspecs, pyrefspecs) < 0) + if (get_strarraygit_from_pylist(&fetch_refspecs, py_list) < 0) return -1; err = git_remote_set_fetch_refspecs(self->remote, &fetch_refspecs); @@ -462,16 +474,12 @@ Remote_push_refspecs__get__(Remote *self) } int -Remote_push_refspecs__set__(Remote *self, PyObject *args) +Remote_push_refspecs__set__(Remote *self, PyObject *py_list) { int err; - PyObject *pyrefspecs; git_strarray push_refspecs; - if (! PyArg_Parse(args, "O", &pyrefspecs)) - return -1; - - if (get_strarraygit_from_pylist(&push_refspecs, pyrefspecs) != 0) + if (get_strarraygit_from_pylist(&push_refspecs, py_list) != 0) return -1; err = git_remote_set_push_refspecs(self->remote, &push_refspecs); diff --git a/test/test_remote.py b/test/test_remote.py index dc1a85025..13fc93ce3 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -130,6 +130,10 @@ def test_refspec(self): '+refs/test/*:refs/test/remotes/*' ]) + self.assertRaises(TypeError, setattr, remote, 'push_refspecs', '+refs/*:refs/*') + self.assertRaises(TypeError, setattr, remote, 'fetch_refspecs', '+refs/*:refs/*') + self.assertRaises(TypeError, setattr, remote, 'fetch_refspecs', ['+refs/*:refs/*', 5]) + self.assertEqual('+refs/*:refs/remotes/*', remote.get_push_refspecs()[0]) self.assertEqual('+refs/test/*:refs/test/remotes/*', From 3a83cb44b66a261a6234186b830e9d1a5586a452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 24 Jan 2014 08:17:49 +0100 Subject: [PATCH 0652/2237] Oid: Deprecate 'hex' in favour of str() or unicode() Python has built-in functions to get a string representation of an object id, so let's use that instead of using a custom attribute. --- src/oid.c | 14 ++++++++++---- test/test_oid.py | 6 +++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/oid.c b/src/oid.c index 40a5d79e4..81749e51d 100644 --- a/src/oid.c +++ b/src/oid.c @@ -257,6 +257,11 @@ Oid_richcompare(PyObject *o1, PyObject *o2, int op) return res; } +PyObject * +Oid__str__(Oid *self) +{ + return git_oid_to_py_str(&self->oid); +} PyDoc_STRVAR(Oid_raw__doc__, "Raw oid, a 20 bytes string."); @@ -267,12 +272,13 @@ Oid_raw__get__(Oid *self) } -PyDoc_STRVAR(Oid_hex__doc__, "Hex oid, a 40 chars long string (type str)."); +PyDoc_STRVAR(Oid_hex__doc__, "Hex oid, a 40 chars long string (type str).\n" + "This attribute is deprecated, please use the built-int str() or unicode()\n"); PyObject * Oid_hex__get__(Oid *self) { - return git_oid_to_py_str(&self->oid); + return Oid__str__(self); } PyGetSetDef Oid_getseters[] = { @@ -293,13 +299,13 @@ PyTypeObject OidType = { 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ - 0, /* tp_repr */ + (reprfunc)Oid__str__, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)Oid_hash, /* tp_hash */ 0, /* tp_call */ - 0, /* tp_str */ + (reprfunc)Oid__str__, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ diff --git a/test/test_oid.py b/test/test_oid.py index e631399b2..d2d7a9205 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -50,19 +50,19 @@ class OidTest(utils.BareRepoTestCase): def test_raw(self): oid = Oid(raw=RAW) self.assertEqual(oid.raw, RAW) - self.assertEqual(oid.hex, HEX) + self.assertEqual(str(oid), HEX) def test_hex(self): oid = Oid(hex=HEX) self.assertEqual(oid.raw, RAW) - self.assertEqual(oid.hex, HEX) + self.assertEqual(str(oid), HEX) def test_hex_bytes(self): if version_info[0] == 2: hex = bytes(HEX) oid = Oid(hex=hex) self.assertEqual(oid.raw, RAW) - self.assertEqual(oid.hex, HEX) + self.assertEqual(str(oid), HEX) else: hex = bytes(HEX, "ascii") self.assertRaises(TypeError, Oid, hex=hex) From 500a6793c43a7370b1197bec21e1faf030cc080e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 24 Jan 2014 10:46:55 +0100 Subject: [PATCH 0653/2237] Object: move to use an 'id' attribute instead of 'oid' This looks like a left-over from the libgit2 misnaming. The current consensus is that 'oid' is the data type and 'id' is the name of the attribute. --- src/object.c | 17 ++++++++++++++--- test/test_blame.py | 2 +- test/test_blob.py | 6 +++--- test/test_commit.py | 6 +++--- test/test_refs.py | 2 +- test/test_remote.py | 12 ++++++------ test/test_repository.py | 10 +++++----- test/test_tag.py | 2 +- test/test_tree.py | 4 ++-- test/test_treebuilder.py | 6 +++--- 10 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/object.c b/src/object.c index 6e018e564..d608e0c53 100644 --- a/src/object.c +++ b/src/object.c @@ -49,11 +49,11 @@ Object_dealloc(Object* self) } -PyDoc_STRVAR(Object_oid__doc__, +PyDoc_STRVAR(Object_id__doc__, "The object id, an instance of the Oid type."); PyObject * -Object_oid__get__(Object *self) +Object_id__get__(Object *self) { const git_oid *oid; @@ -63,10 +63,20 @@ Object_oid__get__(Object *self) return git_oid_to_python(oid); } +PyDoc_STRVAR(Object_oid__doc__, + "The object id, an instance of the Oid type.\n" + "This attribute is deprecated, please use 'id'\n"); + +PyObject * +Object_oid__get__(Object *self) +{ + return Object_id__get__(self); +} PyDoc_STRVAR(Object_hex__doc__, "Hexadecimal representation of the object id. This is a shortcut for\n" - "Object.oid.hex"); + "Object.oid.hex\n" + "This attribute is deprecated, please use 'id'\n"); PyObject * Object_hex__get__(Object *self) @@ -119,6 +129,7 @@ Object_read_raw(Object *self) PyGetSetDef Object_getseters[] = { GETTER(Object, oid), + GETTER(Object, id), GETTER(Object, hex), GETTER(Object, type), {NULL} diff --git a/test/test_blame.py b/test/test_blame.py index 39dcd863f..1f907740b 100644 --- a/test/test_blame.py +++ b/test/test_blame.py @@ -122,7 +122,7 @@ def test_blame_newest(self): for rev, num_commits in revs: commit = repo.revparse_single(rev) - blame = repo.blame(PATH, newest_commit=commit.oid) + blame = repo.blame(PATH, newest_commit=commit.id) self.assertEqual(len(blame), num_commits) diff --git a/test/test_blob.py b/test/test_blob.py index 30faee4f8..cc6bc0aba 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -50,7 +50,7 @@ class BlobTest(utils.RepoTestCase): def test_read_blob(self): blob = self.repo[BLOB_SHA] self.assertEqual(blob.hex, BLOB_SHA) - sha = blob.oid.hex + sha = blob.id.hex self.assertEqual(sha, BLOB_SHA) self.assertTrue(isinstance(blob, pygit2.Blob)) self.assertFalse(blob.is_binary) @@ -66,7 +66,7 @@ def test_create_blob(self): self.assertTrue(isinstance(blob, pygit2.Blob)) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) - self.assertEqual(blob_oid, blob.oid) + self.assertEqual(blob_oid, blob.id) self.assertEqual( utils.gen_blob_sha1(BLOB_NEW_CONTENT), blob_oid.hex) @@ -83,7 +83,7 @@ def test_create_blob_fromworkdir(self): self.assertTrue(isinstance(blob, pygit2.Blob)) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) - self.assertEqual(blob_oid, blob.oid) + self.assertEqual(blob_oid, blob.id) self.assertEqual( utils.gen_blob_sha1(BLOB_FILE_CONTENT), blob_oid.hex) diff --git a/test/test_commit.py b/test/test_commit.py index a944c40d6..f462f7878 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -42,11 +42,11 @@ class CommitTest(utils.BareRepoTestCase): def test_read_commit(self): commit = self.repo[COMMIT_SHA] - self.assertEqual(COMMIT_SHA, commit.hex) + self.assertEqual(COMMIT_SHA, str(commit.id)) parents = commit.parents self.assertEqual(1, len(parents)) self.assertEqual('c2792cfa289ae6321ecf2cd5806c2194b0fd070c', - parents[0].hex) + str(parents[0].id)) self.assertEqual(None, commit.message_encoding) self.assertEqual(('Second test data commit.\n\n' 'This commit has some additional text.\n'), @@ -62,7 +62,7 @@ def test_read_commit(self): Signature('Dave Borowitz', 'dborowitz@google.com', 1288477363, -420)) self.assertEqual( - '967fce8df97cc71722d3c2a5930ef3e6f1d27b12', commit.tree.hex) + '967fce8df97cc71722d3c2a5930ef3e6f1d27b12', str(commit.tree.id)) def test_new_commit(self): repo = self.repo diff --git a/test/test_refs.py b/test/test_refs.py index 347d38114..bac89db6f 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -213,7 +213,7 @@ def test_create_symbolic_reference(self): def test_get_object(self): repo = self.repo ref = repo.lookup_reference('refs/heads/master') - self.assertEqual(repo[ref.target].oid, ref.get_object().oid) + self.assertEqual(repo[ref.target].id, ref.get_object().id) if __name__ == '__main__': diff --git a/test/test_remote.py b/test/test_remote.py index 13fc93ce3..267c22e97 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -199,27 +199,27 @@ def test_push_fast_forward_commits_to_remote_succeeds(self): tip = self.clone[self.clone.head.target] oid = self.clone.create_commit( 'refs/heads/master', tip.author, tip.author, 'empty commit', - tip.tree.oid, [tip.oid] + tip.tree.id, [tip.id] ) self.remote.push('refs/heads/master') - self.assertEqual(self.origin[self.origin.head.target].oid, oid) + self.assertEqual(self.origin[self.origin.head.target].id, oid) def test_push_when_up_to_date_succeeds(self): self.remote.push('refs/heads/master') - origin_tip = self.origin[self.origin.head.target].oid - clone_tip = self.clone[self.clone.head.target].oid + origin_tip = self.origin[self.origin.head.target].id + clone_tip = self.clone[self.clone.head.target].id self.assertEqual(origin_tip, clone_tip) def test_push_non_fast_forward_commits_to_remote_fails(self): tip = self.origin[self.origin.head.target] oid = self.origin.create_commit( 'refs/heads/master', tip.author, tip.author, 'some commit', - tip.tree.oid, [tip.oid] + tip.tree.id, [tip.id] ) tip = self.clone[self.clone.head.target] oid = self.clone.create_commit( 'refs/heads/master', tip.author, tip.author, 'other commit', - tip.tree.oid, [tip.oid] + tip.tree.id, [tip.id] ) self.assertRaises(pygit2.GitError, self.remote.push, 'refs/heads/master') diff --git a/test/test_repository.py b/test/test_repository.py index 081e792b8..acd54e4b4 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -309,7 +309,7 @@ def test_merge_none(self): def test_merge_uptodate(self): branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533' - branch_oid = self.repo.get(branch_head_hex).oid + branch_oid = self.repo.get(branch_head_hex).id merge_result = self.repo.merge(branch_oid) self.assertTrue(merge_result.is_uptodate) self.assertFalse(merge_result.is_fastforward) @@ -318,7 +318,7 @@ def test_merge_uptodate(self): def test_merge_fastforward(self): branch_head_hex = 'e97b4cfd5db0fb4ebabf4f203979ca4e5d1c7c87' - branch_oid = self.repo.get(branch_head_hex).oid + branch_oid = self.repo.get(branch_head_hex).id merge_result = self.repo.merge(branch_oid) self.assertFalse(merge_result.is_uptodate) self.assertTrue(merge_result.is_fastforward) @@ -329,7 +329,7 @@ def test_merge_fastforward(self): def test_merge_no_fastforward_no_conflicts(self): branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' - branch_oid = self.repo.get(branch_head_hex).oid + branch_oid = self.repo.get(branch_head_hex).id merge_result = self.repo.merge(branch_oid) self.assertFalse(merge_result.is_uptodate) self.assertFalse(merge_result.is_fastforward) @@ -345,7 +345,7 @@ def test_merge_no_fastforward_no_conflicts(self): def test_merge_no_fastforward_conflicts(self): branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' - branch_oid = self.repo.get(branch_head_hex).oid + branch_oid = self.repo.get(branch_head_hex).id merge_result = self.repo.merge(branch_oid) self.assertFalse(merge_result.is_uptodate) self.assertFalse(merge_result.is_fastforward) @@ -365,7 +365,7 @@ def test_merge_invalid_hex(self): def test_merge_already_something_in_index(self): branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' - branch_oid = self.repo.get(branch_head_hex).oid + branch_oid = self.repo.get(branch_head_hex).id with open(os.path.join(self.repo.workdir, 'inindex.txt'), 'w') as f: f.write('new content') self.repo.index.add('inindex.txt') diff --git a/test/test_tag.py b/test/test_tag.py index 9530eb5cc..7acfd3069 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -92,7 +92,7 @@ def test_modify_tag(self): def test_get_object(self): repo = self.repo tag = repo[TAG_SHA] - self.assertEqual(repo[tag.target].oid, tag.get_object().oid) + self.assertEqual(repo[tag.target].id, tag.get_object().id) if __name__ == '__main__': diff --git a/test/test_tree.py b/test/test_tree.py index 507a22bdd..e5838ac88 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -100,8 +100,8 @@ def test_new_tree(self): self.assertEqual(x.filemode, 0o0100644) self.assertEqual(y.filemode, 0o0100755) - self.assertEqual(repo[x.oid].oid, b0) - self.assertEqual(repo[y.oid].oid, b1) + self.assertEqual(repo[x.oid].id, b0) + self.assertEqual(repo[y.oid].id, b1) def test_modify_tree(self): diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index 6b2d99adc..c10c4f322 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -49,7 +49,7 @@ def test_noop_treebuilder(self): result = bld.write() self.assertEqual(len(bld), len(tree)) - self.assertEqual(tree.oid, result) + self.assertEqual(tree.id, result) def test_noop_treebuilder_from_tree(self): @@ -58,7 +58,7 @@ def test_noop_treebuilder_from_tree(self): result = bld.write() self.assertEqual(len(bld), len(tree)) - self.assertEqual(tree.oid, result) + self.assertEqual(tree.id, result) def test_rebuild_treebuilder(self): @@ -72,7 +72,7 @@ def test_rebuild_treebuilder(self): result = bld.write() self.assertEqual(len(bld), len(tree)) - self.assertEqual(tree.oid, result) + self.assertEqual(tree.id, result) if __name__ == '__main__': From 541012818750dc2a549028bf3b4c15a31729d242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 24 Jan 2014 11:25:39 +0100 Subject: [PATCH 0654/2237] TreeEntry: move to use 'id' attribute for the object's id Similar to the Object change, we should be using 'id' when referring to the target's id.x --- src/tree.c | 14 ++++++++++++-- test/test_tree.py | 6 +++--- test/test_treebuilder.py | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/tree.c b/src/tree.c index ca91cc04f..bd2c0870e 100644 --- a/src/tree.c +++ b/src/tree.c @@ -67,10 +67,10 @@ TreeEntry_name__get__(TreeEntry *self) } -PyDoc_STRVAR(TreeEntry_oid__doc__, "Object id."); +PyDoc_STRVAR(TreeEntry_id__doc__, "Object id."); PyObject * -TreeEntry_oid__get__(TreeEntry *self) +TreeEntry_id__get__(TreeEntry *self) { const git_oid *oid; @@ -78,6 +78,15 @@ TreeEntry_oid__get__(TreeEntry *self) return git_oid_to_python(oid); } +PyDoc_STRVAR(TreeEntry_oid__doc__, "Object id.\n" + "This attribute is deprecated. Please use 'id'"); + +PyObject * +TreeEntry_oid__get__(TreeEntry *self) +{ + return TreeEntry_id__get__(self); +} + PyObject * TreeEntry_richcompare(PyObject *a, PyObject *b, int op) { @@ -133,6 +142,7 @@ PyGetSetDef TreeEntry_getseters[] = { GETTER(TreeEntry, filemode), GETTER(TreeEntry, name), GETTER(TreeEntry, oid), + GETTER(TreeEntry, id), GETTER(TreeEntry, hex), {NULL} }; diff --git a/test/test_tree.py b/test/test_tree.py index e5838ac88..25ba7ed66 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -77,7 +77,7 @@ def test_read_subtree(self): subtree_entry = tree['c'] self.assertTreeEntryEqual(subtree_entry, SUBTREE_SHA, 'c', 0o0040000) - subtree = self.repo[subtree_entry.oid] + subtree = self.repo[subtree_entry.id] self.assertEqual(1, len(subtree)) sha = '297efb891a47de80be0cfe9c639e4b8c9b450989' self.assertTreeEntryEqual(subtree[0], sha, 'd', 0o0100644) @@ -100,8 +100,8 @@ def test_new_tree(self): self.assertEqual(x.filemode, 0o0100644) self.assertEqual(y.filemode, 0o0100755) - self.assertEqual(repo[x.oid].id, b0) - self.assertEqual(repo[y.oid].id, b1) + self.assertEqual(repo[x.id].id, b0) + self.assertEqual(repo[y.id].id, b1) def test_modify_tree(self): diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index c10c4f322..5c7fa67ac 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -68,7 +68,7 @@ def test_rebuild_treebuilder(self): name = entry.name self.assertTrue(bld.get(name) is None) bld.insert(name, entry.hex, entry.filemode) - self.assertEqual(bld.get(name).oid, entry.oid) + self.assertEqual(bld.get(name).id, entry.id) result = bld.write() self.assertEqual(len(bld), len(tree)) From 1cc112c32f3706f2caa24262bfd6082641eab059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 25 Jan 2014 04:22:54 +0100 Subject: [PATCH 0655/2237] docs: adjust to recent changes It seems I have been forgetting to update the documentation with the last few changes, so adjust to the oid -> id renaming and add missing attributes to the listings. --- docs/merge.rst | 2 +- docs/objects.rst | 10 ++++++++-- docs/oid.rst | 5 +++-- docs/recipes/git-log.rst | 2 +- docs/remotes.rst | 3 +++ docs/repository.rst | 1 + docs/working-copy.rst | 2 +- 7 files changed, 18 insertions(+), 7 deletions(-) diff --git a/docs/merge.rst b/docs/merge.rst index 82c243a7d..4d98590aa 100644 --- a/docs/merge.rst +++ b/docs/merge.rst @@ -22,7 +22,7 @@ merge with the default ones defined in GIT_MERGE_OPTS_INIT libgit2 constant. Example:: >>> branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533' - >>> branch_oid = self.repo.get(branch_head_hex).oid + >>> branch_oid = self.repo.get(branch_head_hex).id >>> merge_result = self.repo.merge(branch_oid) The MergeResult object diff --git a/docs/objects.rst b/docs/objects.rst index 377829e1d..4c868c95a 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -78,6 +78,7 @@ New objects are created using an specific API we will see later. This is the common interface for all Git objects: +.. autoattribute:: pygit2.Object.id .. autoattribute:: pygit2.Object.oid .. autoattribute:: pygit2.Object.hex .. autoattribute:: pygit2.Object.type @@ -170,10 +171,13 @@ Tree entries ------------ .. autoattribute:: pygit2.TreeEntry.name +.. autoattribute:: pygit2.TreeEntry.id .. autoattribute:: pygit2.TreeEntry.oid .. autoattribute:: pygit2.TreeEntry.hex .. autoattribute:: pygit2.TreeEntry.filemode +:class:`TreeEntry` supports comparison against other tree entries. + Example:: >>> tree = commit.tree @@ -181,7 +185,7 @@ Example:: 6 >>> for entry in tree: # Iteration - ... print(entry.hex, entry.name) + ... print(entry.id, entry.name) ... 7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa .gitignore c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 COPYING @@ -194,7 +198,7 @@ Example:: >>> entry - >>> blob = repo[entry.oid] # Get the object the entry points to + >>> blob = repo[entry.id] # Get the object the entry points to >>> blob @@ -221,7 +225,9 @@ committer and others. .. autoattribute:: pygit2.Commit.message_encoding .. autoattribute:: pygit2.Commit.raw_message .. autoattribute:: pygit2.Commit.tree +.. autoattribute:: pygit2.Commit.tree_id .. autoattribute:: pygit2.Commit.parents +.. autoattribute:: pygit2.Commit.parent_ids .. autoattribute:: pygit2.Commit.commit_time .. autoattribute:: pygit2.Commit.commit_time_offset diff --git a/docs/oid.rst b/docs/oid.rst index 1ffc08c61..c01e1afae 100644 --- a/docs/oid.rst +++ b/docs/oid.rst @@ -61,8 +61,9 @@ The Oid type >>> raw = unhexlify("cff3ceaefc955f0dbe1957017db181bc49913781") >>> oid2 = Oid(raw=raw) -An the other way around, from an Oid object we can get the hexadecimal and raw -forms. +And the other way around, from an Oid object we can get the hexadecimal and raw +forms. You can use the built-in `str()` (or `unicode()` in python 2) to get the +hexadecimal representation of the Oid. .. autoattribute:: pygit2.Oid.hex .. autoattribute:: pygit2.Oid.raw diff --git a/docs/recipes/git-log.rst b/docs/recipes/git-log.rst index b234e0f61..df645a6e6 100644 --- a/docs/recipes/git-log.rst +++ b/docs/recipes/git-log.rst @@ -31,7 +31,7 @@ Traverse commit history .. code-block:: python >>> last = repo[repo.head.target] - >>> for commit in repo.walk(last.oid, pygit2.GIT_SORT_TIME): + >>> for commit in repo.walk(last.id, pygit2.GIT_SORT_TIME): >>> print(commit.message) # or some other operation ---------------------------------------------------------------------- diff --git a/docs/remotes.rst b/docs/remotes.rst index 9a9d7bfaa..089ab4a4e 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -12,7 +12,10 @@ The Remote type .. autoattribute:: pygit2.Remote.name .. autoattribute:: pygit2.Remote.url +.. autoattribute:: pygit2.Remote.push_url .. autoattribute:: pygit2.Remote.refspec_count +.. autoattribute:: pygit2.Remote.push_refspecs +.. autoattribute:: pygit2.Remote.fetch_refspecs .. automethod:: pygit2.Remote.get_push_refspecs .. automethod:: pygit2.Remote.get_fetch_refspecs .. automethod:: pygit2.Remote.set_push_refspecs diff --git a/docs/repository.rst b/docs/repository.rst index ca08f1f4d..c2bd0c20c 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -59,6 +59,7 @@ Below there are some general attributes and methods: .. autoattribute:: pygit2.Repository.workdir .. autoattribute:: pygit2.Repository.is_bare .. autoattribute:: pygit2.Repository.is_empty +.. autoattribute:: pygit2.Repository.default_signature .. automethod:: pygit2.Repository.read .. automethod:: pygit2.Repository.write .. automethod:: pygit2.Repository.reset diff --git a/docs/working-copy.rst b/docs/working-copy.rst index bb10f7156..ab8c6fe23 100644 --- a/docs/working-copy.rst +++ b/docs/working-copy.rst @@ -8,7 +8,7 @@ Index read:: >>> index = repo.index >>> index.read() - >>> oid = index['path/to/file'].oid # from path to object id + >>> oid = index['path/to/file'].id # from path to object id >>> blob = repo[oid] # from object id to object Iterate over all entries of the index:: From c2b2c5dd16f62b4dd94f8d891987530e729e1b7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 4 Nov 2013 01:11:20 +0100 Subject: [PATCH 0656/2237] remote: call user-provided callbacks The user can set 'progress', 'transfer_progress' and 'update_tips' to be notified whenever one of those happen. --- docs/remotes.rst | 3 + src/remote.c | 146 ++++++++++++++++++++++++++++++++++++++------ src/remote.h | 2 + src/repository.c | 1 + src/types.h | 10 ++- test/test_remote.py | 41 +++++++++++++ 6 files changed, 185 insertions(+), 18 deletions(-) diff --git a/docs/remotes.rst b/docs/remotes.rst index 089ab4a4e..b136fee06 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -16,6 +16,9 @@ The Remote type .. autoattribute:: pygit2.Remote.refspec_count .. autoattribute:: pygit2.Remote.push_refspecs .. autoattribute:: pygit2.Remote.fetch_refspecs +.. autoattribute:: pygit2.Remote.progress +.. autoattribute:: pygit2.Remote.transfer_progress +.. autoattribute:: pygit2.Remote.update_tips .. automethod:: pygit2.Remote.get_push_refspecs .. automethod:: pygit2.Remote.get_fetch_refspecs .. automethod:: pygit2.Remote.set_push_refspecs diff --git a/src/remote.c b/src/remote.c index cafa64819..1b825860c 100644 --- a/src/remote.c +++ b/src/remote.c @@ -32,6 +32,7 @@ #include "utils.h" #include "types.h" #include "remote.h" +#include "oid.h" extern PyObject *GitError; @@ -294,6 +295,93 @@ PyTypeObject RefspecType = { 0, /* tp_new */ }; +static int +progress_cb(const char *str, int len, void *data) +{ + Remote *remote = (Remote *) data; + PyObject *arglist, *ret; + + if (remote->progress == NULL) + return 0; + + if (!PyCallable_Check(remote->progress)) { + PyErr_SetString(PyExc_TypeError, "progress callback is not callable"); + return -1; + } + + arglist = Py_BuildValue("(s#)", str, len); + ret = PyObject_CallObject(remote->progress, arglist); + Py_DECREF(arglist); + + if (!ret) + return -1; + + Py_DECREF(ret); + + return 0; +} + +static int +transfer_progress_cb(const git_transfer_progress *stats, void *data) +{ + Remote *remote = (Remote *) data; + PyObject *arglist, *ret; + + if (remote->transfer_progress == NULL) + return 0; + + if (!PyCallable_Check(remote->transfer_progress)) { + PyErr_SetString(PyExc_TypeError, "transfer progress callback is not callable"); + return -1; + } + + arglist = Py_BuildValue("({s:I,s:I,s:n})", + "indexed_objects", stats->indexed_objects, + "received_objects", stats->received_objects, + "received_bytes", stats->received_bytes); + + ret = PyObject_CallObject(remote->transfer_progress, arglist); + Py_DECREF(arglist); + + if (!ret) + return -1; + + Py_DECREF(ret); + + return 0; +} + +static int +update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data) +{ + Remote *remote = (Remote *) data; + PyObject *ret; + PyObject *old, *new; + + if (remote->update_tips == NULL) + return 0; + + if (!PyCallable_Check(remote->update_tips)) { + PyErr_SetString(PyExc_TypeError, "update tips callback is not callable"); + return -1; + } + + old = git_oid_to_python(a); + new = git_oid_to_python(b); + + ret = PyObject_CallFunction(remote->update_tips, "(s,O,O)", refname, old ,new); + + Py_DECREF(old); + Py_DECREF(new); + + if (!ret) + return -1; + + Py_DECREF(ret); + + return 0; +} + PyObject * Remote_init(Remote *self, PyObject *args, PyObject *kwds) { @@ -311,19 +399,37 @@ Remote_init(Remote *self, PyObject *args, PyObject *kwds) if (err < 0) return Error_set(err); + self->progress = NULL; + self->transfer_progress = NULL; + self->update_tips = NULL; + + Remote_set_callbacks(self); return (PyObject*) self; } +void +Remote_set_callbacks(Remote *self) +{ + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; + + self->progress = NULL; + + callbacks.progress = progress_cb; + callbacks.transfer_progress = transfer_progress_cb; + callbacks.update_tips = update_tips_cb; + callbacks.payload = self; + git_remote_set_callbacks(self->remote, &callbacks); +} static void Remote_dealloc(Remote *self) { Py_CLEAR(self->repo); + Py_CLEAR(self->progress); git_remote_free(self->remote); PyObject_Del(self); } - PyDoc_STRVAR(Remote_name__doc__, "Name of the remote refspec"); PyObject * @@ -671,24 +777,23 @@ Remote_fetch(Remote *self, PyObject *args) const git_transfer_progress *stats; int err; - err = git_remote_connect(self->remote, GIT_DIRECTION_FETCH); - if (err == GIT_OK) { - err = git_remote_download(self->remote); - if (err == GIT_OK) { - stats = git_remote_stats(self->remote); - py_stats = Py_BuildValue("{s:I,s:I,s:n}", - "indexed_objects", stats->indexed_objects, - "received_objects", stats->received_objects, - "received_bytes", stats->received_bytes); - - err = git_remote_update_tips(self->remote); - } - git_remote_disconnect(self->remote); - } - + PyErr_Clear(); + err = git_remote_fetch(self->remote); + /* + * XXX: We should be checking for GIT_EUSER, but on v0.20, this does not + * make it all the way to us for update_tips + */ + if (err < 0 && PyErr_Occurred()) + return NULL; if (err < 0) return Error_set(err); + stats = git_remote_stats(self->remote); + py_stats = Py_BuildValue("{s:I,s:I,s:n}", + "indexed_objects", stats->indexed_objects, + "received_objects", stats->received_objects, + "received_bytes", stats->received_bytes); + return (PyObject*) py_stats; } @@ -848,6 +953,13 @@ PyGetSetDef Remote_getseters[] = { {NULL} }; +PyMemberDef Remote_members[] = { + MEMBER(Remote, progress, T_OBJECT_EX, "Progress output callback"), + MEMBER(Remote, transfer_progress, T_OBJECT_EX, "Transfer progress callback"), + MEMBER(Remote, update_tips, T_OBJECT_EX, "update tips callback"), + {NULL}, +}; + PyDoc_STRVAR(Remote__doc__, "Remote object."); PyTypeObject RemoteType = { @@ -879,7 +991,7 @@ PyTypeObject RemoteType = { 0, /* tp_iter */ 0, /* tp_iternext */ Remote_methods, /* tp_methods */ - 0, /* tp_members */ + Remote_members, /* tp_members */ Remote_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ diff --git a/src/remote.h b/src/remote.h index 7f6e131de..0ab41a511 100644 --- a/src/remote.h +++ b/src/remote.h @@ -36,4 +36,6 @@ PyObject* Remote_init(Remote *self, PyObject *args, PyObject *kwds); PyObject* Remote_fetch(Remote *self, PyObject *args); +void Remote_set_callbacks(Remote *self); + #endif diff --git a/src/repository.c b/src/repository.c index fa0dd7ee1..f52dd66d7 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1295,6 +1295,7 @@ Repository_create_remote(Repository *self, PyObject *args) Py_INCREF(self); py_remote->repo = self; py_remote->remote = remote; + Remote_set_callbacks(py_remote); return (PyObject*) py_remote; } diff --git a/src/types.h b/src/types.h index 63a5672d5..c8f7a6f98 100644 --- a/src/types.h +++ b/src/types.h @@ -196,7 +196,15 @@ typedef struct { /* git_remote */ -SIMPLE_TYPE(Remote, git_remote, remote) +typedef struct { + PyObject_HEAD + Repository *repo; + git_remote *remote; + /* Callbacks for network events */ + PyObject *progress; + PyObject *transfer_progress; + PyObject *update_tips; +} Remote; /* git_refspec */ typedef struct { diff --git a/test/test_remote.py b/test/test_remote.py index 267c22e97..ce146e0ed 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -30,6 +30,7 @@ import unittest import pygit2 +from pygit2 import Oid from . import utils REMOTE_NAME = 'origin' @@ -173,6 +174,20 @@ def test_add_refspec(self): self.assertEqual('+refs/heads/*:refs/remotes/test_refspec/*', remote.get_fetch_refspecs()[1]) + def test_remote_callback_typecheck(self): + remote = self.repo.remotes[0] + remote.progress = 5 + self.assertRaises(TypeError, remote, 'fetch') + + remote = self.repo.remotes[0] + remote.transfer_progress = 5 + self.assertRaises(TypeError, remote, 'fetch') + + remote = self.repo.remotes[0] + remote.update_tips = 5 + self.assertRaises(TypeError, remote, 'fetch') + + class EmptyRepositoryTest(utils.EmptyRepoTestCase): def test_fetch(self): @@ -182,6 +197,32 @@ def test_fetch(self): self.assertEqual(stats['indexed_objects'], REMOTE_REPO_OBJECTS) self.assertEqual(stats['received_objects'], REMOTE_REPO_OBJECTS) + def test_transfer_progress(self): + self.tp = None + def tp_cb(stats): + self.tp = stats + + remote = self.repo.remotes[0] + remote.transfer_progress = tp_cb + stats = remote.fetch() + self.assertEqual(stats['received_bytes'], self.tp.received_bytes) + self.assertEqual(stats['indexed_objects'], self.tp.indexed_objects) + self.assertEqual(stats['received_objects'], self.tp.received_objects) + + def test_update_tips(self): + remote = self.repo.remotes[0] + self.i = 0 + self.tips = [('refs/remotes/origin/master', Oid(hex='0'*40), + Oid(hex='784855caf26449a1914d2cf62d12b9374d76ae78')), + ('refs/tags/root', Oid(hex='0'*40), + Oid(hex='3d2962987c695a29f1f80b6c3aa4ec046ef44369'))] + + def ut_cb(name, old, new): + self.assertEqual(self.tips[self.i], (name, old, new)) + self.i += 1 + + remote.update_tips = ut_cb + remote.fetch() class PushTestCase(unittest.TestCase): def setUp(self): From 0bbd15b4f1412a8d08fadc3e06c0384c5c1bf1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 25 Jan 2014 10:20:31 +0100 Subject: [PATCH 0657/2237] TransferProgress: create this type for transfer progress reporting This gets passed to the transfer progress callback, instead of the stripped-down version which Remote.fetch() returns. --- docs/remotes.rst | 14 +++++++ src/pygit2.c | 5 ++- src/remote.c | 95 ++++++++++++++++++++++++++++++++++++++++++++---- src/types.h | 12 ++++++ src/utils.h | 3 ++ 5 files changed, 120 insertions(+), 9 deletions(-) diff --git a/docs/remotes.rst b/docs/remotes.rst index b136fee06..e7c512977 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -28,6 +28,20 @@ The Remote type .. automethod:: pygit2.Remote.push .. automethod:: pygit2.Remote.save +The TransferProgress type +=========================== + +This class contains the data which is available to us during a fetch. + +.. autoattribute:: pygit2.TransferProgress.total_objects +.. autoattribute:: pygit2.TransferProgress.indexed_objects +.. autoattribute:: pygit2.TransferProgress.received_objects +.. autoattribute:: pygit2.TransferProgress.local_objects +.. autoattribute:: pygit2.TransferProgress.total_deltas +.. autoattribute:: pygit2.TransferProgress.indexed_deltas +.. autoattribute:: pygit2.TransferProgress.received_bytes + + The Refspec type =================== diff --git a/src/pygit2.c b/src/pygit2.c index 7c72e509c..e324a64b1 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -64,6 +64,7 @@ extern PyTypeObject BranchType; extern PyTypeObject SignatureType; extern PyTypeObject RemoteType; extern PyTypeObject RefspecType; +extern PyTypeObject TransferProgressType; extern PyTypeObject NoteType; extern PyTypeObject NoteIterType; extern PyTypeObject BlameType; @@ -420,9 +421,11 @@ moduleinit(PyObject* m) /* Remotes */ INIT_TYPE(RemoteType, NULL, NULL) - ADD_TYPE(m, Remote) INIT_TYPE(RefspecType, NULL, NULL) + INIT_TYPE(TransferProgressType, NULL, NULL) + ADD_TYPE(m, Remote) ADD_TYPE(m, Refspec) + ADD_TYPE(m, TransferProgress) /* Direction for the refspec */ ADD_CONSTANT_INT(m, GIT_DIRECTION_FETCH) ADD_CONSTANT_INT(m, GIT_DIRECTION_PUSH) diff --git a/src/remote.c b/src/remote.c index 1b825860c..3ee0ab704 100644 --- a/src/remote.c +++ b/src/remote.c @@ -38,6 +38,7 @@ extern PyObject *GitError; extern PyTypeObject RepositoryType; extern PyTypeObject RefspecType; +extern PyTypeObject TransferProgressType; Refspec * wrap_refspec(const Remote *owner, const git_refspec *refspec) @@ -295,6 +296,87 @@ PyTypeObject RefspecType = { 0, /* tp_new */ }; +PyObject * +wrap_transfer_progress(const git_transfer_progress *stats) +{ + TransferProgress *py_stats; + + py_stats = PyObject_New(TransferProgress, &TransferProgressType); + if (!py_stats) + return NULL; + + py_stats->total_objects = stats->total_objects; + py_stats->indexed_objects = stats->indexed_objects; + py_stats->received_objects = stats->received_objects; + py_stats->local_objects = stats->local_objects; + py_stats->total_deltas = stats->total_deltas; + py_stats->indexed_deltas = stats->indexed_deltas; + py_stats->received_bytes = stats->received_bytes; + + return (PyObject *) py_stats; +} + +void +TransferProgress_dealloc(TransferProgress *self) +{ + PyObject_Del(self); +} + +PyMemberDef TransferProgress_members[] = { + RMEMBER(TransferProgress, total_objects, T_UINT, "Total number objects to download"), + RMEMBER(TransferProgress, indexed_objects, T_UINT, "Objects which have been indexed"), + RMEMBER(TransferProgress, received_objects, T_UINT, "Objects which have been received up to now"), + RMEMBER(TransferProgress, local_objects, T_UINT, "Local objects which were used to fix the thin pack"), + RMEMBER(TransferProgress, total_deltas, T_UINT, "Total number of deltas in the pack"), + RMEMBER(TransferProgress, indexed_deltas, T_UINT, "Deltas which have been indexed"), + /* FIXME: technically this is unsigned, but there's no value for size_t here. */ + RMEMBER(TransferProgress, received_bytes, T_PYSSIZET, "Number of bytes received up to now"), + {NULL}, +}; + +PyDoc_STRVAR(TransferProgress__doc__, "Progress downloading and indexing data during a fetch"); + +PyTypeObject TransferProgressType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.TransferProgress", /* tp_name */ + sizeof(TransferProgress), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)TransferProgress_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + TransferProgress__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + TransferProgress_members, /* tp_members */ + 0, /* 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 */ +}; + static int progress_cb(const char *str, int len, void *data) { @@ -325,7 +407,7 @@ static int transfer_progress_cb(const git_transfer_progress *stats, void *data) { Remote *remote = (Remote *) data; - PyObject *arglist, *ret; + PyObject *py_stats, *ret; if (remote->transfer_progress == NULL) return 0; @@ -335,14 +417,11 @@ transfer_progress_cb(const git_transfer_progress *stats, void *data) return -1; } - arglist = Py_BuildValue("({s:I,s:I,s:n})", - "indexed_objects", stats->indexed_objects, - "received_objects", stats->received_objects, - "received_bytes", stats->received_bytes); - - ret = PyObject_CallObject(remote->transfer_progress, arglist); - Py_DECREF(arglist); + py_stats = wrap_transfer_progress(stats); + if (!py_stats) + return -1; + ret = PyObject_CallFunctionObjArgs(remote->transfer_progress, py_stats, NULL); if (!ret) return -1; diff --git a/src/types.h b/src/types.h index c8f7a6f98..95d2bd2b3 100644 --- a/src/types.h +++ b/src/types.h @@ -213,6 +213,18 @@ typedef struct { const git_refspec *refspec; } Refspec; +/* git_transfer_progress */ +typedef struct { + PyObject_HEAD + unsigned int total_objects; + unsigned int indexed_objects; + unsigned int received_objects; + unsigned int local_objects; + unsigned int total_deltas; + unsigned int indexed_deltas; + size_t received_bytes; +} TransferProgress; + /* git_blame */ SIMPLE_TYPE(Blame, git_blame, blame) diff --git a/src/utils.h b/src/utils.h index 62699e835..66d923453 100644 --- a/src/utils.h +++ b/src/utils.h @@ -133,6 +133,9 @@ char * py_str_to_c_str(PyObject *value, const char *encoding); #define MEMBER(type, attr, attr_type, docstr)\ {#attr, attr_type, offsetof(type, attr), 0, PyDoc_STR(docstr)} +#define RMEMBER(type, attr, attr_type, docstr)\ + {#attr, attr_type, offsetof(type, attr), READONLY, PyDoc_STR(docstr)} + /* Helpers for memory allocation */ #define CALLOC(ptr, num, size, label) \ From 9a428f985c29b1872f723af2e06173e6c809e7dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 25 Jan 2014 10:23:41 +0100 Subject: [PATCH 0658/2237] Refspec: fix copy-paste error --- src/remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index 3ee0ab704..6f473746d 100644 --- a/src/remote.c +++ b/src/remote.c @@ -258,7 +258,7 @@ PyDoc_STRVAR(Refspec__doc__, "Refspec object."); PyTypeObject RefspecType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Refspec", /* tp_name */ - sizeof(Remote), /* tp_basicsize */ + sizeof(Refspec), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Refspec_dealloc, /* tp_dealloc */ 0, /* tp_print */ From 30084e00c4a2f941780ea38fe28c2a268098140a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 26 Jan 2014 09:47:42 +0100 Subject: [PATCH 0659/2237] Add support for pypy Fortunately pypy provides support for a lot of the CPython API, so the changes are minimal. The most important changes are: - constructors always get a keyword argument dictionary, even if no keyword arguments are passed - trying to assign to a read-only attribute raises TypeError instead of AttributeError Apart from that, pypy does not provide MAXPATHLEN. There is a hack in place currently, but there is only place that's using that macro, and there shouldn't be a need for it much longer. This fixes #209. --- src/config.c | 2 +- src/index.c | 2 +- src/pygit2.c | 12 +++++++++++- src/repository.c | 2 +- test/test_commit.py | 19 +++++++++++++------ test/test_tag.py | 15 +++++++++++---- 6 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/config.c b/src/config.c index f7675587c..fdb2593c2 100644 --- a/src/config.c +++ b/src/config.c @@ -59,7 +59,7 @@ Config_init(Config *self, PyObject *args, PyObject *kwds) char *path = NULL; int err; - if (kwds) { + if (kwds && PyDict_Size(kwds) > 0) { PyErr_SetString(PyExc_TypeError, "Config takes no keyword arguments"); return -1; diff --git a/src/index.c b/src/index.c index ce47f13a0..34daf1d1d 100644 --- a/src/index.c +++ b/src/index.c @@ -46,7 +46,7 @@ Index_init(Index *self, PyObject *args, PyObject *kwds) char *path; int err; - if (kwds) { + if (kwds && PyDict_Size(kwds) > 0) { PyErr_SetString(PyExc_TypeError, "Index takes no keyword arguments"); return -1; } diff --git a/src/pygit2.c b/src/pygit2.c index 7c72e509c..466527709 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -27,7 +27,12 @@ #define PY_SSIZE_T_CLEAN #include -#include + +/* Pypy does not provide this header */ +#ifndef PYPY_VERSION +# include +#endif + #include #include "error.h" #include "types.h" @@ -35,6 +40,11 @@ #include "repository.h" #include "oid.h" +/* FIXME: This is for pypy */ +#ifndef MAXPATHLEN +# define MAXPATHLEN 1024 +#endif + extern PyObject *GitError; extern PyTypeObject RepositoryType; diff --git a/src/repository.c b/src/repository.c index fa0dd7ee1..b0464d41f 100644 --- a/src/repository.c +++ b/src/repository.c @@ -77,7 +77,7 @@ Repository_init(Repository *self, PyObject *args, PyObject *kwds) char *path; int err; - if (kwds) { + if (kwds && PyDict_Size(kwds) > 0) { PyErr_SetString(PyExc_TypeError, "Repository takes no keyword arguments"); return -1; diff --git a/test/test_commit.py b/test/test_commit.py index f462f7878..e7d8c5c6f 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -34,6 +34,12 @@ from pygit2 import GIT_OBJ_COMMIT, Signature, Oid from . import utils +# pypy raises TypeError on writing to read-only, so we need to check +# and change the test accordingly +try: + import __pypy__ +except ImportError: + __pypy__ = None COMMIT_SHA = '5fe808e8953c12735680c257f56600cb0de44b10' @@ -131,12 +137,13 @@ def test_modify_commit(self): author = ('Jane Doe', 'jdoe2@example.com', 12345) commit = self.repo[COMMIT_SHA] - self.assertRaises(AttributeError, setattr, commit, 'message', message) - self.assertRaises(AttributeError, setattr, commit, 'committer', - committer) - self.assertRaises(AttributeError, setattr, commit, 'author', author) - self.assertRaises(AttributeError, setattr, commit, 'tree', None) - self.assertRaises(AttributeError, setattr, commit, 'parents', None) + + error_type = AttributeError if not __pypy__ else TypeError + self.assertRaises(error_type, setattr, commit, 'message', message) + self.assertRaises(error_type, setattr, commit, 'committer', committer) + self.assertRaises(error_type, setattr, commit, 'author', author) + self.assertRaises(error_type, setattr, commit, 'tree', None) + self.assertRaises(error_type, setattr, commit, 'parents', None) if __name__ == '__main__': diff --git a/test/test_tag.py b/test/test_tag.py index 7acfd3069..e31f7b0f2 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -34,6 +34,12 @@ import pygit2 from . import utils +# pypy raises TypeError on writing to read-only, so we need to check +# and change the test accordingly +try: + import __pypy__ +except ImportError: + __pypy__ = None TAG_SHA = '3d2962987c695a29f1f80b6c3aa4ec046ef44369' @@ -84,10 +90,11 @@ def test_modify_tag(self): tagger = ('John Doe', 'jdoe@example.com', 12347) tag = self.repo[TAG_SHA] - self.assertRaises(AttributeError, setattr, tag, 'name', name) - self.assertRaises(AttributeError, setattr, tag, 'target', target) - self.assertRaises(AttributeError, setattr, tag, 'tagger', tagger) - self.assertRaises(AttributeError, setattr, tag, 'message', message) + error_type = AttributeError if not __pypy__ else TypeError + self.assertRaises(error_type, setattr, tag, 'name', name) + self.assertRaises(error_type, setattr, tag, 'target', target) + self.assertRaises(error_type, setattr, tag, 'tagger', tagger) + self.assertRaises(error_type, setattr, tag, 'message', message) def test_get_object(self): repo = self.repo From 94e841bfb2eda428477bb47f7b6025539c4e3ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 28 Jan 2014 18:35:56 +0100 Subject: [PATCH 0660/2237] Remove useless constructors The Reference, Branch, ConfigIter and Walker types were allowed to be created by the user, but no constructor was set, and the default values are either meaningless (in the case of Reference/Branch) or would cause a segfault as soon as one tried to use them (ConfigIter and Walker). Remove the constructor from these types, as they don't serve a purpose and can only be used by mistake. --- src/pygit2.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index a33efc640..4cb9b3921 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -312,7 +312,7 @@ moduleinit(PyObject* m) /* * Log */ - INIT_TYPE(WalkerType, NULL, PyType_GenericNew) + INIT_TYPE(WalkerType, NULL, NULL) ADD_TYPE(m, Walker); ADD_CONSTANT_INT(m, GIT_SORT_NONE) ADD_CONSTANT_INT(m, GIT_SORT_TOPOLOGICAL) @@ -329,7 +329,7 @@ moduleinit(PyObject* m) /* * References */ - INIT_TYPE(ReferenceType, NULL, PyType_GenericNew) + INIT_TYPE(ReferenceType, NULL, NULL) INIT_TYPE(RefLogEntryType, NULL, NULL) INIT_TYPE(RefLogIterType, NULL, NULL) INIT_TYPE(NoteType, NULL, NULL) @@ -345,7 +345,7 @@ moduleinit(PyObject* m) /* * Branches */ - INIT_TYPE(BranchType, &ReferenceType, PyType_GenericNew); + INIT_TYPE(BranchType, &ReferenceType, NULL); ADD_TYPE(m, Branch) ADD_CONSTANT_INT(m, GIT_BRANCH_LOCAL) ADD_CONSTANT_INT(m, GIT_BRANCH_REMOTE) @@ -425,7 +425,7 @@ moduleinit(PyObject* m) /* Config */ INIT_TYPE(ConfigType, NULL, PyType_GenericNew) - INIT_TYPE(ConfigIterType, NULL, PyType_GenericNew) + INIT_TYPE(ConfigIterType, NULL, NULL) ADD_TYPE(m, Config) ADD_TYPE(m, ConfigIter) From 5b4b7f39d57f51ae5893dd8f3f66b52701e390e9 Mon Sep 17 00:00:00 2001 From: XTao Date: Wed, 29 Jan 2014 15:14:57 +0800 Subject: [PATCH 0661/2237] Add wrap remote. --- src/remote.c | 20 +++++++++++++++++++- src/remote.h | 1 + src/repository.c | 20 +++++++------------- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/remote.c b/src/remote.c index 6f473746d..8201a9035 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1036,7 +1036,7 @@ PyMemberDef Remote_members[] = { MEMBER(Remote, progress, T_OBJECT_EX, "Progress output callback"), MEMBER(Remote, transfer_progress, T_OBJECT_EX, "Transfer progress callback"), MEMBER(Remote, update_tips, T_OBJECT_EX, "update tips callback"), - {NULL}, + {NULL}, }; PyDoc_STRVAR(Remote__doc__, "Remote object."); @@ -1081,3 +1081,21 @@ PyTypeObject RemoteType = { 0, /* tp_alloc */ 0, /* tp_new */ }; + +PyObject * +wrap_remote(git_remote *c_remote, Repository *repo) +{ + Remote *py_remote = NULL; + py_remote = PyObject_New(Remote, &RemoteType); + if (py_remote) { + Py_INCREF(repo); + py_remote->repo = repo; + py_remote->remote = c_remote; + py_remote->progress = NULL; + py_remote->transfer_progress = NULL; + py_remote->update_tips = NULL; + Remote_set_callbacks(py_remote); + } + + return (PyObject *)py_remote; +} diff --git a/src/remote.h b/src/remote.h index 0ab41a511..1deedcd50 100644 --- a/src/remote.h +++ b/src/remote.h @@ -37,5 +37,6 @@ PyObject* Remote_init(Remote *self, PyObject *args, PyObject *kwds); PyObject* Remote_fetch(Remote *self, PyObject *args); void Remote_set_callbacks(Remote *self); +PyObject *wrap_remote(git_remote *c_remote, Repository *repo); #endif diff --git a/src/repository.c b/src/repository.c index 19ee57da7..c1fa44ccd 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1279,7 +1279,6 @@ PyDoc_STRVAR(Repository_create_remote__doc__, PyObject * Repository_create_remote(Repository *self, PyObject *args) { - Remote *py_remote; git_remote *remote; char *name = NULL, *url = NULL; int err; @@ -1291,13 +1290,7 @@ Repository_create_remote(Repository *self, PyObject *args) if (err < 0) return Error_set(err); - py_remote = PyObject_New(Remote, &RemoteType); - Py_INCREF(self); - py_remote->repo = self; - py_remote->remote = remote; - Remote_set_callbacks(py_remote); - - return (PyObject*) py_remote; + return (PyObject*) wrap_remote(remote, self); } @@ -1307,18 +1300,19 @@ PyObject * Repository_remotes__get__(Repository *self) { git_strarray remotes; + git_remote *remote = NULL; PyObject* py_list = NULL, *py_args = NULL; - Remote *py_remote; size_t i; + int err; git_remote_list(&remotes, self->repo); py_list = PyList_New(remotes.count); for (i=0; i < remotes.count; ++i) { - py_remote = PyObject_New(Remote, &RemoteType); - py_args = Py_BuildValue("Os", self, remotes.strings[i]); - Remote_init(py_remote, py_args, NULL); - PyList_SetItem(py_list, i, (PyObject*) py_remote); + err = git_remote_load(&remote, self->repo, remotes.strings[i]); + if (err < 0) + return Error_set(err); + PyList_SetItem(py_list, i, wrap_remote(remote, self)); } git_strarray_free(&remotes); From 9c95cb056048c53efbcaf955c648d397770e227f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 21 Jan 2014 02:58:30 +0100 Subject: [PATCH 0662/2237] IndexEntry: keep a copy of the underlying git_index_entry The tree entries exist more or less independently of the index they were retrieved from. The idea behind them is that they can be kept by a binding without needing to refer to the original anymore, which may disappear at any moment if the index is modified. Keep a copy of the data in TreeEntry instead of pointing to the one retrieved from the index, which is not safe to keep around. --- src/index.c | 19 +++++++++++++------ src/types.h | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/index.c b/src/index.c index ce47f13a0..d9a12d84d 100644 --- a/src/index.c +++ b/src/index.c @@ -314,8 +314,15 @@ wrap_index_entry(const git_index_entry *entry, Index *index) IndexEntry *py_entry; py_entry = PyObject_New(IndexEntry, &IndexEntryType); - if (py_entry) - py_entry->entry = entry; + if (!py_entry) + return NULL; + + memcpy(&py_entry->entry, entry, sizeof(struct git_index_entry)); + py_entry->entry.path = strdup(entry->path); + if (!py_entry->entry.path) { + Py_CLEAR(py_entry); + return NULL; + } return (PyObject*)py_entry; } @@ -569,7 +576,7 @@ PyDoc_STRVAR(IndexEntry_mode__doc__, "Mode."); PyObject * IndexEntry_mode__get__(IndexEntry *self) { - return PyLong_FromLong(self->entry->mode); + return PyLong_FromLong(self->entry.mode); } @@ -578,7 +585,7 @@ PyDoc_STRVAR(IndexEntry_path__doc__, "Path."); PyObject * IndexEntry_path__get__(IndexEntry *self) { - return to_path(self->entry->path); + return to_path(self->entry.path); } @@ -587,7 +594,7 @@ PyDoc_STRVAR(IndexEntry_oid__doc__, "Object id."); PyObject * IndexEntry_oid__get__(IndexEntry *self) { - return git_oid_to_python(&self->entry->oid); + return git_oid_to_python(&self->entry.oid); } @@ -596,7 +603,7 @@ PyDoc_STRVAR(IndexEntry_hex__doc__, "Hex id."); PyObject * IndexEntry_hex__get__(IndexEntry *self) { - return git_oid_to_py_str(&self->entry->oid); + return git_oid_to_py_str(&self->entry.oid); } PyGetSetDef IndexEntry_getseters[] = { diff --git a/src/types.h b/src/types.h index 63a5672d5..1918c9d24 100644 --- a/src/types.h +++ b/src/types.h @@ -153,7 +153,7 @@ SIMPLE_TYPE(Index, git_index, index) typedef struct { PyObject_HEAD - const git_index_entry *entry; + git_index_entry entry; } IndexEntry; typedef struct { From d98a7014772e453f16fc9c5b2820b592e0ea780d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 21 Jan 2014 03:38:22 +0100 Subject: [PATCH 0663/2237] IndexEntry: allow creation of this object They are not required to belong to a particular index, so it should be possible to create them at runtime in order to insert them. --- src/index.c | 28 +++++++++++++++++++++++++++- src/pygit2.c | 2 +- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/index.c b/src/index.c index d9a12d84d..9add4a912 100644 --- a/src/index.c +++ b/src/index.c @@ -39,6 +39,7 @@ extern PyTypeObject TreeType; extern PyTypeObject DiffType; extern PyTypeObject IndexIterType; extern PyTypeObject IndexEntryType; +extern PyTypeObject OidType; int Index_init(Index *self, PyObject *args, PyObject *kwds) @@ -564,6 +565,31 @@ PyTypeObject IndexIterType = { (iternextfunc)IndexIter_iternext, /* tp_iternext */ }; +int +IndexEntry_init(IndexEntry *self, PyObject *args, PyObject *kwds) +{ + char *c_path = NULL; + Oid *id = NULL; + unsigned int mode; + char *keywords[] = {"path", "oid", "mode", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO!I", keywords, + &c_path, &OidType, &id, &mode)) + return -1; + + memset(&self->entry, 0, sizeof(struct git_index_entry)); + if (c_path) + self->entry.path = c_path; + + if (id) + git_oid_cpy(&self->entry.oid, &id->oid); + + if (mode) + self->entry.mode = mode; + + return 0; +} + void IndexEntry_dealloc(IndexEntry *self) { @@ -652,7 +678,7 @@ PyTypeObject IndexEntryType = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - 0, /* tp_init */ + (initproc)IndexEntry_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; diff --git a/src/pygit2.c b/src/pygit2.c index 7c72e509c..aeb5d8f67 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -343,7 +343,7 @@ moduleinit(PyObject* m) * Index & Working copy */ INIT_TYPE(IndexType, NULL, PyType_GenericNew) - INIT_TYPE(IndexEntryType, NULL, NULL) + INIT_TYPE(IndexEntryType, NULL, PyType_GenericNew) INIT_TYPE(IndexIterType, NULL, NULL) ADD_TYPE(m, Index) ADD_TYPE(m, IndexEntry) From f79ae6b42166638788e0747fcb57957d7910ddf5 Mon Sep 17 00:00:00 2001 From: XTao Date: Wed, 29 Jan 2014 18:09:55 +0800 Subject: [PATCH 0664/2237] Drop Remote_init. --- src/remote.c | 50 +++++++++--------------------------------------- src/remote.h | 1 - src/repository.c | 19 ++++++++++++++---- 3 files changed, 24 insertions(+), 46 deletions(-) diff --git a/src/remote.c b/src/remote.c index 8201a9035..3f2388cb4 100644 --- a/src/remote.c +++ b/src/remote.c @@ -461,45 +461,6 @@ update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *da return 0; } -PyObject * -Remote_init(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; - Py_INCREF(self->repo); - err = git_remote_load(&self->remote, py_repo->repo, name); - - if (err < 0) - return Error_set(err); - - self->progress = NULL; - self->transfer_progress = NULL; - self->update_tips = NULL; - - Remote_set_callbacks(self); - return (PyObject*) self; -} - -void -Remote_set_callbacks(Remote *self) -{ - git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; - - self->progress = NULL; - - callbacks.progress = progress_cb; - callbacks.transfer_progress = transfer_progress_cb; - callbacks.update_tips = update_tips_cb; - callbacks.payload = self; - git_remote_set_callbacks(self->remote, &callbacks); -} - static void Remote_dealloc(Remote *self) { @@ -1077,7 +1038,7 @@ PyTypeObject RemoteType = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)Remote_init, /* tp_init */ + 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; @@ -1086,6 +1047,8 @@ PyObject * wrap_remote(git_remote *c_remote, Repository *repo) { Remote *py_remote = NULL; + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; + py_remote = PyObject_New(Remote, &RemoteType); if (py_remote) { Py_INCREF(repo); @@ -1094,7 +1057,12 @@ wrap_remote(git_remote *c_remote, Repository *repo) py_remote->progress = NULL; py_remote->transfer_progress = NULL; py_remote->update_tips = NULL; - Remote_set_callbacks(py_remote); + + callbacks.progress = progress_cb; + callbacks.transfer_progress = transfer_progress_cb; + callbacks.update_tips = update_tips_cb; + callbacks.payload = py_remote; + git_remote_set_callbacks(c_remote, &callbacks); } return (PyObject *)py_remote; diff --git a/src/remote.h b/src/remote.h index 1deedcd50..426314038 100644 --- a/src/remote.h +++ b/src/remote.h @@ -33,7 +33,6 @@ #include #include -PyObject* Remote_init(Remote *self, PyObject *args, PyObject *kwds); PyObject* Remote_fetch(Remote *self, PyObject *args); void Remote_set_callbacks(Remote *self); diff --git a/src/repository.c b/src/repository.c index c1fa44ccd..00d9cc55e 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1301,7 +1301,8 @@ Repository_remotes__get__(Repository *self) { git_strarray remotes; git_remote *remote = NULL; - PyObject* py_list = NULL, *py_args = NULL; + PyObject *py_list = NULL; + PyObject *py_remote = NULL; size_t i; int err; @@ -1311,13 +1312,23 @@ Repository_remotes__get__(Repository *self) for (i=0; i < remotes.count; ++i) { err = git_remote_load(&remote, self->repo, remotes.strings[i]); if (err < 0) - return Error_set(err); - PyList_SetItem(py_list, i, wrap_remote(remote, self)); + goto cleanup; + py_remote = wrap_remote(remote, self); + if (py_remote == NULL) + goto cleanup; + PyList_SetItem(py_list, i, py_remote); } git_strarray_free(&remotes); - return (PyObject*) py_list; + +cleanup: + git_strarray_free(&remotes); + if (py_list) + Py_DECREF(py_list); + if (err < 0) + return Error_set(err); + return NULL; } PyDoc_STRVAR(Repository_default_signature__doc__, "Return the signature according to the repository's configuration"); From 5a785ba976bd3b74c43a6d14fef2dfe15ddb62b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 29 Jan 2014 11:26:13 +0100 Subject: [PATCH 0665/2237] Remove left over declaration of Remote_set_callbacks --- src/remote.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/remote.h b/src/remote.h index 426314038..1c8605f8e 100644 --- a/src/remote.h +++ b/src/remote.h @@ -34,8 +34,6 @@ #include PyObject* Remote_fetch(Remote *self, PyObject *args); - -void Remote_set_callbacks(Remote *self); -PyObject *wrap_remote(git_remote *c_remote, Repository *repo); +PyObject* wrap_remote(git_remote *c_remote, Repository *repo); #endif From f6389ee2c30ea0807a86742736e6adfc39f6f7b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 21 Jan 2014 04:06:31 +0100 Subject: [PATCH 0666/2237] IndexEntry: make the attributes writable When updating entries in an index, it is necessary to modify the attributes of tree entries. make it possible to do so. --- src/index.c | 47 +++++++++++++++++++++++++++++++++++++++++++--- test/test_index.py | 13 +++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/index.c b/src/index.c index 9add4a912..ac08898a3 100644 --- a/src/index.c +++ b/src/index.c @@ -605,6 +605,19 @@ IndexEntry_mode__get__(IndexEntry *self) return PyLong_FromLong(self->entry.mode); } +int +IndexEntry_mode__set__(IndexEntry *self, PyObject *py_mode) +{ + long c_val; + + c_val = PyLong_AsLong(py_mode); + if (c_val == -1 && PyErr_Occurred()) + return -1; + + self->entry.mode = (unsigned int) c_val; + + return 0; +} PyDoc_STRVAR(IndexEntry_path__doc__, "Path."); @@ -614,6 +627,26 @@ IndexEntry_path__get__(IndexEntry *self) return to_path(self->entry.path); } +int +IndexEntry_path__set__(IndexEntry *self, PyObject *py_path) +{ + char *c_inner, *c_path; + + c_inner = py_str_to_c_str(py_path, NULL); + if (!c_inner) + return -1; + + c_path = strdup(c_inner); + if (!c_path) { + PyErr_NoMemory(); + return -1; + } + + free(self->entry.path); + self->entry.path = c_path; + + return 0; +} PyDoc_STRVAR(IndexEntry_oid__doc__, "Object id."); @@ -623,6 +656,14 @@ IndexEntry_oid__get__(IndexEntry *self) return git_oid_to_python(&self->entry.oid); } +int +IndexEntry_oid__set__(IndexEntry *self, PyObject *py_id) +{ + if (!py_oid_to_git_oid(py_id, &self->entry.oid)) + return -1; + + return 0; +} PyDoc_STRVAR(IndexEntry_hex__doc__, "Hex id."); @@ -633,9 +674,9 @@ IndexEntry_hex__get__(IndexEntry *self) } PyGetSetDef IndexEntry_getseters[] = { - GETTER(IndexEntry, mode), - GETTER(IndexEntry, path), - GETTER(IndexEntry, oid), + GETSET(IndexEntry, mode), + GETSET(IndexEntry, path), + GETSET(IndexEntry, oid), GETTER(IndexEntry, hex), {NULL}, }; diff --git a/test/test_index.py b/test/test_index.py index 8613020e4..47b98b090 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -139,6 +139,19 @@ def test_remove(self): index.remove('hello.txt') self.assertFalse('hello.txt' in index) + def test_change_attributes(self): + index = self.repo.index + entry = index['hello.txt'] + ign_entry = index['.gitignore'] + self.assertNotEqual(ign_entry.oid, entry.oid) + self.assertNotEqual(entry.mode, pygit2.GIT_FILEMODE_BLOB_EXECUTABLE) + entry.path = 'foo.txt' + entry.oid = ign_entry.oid + entry.mode = pygit2.GIT_FILEMODE_BLOB_EXECUTABLE + self.assertEqual('foo.txt', entry.path) + self.assertEqual(ign_entry.oid, entry.oid) + self.assertEqual(pygit2.GIT_FILEMODE_BLOB_EXECUTABLE, entry.mode) + if __name__ == '__main__': unittest.main() From c43c320c3e099901efc5591ec21535a47a06dfc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 21 Jan 2014 04:47:57 +0100 Subject: [PATCH 0667/2237] Index: accept adding either a path or an IndexEntry A path is only useful if we have the file on the worktree. Passing an IndexEntry allows us to add an entry with arbitrary attributes. --- docs/working-copy.rst | 3 +++ src/index.c | 12 +++++++++++- test/test_index.py | 9 +++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/working-copy.rst b/docs/working-copy.rst index ab8c6fe23..76bcada87 100644 --- a/docs/working-copy.rst +++ b/docs/working-copy.rst @@ -22,6 +22,9 @@ Index write:: >>> del index['path/to/file'] # git rm >>> index.write() # don't forget to save the changes +Custom entries:: + >>> entry = pygit2.IndexEntry('README.md', blob_id, blob_filemode) + >>> repo.index.add(entry) The Index type ==================== diff --git a/src/index.c b/src/index.c index ac08898a3..e0b0ed4c4 100644 --- a/src/index.c +++ b/src/index.c @@ -82,7 +82,7 @@ Index_traverse(Index *self, visitproc visit, void *arg) PyDoc_STRVAR(Index_add__doc__, - "add(path)\n" + "add([path|entry])\n" "\n" "Add or update an index entry from a file in disk."); @@ -91,6 +91,16 @@ Index_add(Index *self, PyObject *args) { int err; const char *path; + IndexEntry *py_entry; + + if (PyArg_ParseTuple(args, "O!", &IndexEntryType, &py_entry)) { + err = git_index_add(self->index, &py_entry->entry); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; + } + PyErr_Clear(); if (!PyArg_ParseTuple(args, "s", &path)) return NULL; diff --git a/test/test_index.py b/test/test_index.py index 47b98b090..6242efa79 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -152,6 +152,15 @@ def test_change_attributes(self): self.assertEqual(ign_entry.oid, entry.oid) self.assertEqual(pygit2.GIT_FILEMODE_BLOB_EXECUTABLE, entry.mode) +class IndexEntryTest(utils.RepoTestCase): + + def test_create_entry(self): + index = self.repo.index + hello_entry = index['hello.txt'] + entry = pygit2.IndexEntry('README.md', hello_entry.oid, hello_entry.mode) + index.add(entry) + tree_id = index.write_tree() + self.assertEqual('60e769e57ae1d6a2ab75d8d253139e6260e1f912', str(tree_id)) if __name__ == '__main__': unittest.main() From f3f3d28637b1a183f31cc920d93ec22227674770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 26 Jan 2014 05:00:45 +0100 Subject: [PATCH 0668/2237] Index: allow writing the tree to a particular repository Take an optional repository in Index.write_tree() to serialize to a tree into a particular repository's odb. --- src/index.c | 20 +++++++++++++++----- src/index.h | 2 +- test/test_index.py | 9 +++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/index.c b/src/index.c index e0b0ed4c4..2149a70e2 100644 --- a/src/index.c +++ b/src/index.c @@ -40,6 +40,7 @@ extern PyTypeObject DiffType; extern PyTypeObject IndexIterType; extern PyTypeObject IndexEntryType; extern PyTypeObject OidType; +extern PyTypeObject RepositoryType; int Index_init(Index *self, PyObject *args, PyObject *kwds) @@ -428,17 +429,26 @@ Index_read_tree(Index *self, PyObject *value) PyDoc_STRVAR(Index_write_tree__doc__, - "write_tree() -> Oid\n" + "write_tree([repo]) -> Oid\n" "\n" - "Create a tree object from the index file, return its oid."); + "Create a tree object from the index file, return its oid.\n" + "If 'repo' is passed, write to that repository's odb."); PyObject * -Index_write_tree(Index *self) +Index_write_tree(Index *self, PyObject *args) { git_oid oid; + Repository *repo = NULL; int err; - err = git_index_write_tree(&oid, self->index); + if (!PyArg_ParseTuple(args, "|O!", &RepositoryType, &repo)) + return NULL; + + if (repo) + err = git_index_write_tree_to(&oid, self->index, repo->repo); + else + err = git_index_write_tree(&oid, self->index); + if (err < 0) return Error_set(err); @@ -455,7 +465,7 @@ PyMethodDef Index_methods[] = { METHOD(Index, read, METH_VARARGS), METHOD(Index, write, METH_NOARGS), METHOD(Index, read_tree, METH_O), - METHOD(Index, write_tree, METH_NOARGS), + METHOD(Index, write_tree, METH_VARARGS), {NULL} }; diff --git a/src/index.h b/src/index.h index f38cb4a04..e59262ca6 100644 --- a/src/index.h +++ b/src/index.h @@ -40,7 +40,7 @@ PyObject* Index_write(Index *self); PyObject* Index_iter(Index *self); PyObject* Index_getitem(Index *self, PyObject *value); PyObject* Index_read_tree(Index *self, PyObject *value); -PyObject* Index_write_tree(Index *self); +PyObject* Index_write_tree(Index *self, PyObject *args); Py_ssize_t Index_len(Index *self); int Index_setitem(Index *self, PyObject *key, PyObject *value); diff --git a/test/test_index.py b/test/test_index.py index 6242efa79..1afdf56ce 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -31,6 +31,7 @@ from __future__ import unicode_literals import os import unittest +import tempfile import pygit2 from . import utils @@ -152,6 +153,14 @@ def test_change_attributes(self): self.assertEqual(ign_entry.oid, entry.oid) self.assertEqual(pygit2.GIT_FILEMODE_BLOB_EXECUTABLE, entry.mode) + def test_write_tree_to(self): + path = tempfile.mkdtemp() + pygit2.init_repository(path) + nrepo = pygit2.Repository(path) + + id = self.repo.index.write_tree(nrepo) + self.assertNotEqual(None, nrepo[id]) + class IndexEntryTest(utils.RepoTestCase): def test_create_entry(self): From b9bf1175e266d387c028caf485c587e9c72b15cd Mon Sep 17 00:00:00 2001 From: Alexander Bayandin Date: Sun, 2 Feb 2014 12:11:19 +0300 Subject: [PATCH 0669/2237] Run tests on travis CI for pypy Because #209 is done --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 2279b1581..f2ffa47a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ python: - "2.7" - "3.2" - "3.3" + - "pypy" env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib From 977c315c21a81db5da319336b9296eff7cd12d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 2 Feb 2014 14:05:43 +0100 Subject: [PATCH 0670/2237] Preparing for release --- README.rst | 51 +++++++++++++++++++++++++-------- docs/config.rst | 15 +++++----- docs/objects.rst | 10 ++++--- docs/oid.rst | 2 +- docs/remotes.rst | 6 ++-- src/remote.c | 69 ++++----------------------------------------- src/tree.c | 6 ++-- test/test_remote.py | 50 +++++++++++++++----------------- 8 files changed, 89 insertions(+), 120 deletions(-) diff --git a/README.rst b/README.rst index 5d116a7e0..53d69ab59 100644 --- a/README.rst +++ b/README.rst @@ -79,6 +79,43 @@ Authors Changelog ============== +0.20.2 (2014-02-XX) +------------------- + +- New ``Blob.diff(...)`` and ``Blob.diff_to_buffer(...)`` + `#307 `_ + +- New ``Repository.default_signature`` + `#310 `_ + +- New ``Commit.tree_id`` and ``Commit.parent_ids`` + `#730 `_ + + +- New rich comparison between tree entries +- New ``Config`` iterator replaces ``Config.foreach`` +- New type ``Refspec`` +- New ``Remote.push_url`` +- New ``Remote.add_push`` and ``Remote.add_fetch`` +- New ``Remote.fetch_refspecs`` replaces ``Remote.get_fetch_refspecs()`` and + ``Remote.set_fetch_refspecs(...)`` +- New ``Remote.push_refspecs`` replaces ``Remote.get_push_refspecs()`` and + ``Remote.set_push_refspecs(...)`` +- Now *path* in ``Tree`` works +- New ``str(Oid)`` deprecates ``Oid.hex`` +- New ``Object.id`` deprecates ``Object.oid`` +- New ``TreeEntry.id`` deprecates ``TreeEntry.oid`` +- New ``Remote.progress``, ``Remote.transfer_progress`` and + ``Remote.update_tips`` +- New type ``TransferProgress`` +- Now possible to create ``IndexEntry(...)`` +- Now ``IndexEntry.path``, ``IndexEntry.oid`` and ``IndexEntry.mode`` are + writable +- Now ``Index.add(...)`` accepts an ``IndexEntry`` too +- Now ``Index.write_tree(...)`` is able to write to a different repository +- Support pypy + + 0.20.1 (2013-12-24) ------------------- @@ -117,18 +154,10 @@ Changelog - Upgrade to libgit2 v0.20.0: `#288 `_ - Rename ``Repository.head_is_orphaned`` to ``Repository.head_is_unborn`` - - Prototype of ``pygit2.clone_repository(...)`` changed:: - - # Before - pygit2.clone_repository(url, path, bare=False, remote_name='origin', - push_url=None, fetch_spec=None, push_spec=None, - checkout_branch=None) +- New ``Repository.head_is_unborn`` replaces ``Repository.head_is_orphaned`` - # Now - pygit2.clone_repository(url, path, bare=False, ignore_cert_errors=False, - remote_name='origin', checkout_branch=None) +- Changed ``pygit2.clone_repository(...)``. Drop ``push_url``, ``fetch_spec`` + and ``push_spec`` parameters. Add ``ignore_cert_errors``. - New ``Patch.additions`` and ``Patch.deletions``: `#275 `_ diff --git a/docs/config.rst b/docs/config.rst index ef3a1a54f..e9b5c647b 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -14,13 +14,12 @@ The Config type .. automethod:: pygit2.Config.get_multivar .. automethod:: pygit2.Config.set_multivar -The :class:`Config` Mapping interface. +.. method:: for (name, value) in Config -Iterator -========= + The :class:`Config` class has an iterator which can be used to loop + through all the entries in the configuration. Each element is a tuple + containing the name and the value of each configuration variable. Be + aware that this may return multiple versions of each entry if they are + set multiple times in the configuration files. -The :class:`Config` class has an iterator which can be used to loop -through all the entries in the configuration. Each element is a tuple -containing the name and the value of each configuration variable. Be -aware that this may return multiple versions of each entry if they are -set multiple times in the configuration files. +The :class:`Config` Mapping interface. diff --git a/docs/objects.rst b/docs/objects.rst index 4c868c95a..432481b4f 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -79,8 +79,6 @@ New objects are created using an specific API we will see later. This is the common interface for all Git objects: .. autoattribute:: pygit2.Object.id -.. autoattribute:: pygit2.Object.oid -.. autoattribute:: pygit2.Object.hex .. autoattribute:: pygit2.Object.type .. automethod:: pygit2.Object.read_raw @@ -112,6 +110,9 @@ This is their API: .. autoattribute:: pygit2.Blob.is_binary +.. automethod:: pygit2.Blob.diff +.. automethod:: pygit2.Blob.diff_to_buffer + Creating blobs -------------- @@ -172,11 +173,12 @@ Tree entries .. autoattribute:: pygit2.TreeEntry.name .. autoattribute:: pygit2.TreeEntry.id -.. autoattribute:: pygit2.TreeEntry.oid .. autoattribute:: pygit2.TreeEntry.hex .. autoattribute:: pygit2.TreeEntry.filemode -:class:`TreeEntry` supports comparison against other tree entries. +.. method:: cmp(TreeEntry, TreeEntry) + + Rich comparison between tree entries. Example:: diff --git a/docs/oid.rst b/docs/oid.rst index c01e1afae..66adcdb42 100644 --- a/docs/oid.rst +++ b/docs/oid.rst @@ -65,7 +65,7 @@ And the other way around, from an Oid object we can get the hexadecimal and raw forms. You can use the built-in `str()` (or `unicode()` in python 2) to get the hexadecimal representation of the Oid. -.. autoattribute:: pygit2.Oid.hex +.. automethod:: str(pygit2.Oid) .. autoattribute:: pygit2.Oid.raw The Oid type supports: diff --git a/docs/remotes.rst b/docs/remotes.rst index e7c512977..f77bd7393 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -19,14 +19,12 @@ The Remote type .. autoattribute:: pygit2.Remote.progress .. autoattribute:: pygit2.Remote.transfer_progress .. autoattribute:: pygit2.Remote.update_tips -.. automethod:: pygit2.Remote.get_push_refspecs -.. automethod:: pygit2.Remote.get_fetch_refspecs -.. automethod:: pygit2.Remote.set_push_refspecs -.. automethod:: pygit2.Remote.set_fetch_refspecs .. automethod:: pygit2.Remote.get_refspec .. automethod:: pygit2.Remote.fetch .. automethod:: pygit2.Remote.push .. automethod:: pygit2.Remote.save +.. automethod:: pygit2.Remote.add_push +.. automethod:: pygit2.Remote.add_fetch The TransferProgress type =========================== diff --git a/src/remote.c b/src/remote.c index 3f2388cb4..63d11c8ca 100644 --- a/src/remote.c +++ b/src/remote.c @@ -43,9 +43,9 @@ extern PyTypeObject TransferProgressType; Refspec * wrap_refspec(const Remote *owner, const git_refspec *refspec) { - Refspec *spec; + Refspec *spec; - spec = PyObject_New(Refspec, &RefspecType); + spec = PyObject_New(Refspec, &RefspecType); if (!spec) return NULL; @@ -258,7 +258,7 @@ PyDoc_STRVAR(Refspec__doc__, "Refspec object."); PyTypeObject RefspecType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Refspec", /* tp_name */ - sizeof(Refspec), /* tp_basicsize */ + sizeof(Refspec), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Refspec_dealloc, /* tp_dealloc */ 0, /* tp_print */ @@ -331,7 +331,7 @@ PyMemberDef TransferProgress_members[] = { RMEMBER(TransferProgress, indexed_deltas, T_UINT, "Deltas which have been indexed"), /* FIXME: technically this is unsigned, but there's no value for size_t here. */ RMEMBER(TransferProgress, received_bytes, T_PYSSIZET, "Number of bytes received up to now"), - {NULL}, + {NULL}, }; PyDoc_STRVAR(TransferProgress__doc__, "Progress downloading and indexing data during a fetch"); @@ -639,59 +639,6 @@ Remote_push_refspecs__set__(Remote *self, PyObject *py_list) return 0; } -PyDoc_STRVAR(Remote_get_fetch_refspecs__doc__, - "Fetch refspecs.\n" - "This function is deprecated, please use the fetch_refspecs attribute" - "\n"); - - -PyObject * -Remote_get_fetch_refspecs(Remote *self) -{ - return Remote_fetch_refspecs__get__(self); -} - - -PyDoc_STRVAR(Remote_get_push_refspecs__doc__, "Push refspecs"); - - -PyObject * -Remote_get_push_refspecs(Remote *self) -{ - return Remote_push_refspecs__get__(self); -} - -PyDoc_STRVAR(Remote_set_fetch_refspecs__doc__, - "set_fetch_refspecs([str])\n" - "This function is deprecated, please use the push_refspecs attribute" - "\n"); - - -PyObject * -Remote_set_fetch_refspecs(Remote *self, PyObject *args) -{ - if (Remote_fetch_refspecs__set__(self, args) < 0) - return NULL; - - Py_RETURN_NONE; -} - - -PyDoc_STRVAR(Remote_set_push_refspecs__doc__, - "set_push_refspecs([str])\n" - "This function is deprecated, please use the push_refspecs attribute" - "\n"); - - -PyObject * -Remote_set_push_refspecs(Remote *self, PyObject *args) -{ - if (Remote_push_refspecs__set__(self, args) < 0) - return NULL; - - Py_RETURN_NONE; -} - PyDoc_STRVAR(Remote_url__doc__, "Url of the remote"); @@ -699,7 +646,7 @@ PyDoc_STRVAR(Remote_url__doc__, "Url of the remote"); PyObject * Remote_url__get__(Remote *self) { - const char *url; + const char *url; url = git_remote_url(self->remote); if (!url) @@ -735,7 +682,7 @@ PyDoc_STRVAR(Remote_push_url__doc__, "Push url of the remote"); PyObject * Remote_push_url__get__(Remote *self) { - const char *url; + const char *url; url = git_remote_pushurl(self->remote); if (!url) @@ -976,10 +923,6 @@ PyMethodDef Remote_methods[] = { METHOD(Remote, push, METH_VARARGS), METHOD(Remote, add_push, METH_VARARGS), METHOD(Remote, add_fetch, METH_VARARGS), - METHOD(Remote, get_fetch_refspecs, METH_NOARGS), - METHOD(Remote, set_fetch_refspecs, METH_O), - METHOD(Remote, get_push_refspecs, METH_NOARGS), - METHOD(Remote, set_push_refspecs, METH_O), {NULL} }; diff --git a/src/tree.c b/src/tree.c index bd2c0870e..dae77dcd2 100644 --- a/src/tree.c +++ b/src/tree.c @@ -201,9 +201,11 @@ Tree_len(Tree *self) int Tree_contains(Tree *self, PyObject *py_name) { - int err; + int err; git_tree_entry *entry; - char *name = py_path_to_c_str(py_name); + char *name; + + name = py_path_to_c_str(py_name); if (name == NULL) return -1; diff --git a/test/test_remote.py b/test/test_remote.py index ce146e0ed..d98bfc5c9 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -92,53 +92,49 @@ def test_refspec(self): self.assertEqual(True, refspec.force) self.assertEqual(ORIGIN_REFSPEC, refspec.string) - self.assertEqual(list, type(remote.get_fetch_refspecs())) - self.assertEqual(1, len(remote.get_fetch_refspecs())) - self.assertEqual(ORIGIN_REFSPEC, remote.get_fetch_refspecs()[0]) + self.assertEqual(list, type(remote.fetch_refspecs)) + self.assertEqual(1, len(remote.fetch_refspecs)) + self.assertEqual(ORIGIN_REFSPEC, remote.fetch_refspecs[0]) self.assertTrue(refspec.src_matches('refs/heads/master')) self.assertTrue(refspec.dst_matches('refs/remotes/origin/master')) self.assertEqual('refs/remotes/origin/master', refspec.transform('refs/heads/master')) self.assertEqual('refs/heads/master', refspec.rtransform('refs/remotes/origin/master')) - self.assertEqual(list, type(remote.get_push_refspecs())) - self.assertEqual(0, len(remote.get_push_refspecs())) + self.assertEqual(list, type(remote.push_refspecs)) + self.assertEqual(0, len(remote.push_refspecs)) push_specs = remote.push_refspecs self.assertEqual(list, type(push_specs)) self.assertEqual(0, len(push_specs)) - remote.set_fetch_refspecs(['+refs/*:refs/remotes/*']) - self.assertEqual('+refs/*:refs/remotes/*', - remote.get_fetch_refspecs()[0]) + remote.fetch_refspecs = ['+refs/*:refs/remotes/*'] + self.assertEqual('+refs/*:refs/remotes/*', remote.fetch_refspecs[0]) fetch_specs = remote.fetch_refspecs self.assertEqual(list, type(fetch_specs)) self.assertEqual(1, len(fetch_specs)) self.assertEqual('+refs/*:refs/remotes/*', fetch_specs[0]) - remote.set_fetch_refspecs([ - '+refs/*:refs/remotes/*', - '+refs/test/*:refs/test/remotes/*' - ]) - self.assertEqual('+refs/*:refs/remotes/*', - remote.get_fetch_refspecs()[0]) + remote.fetch_refspecs = ['+refs/*:refs/remotes/*', + '+refs/test/*:refs/test/remotes/*'] + self.assertEqual('+refs/*:refs/remotes/*', remote.fetch_refspecs[0]) self.assertEqual('+refs/test/*:refs/test/remotes/*', - remote.get_fetch_refspecs()[1]) + remote.fetch_refspecs[1]) - remote.set_push_refspecs([ - '+refs/*:refs/remotes/*', - '+refs/test/*:refs/test/remotes/*' - ]) + remote.push_refspecs = ['+refs/*:refs/remotes/*', + '+refs/test/*:refs/test/remotes/*'] - self.assertRaises(TypeError, setattr, remote, 'push_refspecs', '+refs/*:refs/*') - self.assertRaises(TypeError, setattr, remote, 'fetch_refspecs', '+refs/*:refs/*') - self.assertRaises(TypeError, setattr, remote, 'fetch_refspecs', ['+refs/*:refs/*', 5]) + self.assertRaises(TypeError, setattr, remote, 'push_refspecs', + '+refs/*:refs/*') + self.assertRaises(TypeError, setattr, remote, 'fetch_refspecs', + '+refs/*:refs/*') + self.assertRaises(TypeError, setattr, remote, 'fetch_refspecs', + ['+refs/*:refs/*', 5]) - self.assertEqual('+refs/*:refs/remotes/*', - remote.get_push_refspecs()[0]) + self.assertEqual('+refs/*:refs/remotes/*', remote.push_refspecs[0]) self.assertEqual('+refs/test/*:refs/test/remotes/*', - remote.get_push_refspecs()[1]) + remote.push_refspecs[1]) def test_remote_list(self): @@ -169,10 +165,10 @@ def test_add_refspec(self): remote = self.repo.create_remote('test_add_refspec', REMOTE_URL) remote.add_push('refs/heads/*:refs/heads/test_refspec/*') self.assertEqual('refs/heads/*:refs/heads/test_refspec/*', - remote.get_push_refspecs()[0]) + remote.push_refspecs[0]) remote.add_fetch('+refs/heads/*:refs/remotes/test_refspec/*') self.assertEqual('+refs/heads/*:refs/remotes/test_refspec/*', - remote.get_fetch_refspecs()[1]) + remote.fetch_refspecs[1]) def test_remote_callback_typecheck(self): remote = self.repo.remotes[0] From b95ee3758c4eb44b195758d3e83bc061936c080a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 2 Feb 2014 23:32:21 +0100 Subject: [PATCH 0671/2237] Preparing release --- README.rst | 56 ++++++++++++++++++++++++++++++++++++++++++++++++---- docs/oid.rst | 2 +- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 53d69ab59..1be1203de 100644 --- a/README.rst +++ b/README.rst @@ -82,6 +82,11 @@ Changelog 0.20.2 (2014-02-XX) ------------------- +- Support pypy + `#209 `_ + `#327 `_ + `#333 `_ + - New ``Blob.diff(...)`` and ``Blob.diff_to_buffer(...)`` `#307 `_ @@ -89,31 +94,74 @@ Changelog `#310 `_ - New ``Commit.tree_id`` and ``Commit.parent_ids`` - `#730 `_ + `#73 `_ + `#311 `_ +- New ``Config`` iterator replaces ``Config.foreach`` + `#183 `_ + `#312 `_ - New rich comparison between tree entries -- New ``Config`` iterator replaces ``Config.foreach`` + `#305 `_ + `#313 `_ + - New type ``Refspec`` + `#314 `_ + - New ``Remote.push_url`` + `#315 `_ + +- Now ``path in Tree`` works + `#306 `_ + `#316 `_ + - New ``Remote.add_push`` and ``Remote.add_fetch`` + `#255 `_ + `#318 `_ + - New ``Remote.fetch_refspecs`` replaces ``Remote.get_fetch_refspecs()`` and ``Remote.set_fetch_refspecs(...)`` + `#319 `_ + - New ``Remote.push_refspecs`` replaces ``Remote.get_push_refspecs()`` and ``Remote.set_push_refspecs(...)`` -- Now *path* in ``Tree`` works + `#319 `_ + - New ``str(Oid)`` deprecates ``Oid.hex`` + `#322 `_ + - New ``Object.id`` deprecates ``Object.oid`` + `#322 `_ + - New ``TreeEntry.id`` deprecates ``TreeEntry.oid`` + `#322 `_ + - New ``Remote.progress``, ``Remote.transfer_progress`` and ``Remote.update_tips`` + `#274 `_ + `#324 `_ + - New type ``TransferProgress`` + `#274 `_ + `#324 `_ + - Now possible to create ``IndexEntry(...)`` + `#325 `_ + - Now ``IndexEntry.path``, ``IndexEntry.oid`` and ``IndexEntry.mode`` are writable + `#325 `_ + - Now ``Index.add(...)`` accepts an ``IndexEntry`` too + `#325 `_ + - Now ``Index.write_tree(...)`` is able to write to a different repository -- Support pypy + `#325 `_ + +- Other non user visible changes: + `#331 `_ + and + `#332 `_ 0.20.1 (2013-12-24) diff --git a/docs/oid.rst b/docs/oid.rst index 66adcdb42..ff5cad803 100644 --- a/docs/oid.rst +++ b/docs/oid.rst @@ -65,7 +65,7 @@ And the other way around, from an Oid object we can get the hexadecimal and raw forms. You can use the built-in `str()` (or `unicode()` in python 2) to get the hexadecimal representation of the Oid. -.. automethod:: str(pygit2.Oid) +.. method:: str(pygit2.Oid) .. autoattribute:: pygit2.Oid.raw The Oid type supports: From 873b38bbc3b1f56618c8ba796ccfc62af4325da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 3 Feb 2014 22:39:58 +0100 Subject: [PATCH 0672/2237] Update README --- README.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 1be1203de..3b6767a86 100644 --- a/README.rst +++ b/README.rst @@ -158,10 +158,12 @@ Changelog - Now ``Index.write_tree(...)`` is able to write to a different repository `#325 `_ +- Fix refcount leak in ``Repository.remotes`` + `#321 `_ + `#332 `_ + - Other non user visible changes: `#331 `_ - and - `#332 `_ 0.20.1 (2013-12-24) From c04db1f66dcc2c88b9066ec2d73d0588a61b3090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 4 Feb 2014 07:25:00 +0100 Subject: [PATCH 0673/2237] Split the Refspec type to its own file --- src/refspec.c | 293 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/refspec.h | 38 +++++++ src/remote.c | 262 +------------------------------------------- 3 files changed, 334 insertions(+), 259 deletions(-) create mode 100644 src/refspec.c create mode 100644 src/refspec.h diff --git a/src/refspec.c b/src/refspec.c new file mode 100644 index 000000000..ce679df38 --- /dev/null +++ b/src/refspec.c @@ -0,0 +1,293 @@ +/* + * Copyright 2010-2013 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 "error.h" +#include "types.h" +#include "utils.h" +#include "refspec.h" + + +extern PyTypeObject RefspecType; + +Refspec * +wrap_refspec(const Remote *owner, const git_refspec *refspec) +{ + Refspec *spec; + + spec = PyObject_New(Refspec, &RefspecType); + if (!spec) + return NULL; + + Py_INCREF(owner); + spec->owner = owner; + spec->refspec = refspec; + + return spec; +} + +PyDoc_STRVAR(Refspec_direction__doc__, + "The direction of this refspec (fetch or push)"); + +PyObject * +Refspec_direction__get__(Refspec *self) +{ + return Py_BuildValue("i", git_refspec_direction(self->refspec)); +} + +PyDoc_STRVAR(Refspec_src__doc__, "Source or lhs of the refspec"); + +PyObject * +Refspec_src__get__(Refspec *self) +{ + return to_unicode(git_refspec_src(self->refspec), NULL, NULL); +} + +PyDoc_STRVAR(Refspec_dst__doc__, "Destination or rhs of the refspec"); + +PyObject * +Refspec_dst__get__(Refspec *self) +{ + return to_unicode(git_refspec_dst(self->refspec), NULL, NULL); +} + +PyDoc_STRVAR(Refspec_string__doc__, "String used to create this refspec"); + +PyObject * +Refspec_string__get__(Refspec *self) +{ + return to_unicode(git_refspec_string(self->refspec), NULL, NULL); +} + +PyDoc_STRVAR(Refspec_force__doc__, + "Whether this refspec allows non-fast-forward updates"); + +PyObject * +Refspec_force__get__(Refspec *self) +{ + if (git_refspec_force(self->refspec)) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +PyDoc_STRVAR(Refspec_src_matches__doc__, + "src_matches(str) -> Bool\n" + "\n" + "Returns whether the string matches the source refspec\n"); + +PyObject * +Refspec_src_matches(Refspec *self, PyObject *py_str) +{ + char *str; + int res; + + str = py_str_to_c_str(py_str, NULL); + if (!str) + return NULL; + + res = git_refspec_src_matches(self->refspec, str); + free(str); + + if (res) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +PyDoc_STRVAR(Refspec_dst_matches__doc__, + "dst_matches(str) -> Bool\n" + "\n" + "Returns whether the string matches the destination refspec\n"); + +PyObject * +Refspec_dst_matches(Refspec *self, PyObject *py_str) +{ + char *str; + int res; + + str = py_str_to_c_str(py_str, NULL); + if (!str) + return NULL; + + res = git_refspec_dst_matches(self->refspec, str); + free(str); + + if (res) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +PyDoc_STRVAR(Refspec_transform__doc__, + "transform(str) -> str\n" + "\n" + "Transform a reference according to the refspec\n"); + +PyObject * +Refspec_transform(Refspec *self, PyObject *py_str) +{ + char *str, *trans; + int err, len, alen; + PyObject *py_trans; + + str = py_str_to_c_str(py_str, NULL); + alen = len = strlen(str); + + do { + alen *= alen; + trans = malloc(alen); + if (!trans) { + free(str); + return PyErr_NoMemory(); + } + + err = git_refspec_transform(trans, alen, self->refspec, str); + } while(err == GIT_EBUFS); + free(str); + + if (err < 0) { + free(trans); + Error_set(err); + return NULL; + } + + py_trans = to_unicode(trans, NULL, NULL); + + free(trans); + + return py_trans; +} + +PyDoc_STRVAR(Refspec_rtransform__doc__, + "rtransform(str) -> str\n" + "\n" + "Transform a reference according to the refspec in reverse\n"); + +PyObject * +Refspec_rtransform(Refspec *self, PyObject *py_str) +{ + char *str, *trans; + int err, len, alen; + PyObject *py_trans; + + str = py_str_to_c_str(py_str, NULL); + alen = len = strlen(str); + + do { + alen *= alen; + trans = malloc(alen); + if (!trans) { + free(str); + return PyErr_NoMemory(); + } + + err = git_refspec_rtransform(trans, alen, self->refspec, str); + } while(err == GIT_EBUFS); + free(str); + + if (err < 0) { + free(trans); + Error_set(err); + return NULL; + } + + py_trans = to_unicode(trans, NULL, NULL); + + free(trans); + + return py_trans; +} + +PyMethodDef Refspec_methods[] = { + METHOD(Refspec, src_matches, METH_O), + METHOD(Refspec, dst_matches, METH_O), + METHOD(Refspec, transform, METH_O), + METHOD(Refspec, rtransform, METH_O), + {NULL} +}; + +PyGetSetDef Refspec_getseters[] = { + GETTER(Refspec, direction), + GETTER(Refspec, src), + GETTER(Refspec, dst), + GETTER(Refspec, string), + GETTER(Refspec, force), + {NULL} +}; + +static void +Refspec_dealloc(Refspec *self) +{ + Py_CLEAR(self->owner); + PyObject_Del(self); +} + +PyDoc_STRVAR(Refspec__doc__, "Refspec object."); + +PyTypeObject RefspecType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.Refspec", /* tp_name */ + sizeof(Refspec), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Refspec_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Refspec__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Refspec_methods, /* tp_methods */ + 0, /* tp_members */ + Refspec_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/refspec.h b/src/refspec.h new file mode 100644 index 000000000..e653bf59b --- /dev/null +++ b/src/refspec.h @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2013 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. + */ + +#ifndef INCLUDE_pygit2_refspec_h +#define INCLUDE_pygit2_refspec_h + +#define PY_SSIZE_T_CLEAN +#include +#include +#include + +Refspec* wrap_refspec(const Remote *owner, const git_refspec *refspec); + +#endif diff --git a/src/remote.c b/src/remote.c index 63d11c8ca..00072b7bb 100644 --- a/src/remote.c +++ b/src/remote.c @@ -29,273 +29,17 @@ #include #include #include "error.h" -#include "utils.h" #include "types.h" -#include "remote.h" +#include "utils.h" #include "oid.h" +#include "refspec.h" +#include "remote.h" extern PyObject *GitError; extern PyTypeObject RepositoryType; -extern PyTypeObject RefspecType; extern PyTypeObject TransferProgressType; -Refspec * -wrap_refspec(const Remote *owner, const git_refspec *refspec) -{ - Refspec *spec; - - spec = PyObject_New(Refspec, &RefspecType); - if (!spec) - return NULL; - - Py_INCREF(owner); - spec->owner = owner; - spec->refspec = refspec; - - return spec; -} - -PyDoc_STRVAR(Refspec_direction__doc__, - "The direction of this refspec (fetch or push)"); - -PyObject * -Refspec_direction__get__(Refspec *self) -{ - return Py_BuildValue("i", git_refspec_direction(self->refspec)); -} - -PyDoc_STRVAR(Refspec_src__doc__, "Source or lhs of the refspec"); - -PyObject * -Refspec_src__get__(Refspec *self) -{ - return to_unicode(git_refspec_src(self->refspec), NULL, NULL); -} - -PyDoc_STRVAR(Refspec_dst__doc__, "Destination or rhs of the refspec"); - -PyObject * -Refspec_dst__get__(Refspec *self) -{ - return to_unicode(git_refspec_dst(self->refspec), NULL, NULL); -} - -PyDoc_STRVAR(Refspec_string__doc__, "String used to create this refspec"); - -PyObject * -Refspec_string__get__(Refspec *self) -{ - return to_unicode(git_refspec_string(self->refspec), NULL, NULL); -} - -PyDoc_STRVAR(Refspec_force__doc__, - "Whether this refspec allows non-fast-forward updates"); - -PyObject * -Refspec_force__get__(Refspec *self) -{ - if (git_refspec_force(self->refspec)) - Py_RETURN_TRUE; - - Py_RETURN_FALSE; -} - -PyDoc_STRVAR(Refspec_src_matches__doc__, - "src_matches(str) -> Bool\n" - "\n" - "Returns whether the string matches the source refspec\n"); - -PyObject * -Refspec_src_matches(Refspec *self, PyObject *py_str) -{ - char *str; - int res; - - str = py_str_to_c_str(py_str, NULL); - if (!str) - return NULL; - - res = git_refspec_src_matches(self->refspec, str); - free(str); - - if (res) - Py_RETURN_TRUE; - - Py_RETURN_FALSE; -} - -PyDoc_STRVAR(Refspec_dst_matches__doc__, - "dst_matches(str) -> Bool\n" - "\n" - "Returns whether the string matches the destination refspec\n"); - -PyObject * -Refspec_dst_matches(Refspec *self, PyObject *py_str) -{ - char *str; - int res; - - str = py_str_to_c_str(py_str, NULL); - if (!str) - return NULL; - - res = git_refspec_dst_matches(self->refspec, str); - free(str); - - if (res) - Py_RETURN_TRUE; - - Py_RETURN_FALSE; -} - -PyDoc_STRVAR(Refspec_transform__doc__, - "transform(str) -> str\n" - "\n" - "Transform a reference according to the refspec\n"); - -PyObject * -Refspec_transform(Refspec *self, PyObject *py_str) -{ - char *str, *trans; - int err, len, alen; - PyObject *py_trans; - - str = py_str_to_c_str(py_str, NULL); - alen = len = strlen(str); - - do { - alen *= alen; - trans = malloc(alen); - if (!trans) { - free(str); - return PyErr_NoMemory(); - } - - err = git_refspec_transform(trans, alen, self->refspec, str); - } while(err == GIT_EBUFS); - free(str); - - if (err < 0) { - free(trans); - Error_set(err); - return NULL; - } - - py_trans = to_unicode(trans, NULL, NULL); - - free(trans); - - return py_trans; -} - -PyDoc_STRVAR(Refspec_rtransform__doc__, - "rtransform(str) -> str\n" - "\n" - "Transform a reference according to the refspec in reverse\n"); - -PyObject * -Refspec_rtransform(Refspec *self, PyObject *py_str) -{ - char *str, *trans; - int err, len, alen; - PyObject *py_trans; - - str = py_str_to_c_str(py_str, NULL); - alen = len = strlen(str); - - do { - alen *= alen; - trans = malloc(alen); - if (!trans) { - free(str); - return PyErr_NoMemory(); - } - - err = git_refspec_rtransform(trans, alen, self->refspec, str); - } while(err == GIT_EBUFS); - free(str); - - if (err < 0) { - free(trans); - Error_set(err); - return NULL; - } - - py_trans = to_unicode(trans, NULL, NULL); - - free(trans); - - return py_trans; -} - -PyMethodDef Refspec_methods[] = { - METHOD(Refspec, src_matches, METH_O), - METHOD(Refspec, dst_matches, METH_O), - METHOD(Refspec, transform, METH_O), - METHOD(Refspec, rtransform, METH_O), - {NULL} -}; - -PyGetSetDef Refspec_getseters[] = { - GETTER(Refspec, direction), - GETTER(Refspec, src), - GETTER(Refspec, dst), - GETTER(Refspec, string), - GETTER(Refspec, force), - {NULL} -}; - -static void -Refspec_dealloc(Refspec *self) -{ - Py_CLEAR(self->owner); - PyObject_Del(self); -} - -PyDoc_STRVAR(Refspec__doc__, "Refspec object."); - -PyTypeObject RefspecType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Refspec", /* tp_name */ - sizeof(Refspec), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Refspec_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - Refspec__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Refspec_methods, /* tp_methods */ - 0, /* tp_members */ - Refspec_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 */ -}; - PyObject * wrap_transfer_progress(const git_transfer_progress *stats) { From 7b1310f31b749109fd11d0055a9528539f0e1a10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 4 Feb 2014 07:52:58 +0100 Subject: [PATCH 0674/2237] docs: fix warnings --- docs/blame.rst | 6 +++--- docs/config.rst | 2 +- docs/diff.rst | 2 +- docs/objects.rst | 16 ++++++++-------- docs/oid.rst | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/blame.rst b/docs/blame.rst index b0c7381bb..fc052e0e1 100644 --- a/docs/blame.rst +++ b/docs/blame.rst @@ -12,9 +12,9 @@ The Blame type ============== .. automethod:: pygit2.Blame.for_line -.. method:: iter(Blame) -.. method:: len(Blame) -.. method:: Blame[n] +.. method:: Blame.__iter__() +.. method:: Blame.__len__() +.. method:: Blame.__getitem__(n) The BlameHunk type diff --git a/docs/config.rst b/docs/config.rst index e9b5c647b..348c34743 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -14,7 +14,7 @@ The Config type .. automethod:: pygit2.Config.get_multivar .. automethod:: pygit2.Config.set_multivar -.. method:: for (name, value) in Config +.. method:: Config.__iter__() The :class:`Config` class has an iterator which can be used to loop through all the entries in the configuration. Each element is a tuple diff --git a/docs/diff.rst b/docs/diff.rst index 0e5fbaffa..09fc9da21 100644 --- a/docs/diff.rst +++ b/docs/diff.rst @@ -35,7 +35,7 @@ The Diff type ==================== .. autoattribute:: pygit2.Diff.patch -.. method:: len(Diff) +.. method:: Diff.__len__() Returns the number of deltas/patches in this diff. diff --git a/docs/objects.rst b/docs/objects.rst index 432481b4f..ecb39e998 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -18,7 +18,7 @@ In the previous chapter we learnt about Object IDs. With an oid we can ask the repository to get the associated object. To do that the ``Repository`` class implementes a subset of the mapping interface. -.. method:: Repository.get(oid, default=None) +.. automethod:: pygit2.Repository.get Return the Git object for the given *oid*, returns the *default* value if there's no object in the repository with that oid. The oid can be an Oid @@ -32,13 +32,13 @@ implementes a subset of the mapping interface. >>> obj <_pygit2.Commit object at 0x7ff27a6b60f0> -.. method:: Repository[oid] +.. method:: Repository.__getitem__(oid) Return the Git object for the given oid, raise ``KeyError`` if there's no object in the repository with that oid. The oid can be an Oid object, or an hexadecimal string. -.. method:: oid in Repository +.. method:: Repository.__contains__(oid) Returns True if there is an object in the Repository with that oid, False if there is not. The oid can be an Oid object, or an hexadecimal string. @@ -147,20 +147,20 @@ directory in a file system. Each entry points to another tree or a blob. A tree can be iterated, and partially implements the sequence and mapping interfaces. -.. method:: Tree[name] +.. method:: Tree.__getitem__(name) Return the TreeEntry object for the given *name*. Raise ``KeyError`` if there is not a tree entry with that name. -.. method:: name in Tree +.. method:: Tree.__contains__(name) Return True if there is a tree entry with the given name, False otherwise. -.. method:: len(Tree) +.. method:: Tree.__len__() Return the number of entries in the tree. -.. method:: iter(Tree) +.. method:: Tree.__iter__() Return an iterator over the entries of the tree. @@ -176,7 +176,7 @@ Tree entries .. autoattribute:: pygit2.TreeEntry.hex .. autoattribute:: pygit2.TreeEntry.filemode -.. method:: cmp(TreeEntry, TreeEntry) +.. method:: TreeEntry.__cmp__(TreeEntry) Rich comparison between tree entries. diff --git a/docs/oid.rst b/docs/oid.rst index ff5cad803..2d66bde72 100644 --- a/docs/oid.rst +++ b/docs/oid.rst @@ -65,7 +65,7 @@ And the other way around, from an Oid object we can get the hexadecimal and raw forms. You can use the built-in `str()` (or `unicode()` in python 2) to get the hexadecimal representation of the Oid. -.. method:: str(pygit2.Oid) +.. method:: Oid.__str__() .. autoattribute:: pygit2.Oid.raw The Oid type supports: From d7071b88cd87d3e34a414d92e3b8949929b37472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 4 Feb 2014 08:02:12 +0100 Subject: [PATCH 0675/2237] Update copyright year --- docs/conf.py | 2 +- pygit2/__init__.py | 2 +- pygit2/repository.py | 2 +- pygit2/version.py | 2 +- setup.py | 2 +- src/blame.c | 2 +- src/blame.h | 2 +- src/blob.c | 2 +- src/blob.h | 2 +- src/branch.c | 2 +- src/branch.h | 2 +- src/commit.c | 2 +- src/commit.h | 2 +- src/config.c | 2 +- src/config.h | 2 +- src/diff.c | 2 +- src/diff.h | 2 +- src/error.c | 2 +- src/error.h | 2 +- src/index.c | 2 +- src/index.h | 2 +- src/mergeresult.c | 2 +- src/mergeresult.h | 2 +- src/note.c | 2 +- src/note.h | 2 +- src/object.c | 2 +- src/object.h | 2 +- src/oid.c | 2 +- src/oid.h | 2 +- src/pygit2.c | 2 +- src/reference.c | 2 +- src/reference.h | 2 +- src/refspec.c | 2 +- src/refspec.h | 2 +- src/remote.c | 2 +- src/remote.h | 2 +- src/repository.c | 2 +- src/repository.h | 2 +- src/signature.c | 2 +- src/signature.h | 2 +- src/tag.c | 2 +- src/tag.h | 2 +- src/tree.c | 2 +- src/tree.h | 2 +- src/treebuilder.c | 2 +- src/treebuilder.h | 2 +- src/types.h | 2 +- src/utils.c | 2 +- src/utils.h | 2 +- src/walker.c | 2 +- src/walker.h | 2 +- test/__init__.py | 2 +- test/test_blame.py | 2 +- test/test_blob.py | 2 +- test/test_branch.py | 2 +- test/test_commit.py | 2 +- test/test_config.py | 2 +- test/test_diff.py | 2 +- test/test_index.py | 2 +- test/test_note.py | 2 +- test/test_oid.py | 2 +- test/test_reflog.py | 2 +- test/test_refs.py | 2 +- test/test_remote.py | 2 +- test/test_repository.py | 2 +- test/test_revwalk.py | 2 +- test/test_signature.py | 2 +- test/test_status.py | 2 +- test/test_tag.py | 2 +- test/test_tree.py | 2 +- test/test_treebuilder.py | 2 +- test/utils.py | 2 +- 72 files changed, 72 insertions(+), 72 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 7ec185961..addf93dd3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -41,7 +41,7 @@ # General information about the project. project = u'pygit2' -copyright = u'2013, J. David Ibáñez' +copyright = u'2010-2014 The pygit2 contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/pygit2/__init__.py b/pygit2/__init__.py index e2d52467c..244fdad1e 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/pygit2/repository.py b/pygit2/repository.py index fbb77ced3..4b7876579 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/pygit2/version.py b/pygit2/version.py index 9469822bf..71fbd6f5f 100644 --- a/pygit2/version.py +++ b/pygit2/version.py @@ -1,4 +1,4 @@ -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/setup.py b/setup.py index 0717839a8..eff54e184 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # coding: UTF-8 # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/src/blame.c b/src/blame.c index d5545bf83..f0ee18f2d 100644 --- a/src/blame.c +++ b/src/blame.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/blame.h b/src/blame.h index a6f1e53e4..20bff683e 100644 --- a/src/blame.h +++ b/src/blame.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/blob.c b/src/blob.c index 1b2840f71..94352dfda 100644 --- a/src/blob.c +++ b/src/blob.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/blob.h b/src/blob.h index 5de1dee00..7af6537cb 100644 --- a/src/blob.h +++ b/src/blob.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/branch.c b/src/branch.c index 483906b82..8f78de8bb 100644 --- a/src/branch.c +++ b/src/branch.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/branch.h b/src/branch.h index ee1185e09..cad0c2dc0 100644 --- a/src/branch.h +++ b/src/branch.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/commit.c b/src/commit.c index 258f082ba..8a61d29e7 100644 --- a/src/commit.c +++ b/src/commit.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/commit.h b/src/commit.h index b94e30370..eeb0a92aa 100644 --- a/src/commit.h +++ b/src/commit.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/config.c b/src/config.c index fdb2593c2..441abb127 100644 --- a/src/config.c +++ b/src/config.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/config.h b/src/config.h index b0eba9353..ffa0733bb 100644 --- a/src/config.h +++ b/src/config.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/diff.c b/src/diff.c index 1609a7d65..8e59ebaa0 100644 --- a/src/diff.c +++ b/src/diff.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/diff.h b/src/diff.h index 9ba3a9ea0..4ae78ddff 100644 --- a/src/diff.h +++ b/src/diff.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/error.c b/src/error.c index 66587143a..848178c7e 100644 --- a/src/error.c +++ b/src/error.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/error.h b/src/error.h index f487763bf..cc52790b3 100644 --- a/src/error.h +++ b/src/error.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/index.c b/src/index.c index fcd36128c..5d70a1dcf 100644 --- a/src/index.c +++ b/src/index.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/index.h b/src/index.h index e59262ca6..cd570aa0c 100644 --- a/src/index.h +++ b/src/index.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/mergeresult.c b/src/mergeresult.c index 31699c1ca..ada872fb4 100644 --- a/src/mergeresult.c +++ b/src/mergeresult.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/mergeresult.h b/src/mergeresult.h index 770a4bd40..74161ea9d 100644 --- a/src/mergeresult.h +++ b/src/mergeresult.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/note.c b/src/note.c index c515d8bd4..2e5c9b149 100644 --- a/src/note.c +++ b/src/note.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/note.h b/src/note.h index a4ded369a..18858f053 100644 --- a/src/note.h +++ b/src/note.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/object.c b/src/object.c index d608e0c53..e17ef3f12 100644 --- a/src/object.c +++ b/src/object.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/object.h b/src/object.h index 7ab68a4a9..5cf01afa0 100644 --- a/src/object.h +++ b/src/object.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/oid.c b/src/oid.c index 81749e51d..b9769f78d 100644 --- a/src/oid.c +++ b/src/oid.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/oid.h b/src/oid.h index 871f26a47..4d4825b07 100644 --- a/src/oid.h +++ b/src/oid.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/pygit2.c b/src/pygit2.c index 0ec81676c..77535c735 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/reference.c b/src/reference.c index df5e9a6b2..2efbca3c5 100644 --- a/src/reference.c +++ b/src/reference.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/reference.h b/src/reference.h index 5cbd13453..324866aff 100644 --- a/src/reference.h +++ b/src/reference.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/refspec.c b/src/refspec.c index ce679df38..a59350afd 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/refspec.h b/src/refspec.h index e653bf59b..829823f94 100644 --- a/src/refspec.h +++ b/src/refspec.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/remote.c b/src/remote.c index 00072b7bb..f6ff1e3e5 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/remote.h b/src/remote.h index 1c8605f8e..ce6ee47d5 100644 --- a/src/remote.h +++ b/src/remote.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/repository.c b/src/repository.c index 00d9cc55e..95e2e3459 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/repository.h b/src/repository.h index 735f77480..53a760e83 100644 --- a/src/repository.h +++ b/src/repository.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/signature.c b/src/signature.c index 61fd0746c..687343b89 100644 --- a/src/signature.c +++ b/src/signature.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/signature.h b/src/signature.h index a0028a9d3..425b3dda3 100644 --- a/src/signature.h +++ b/src/signature.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/tag.c b/src/tag.c index c5fdd0511..6224d4c80 100644 --- a/src/tag.c +++ b/src/tag.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/tag.h b/src/tag.h index 04d3a1092..ada402c59 100644 --- a/src/tag.h +++ b/src/tag.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/tree.c b/src/tree.c index dae77dcd2..8c83d3aa7 100644 --- a/src/tree.c +++ b/src/tree.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/tree.h b/src/tree.h index 94d0ff184..6392cf9f0 100644 --- a/src/tree.h +++ b/src/tree.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/treebuilder.c b/src/treebuilder.c index 8981e98c3..8354b1b5e 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/treebuilder.h b/src/treebuilder.h index ad000dfff..16a5af03a 100644 --- a/src/treebuilder.h +++ b/src/treebuilder.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/types.h b/src/types.h index 7a573dd96..e6a318994 100644 --- a/src/types.h +++ b/src/types.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/utils.c b/src/utils.c index 31f0472aa..6a20b01bf 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/utils.h b/src/utils.h index 66d923453..05ddcd21b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/walker.c b/src/walker.c index 79cf389bf..7c76560bf 100644 --- a/src/walker.c +++ b/src/walker.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/src/walker.h b/src/walker.h index 9742ea97a..1f4b801d6 100644 --- a/src/walker.h +++ b/src/walker.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 The pygit2 contributors + * Copyright 2010-2014 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, diff --git a/test/__init__.py b/test/__init__.py index 1cbb37d02..f065d8b39 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_blame.py b/test/test_blame.py index 1f907740b..dbe9e6bf0 100644 --- a/test/test_blame.py +++ b/test/test_blame.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_blob.py b/test/test_blob.py index cc6bc0aba..da3db3e8f 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_branch.py b/test/test_branch.py index 8dc62cb1a..87e3a57df 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_commit.py b/test/test_commit.py index e7d8c5c6f..f7a42b680 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_config.py b/test/test_config.py index 4a22f407e..9f4d460a7 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_diff.py b/test/test_diff.py index 60078ac24..f982d0194 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_index.py b/test/test_index.py index 1afdf56ce..7076e2e12 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_note.py b/test/test_note.py index a0ef44385..74f08940d 100644 --- a/test/test_note.py +++ b/test/test_note.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_oid.py b/test/test_oid.py index d2d7a9205..6c9a71e25 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_reflog.py b/test/test_reflog.py index 84473c8ee..1251c2bde 100644 --- a/test/test_reflog.py +++ b/test/test_reflog.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_refs.py b/test/test_refs.py index bac89db6f..0757b7a90 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_remote.py b/test/test_remote.py index d98bfc5c9..797474c62 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_repository.py b/test/test_repository.py index acd54e4b4..bba6d76be 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_revwalk.py b/test/test_revwalk.py index 654ef47a8..be4a60882 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_signature.py b/test/test_signature.py index 4131c6c83..13a2a2598 100644 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_status.py b/test/test_status.py index 98a7a7099..70ee5bf92 100644 --- a/test/test_status.py +++ b/test/test_status.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_tag.py b/test/test_tag.py index e31f7b0f2..c8448686d 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_tree.py b/test/test_tree.py index 25ba7ed66..128fc75c8 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index 5c7fa67ac..6a513e4c5 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, diff --git a/test/utils.py b/test/utils.py index 4d941e5c3..07952a189 100644 --- a/test/utils.py +++ b/test/utils.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2013 The pygit2 contributors +# Copyright 2010-2014 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, From 74d091d6097f7792bac72dbf8e3da22560714b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 3 Feb 2014 17:45:49 +0100 Subject: [PATCH 0676/2237] utils: allow borrowing a C string We don't always need our own copy of a C string; sometimes we just need to parse it. Create py_str_borrow_c_str() which returns a char pointer to python's internal value string, with which we can avoid an extra copy. --- src/utils.c | 43 +++++++++++++++++++++++++++++++++---------- src/utils.h | 2 ++ 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/utils.c b/src/utils.c index 6a20b01bf..e63ea9dbf 100644 --- a/src/utils.c +++ b/src/utils.c @@ -32,27 +32,50 @@ extern PyTypeObject ReferenceType; -/* py_str_to_c_str() returns a newly allocated C string holding - * the string contained in the value argument. */ +/** + * py_str_to_c_str() returns a newly allocated C string holding the string + * contained in the 'value' argument. + */ char * py_str_to_c_str(PyObject *value, const char *encoding) { + const char *borrowed; char *c_str = NULL; + PyObject *tmp = NULL; + + borrowed = py_str_borrow_c_str(&tmp, value, encoding); + if (!borrowed) + return NULL; + + c_str = strdup(borrowed); + + Py_DECREF(tmp); + return c_str; +} + +/** + * Return a pointer to the underlying C string in 'value'. The pointer is + * guaranteed by 'tvalue'. Decrease its refcount when done with the string. + */ +const char * +py_str_borrow_c_str(PyObject **tvalue, PyObject *value, const char *encoding) +{ /* Case 1: byte string */ - if (PyBytes_Check(value)) - return strdup(PyBytes_AsString(value)); + if (PyBytes_Check(value)) { + Py_INCREF(value); + *tvalue = value; + return PyBytes_AsString(value); + } /* Case 2: text string */ if (PyUnicode_Check(value)) { if (encoding == NULL) - value = PyUnicode_AsUTF8String(value); + *tvalue = PyUnicode_AsUTF8String(value); else - value = PyUnicode_AsEncodedString(value, encoding, "strict"); - if (value == NULL) + *tvalue = PyUnicode_AsEncodedString(value, encoding, "strict"); + if (*tvalue == NULL) return NULL; - c_str = strdup(PyBytes_AsString(value)); - Py_DECREF(value); - return c_str; + return PyBytes_AsString(*tvalue); } /* Type error */ diff --git a/src/utils.h b/src/utils.h index 05ddcd21b..3b3c42a07 100644 --- a/src/utils.h +++ b/src/utils.h @@ -108,6 +108,8 @@ to_bytes(const char * value) } char * py_str_to_c_str(PyObject *value, const char *encoding); +const char *py_str_borrow_c_str(PyObject **tvaue, PyObject *value, const char *encoding); + #define py_path_to_c_str(py_path) \ py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding) From 1c74676ed405a5ccd199080618417158589f39c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 3 Feb 2014 17:56:59 +0100 Subject: [PATCH 0677/2237] config: borrow the string for lookup and setting We don't need our own copy of the string, so use the new borrowing mechanism to use python's underlying string for the key to get/set. --- src/config.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/config.c b/src/config.c index 441abb127..8cbb05451 100644 --- a/src/config.c +++ b/src/config.c @@ -147,15 +147,15 @@ Config_get_system_config(void) int Config_contains(Config *self, PyObject *py_key) { int err; - const char *c_value; - char *c_key; + const char *c_value, *c_key; + PyObject *tkey; - c_key = py_str_to_c_str(py_key, NULL); + c_key = py_str_borrow_c_str(&tkey, py_key, NULL); if (c_key == NULL) return -1; err = git_config_get_string(&c_value, self->config, c_key); - free(c_key); + Py_DECREF(tkey); if (err < 0) { if (err == GIT_ENOTFOUND) @@ -175,14 +175,15 @@ Config_getitem(Config *self, PyObject *py_key) int64_t value_int; int err, value_bool; const char *value_str; - char *key; - PyObject* py_value; + const char *key; + PyObject* py_value, *tmp; - key = py_str_to_c_str(py_key, NULL); + key = py_str_borrow_c_str(&tmp, py_key, NULL); if (key == NULL) return NULL; err = git_config_get_string(&value_str, self->config, key); + Py_CLEAR(tmp); if (err < 0) goto cleanup; @@ -194,8 +195,6 @@ Config_getitem(Config *self, PyObject *py_key) py_value = to_unicode(value_str, NULL, NULL); cleanup: - free(key); - if (err < 0) { if (err == GIT_ENOTFOUND) { PyErr_SetObject(PyExc_KeyError, py_key); @@ -212,9 +211,10 @@ int Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) { int err; - char *key, *value; + const char *key, *value; + PyObject *tkey, *tvalue; - key = py_str_to_c_str(py_key, NULL); + key = py_str_borrow_c_str(&tkey, py_key, NULL); if (key == NULL) return -1; @@ -227,12 +227,12 @@ Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) err = git_config_set_int64(self->config, key, (int64_t)PyLong_AsLong(py_value)); } else { - value = py_str_to_c_str(py_value, NULL); + value = py_str_borrow_c_str(&tvalue, py_value, NULL); err = git_config_set_string(self->config, key, value); - free(value); + Py_DECREF(tvalue); } - free(key); + Py_DECREF(tkey); if (err < 0) { Error_set(err); return -1; From b4827ba0815092b5008a4b4b211bfc5e5da63a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 4 Feb 2014 12:32:08 +0100 Subject: [PATCH 0678/2237] repository: borrow C strings where possible --- src/repository.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/repository.c b/src/repository.c index 95e2e3459..750517859 100644 --- a/src/repository.c +++ b/src/repository.c @@ -181,14 +181,15 @@ int Repository_head__set__(Repository *self, PyObject *py_refname) { int err; - char *refname; + const char *refname; + PyObject *trefname; - refname = py_str_to_c_str(py_refname, NULL); + refname = py_str_borrow_c_str(&trefname, py_refname, NULL); if (refname == NULL) return -1; err = git_repository_set_head(self->repo, refname); - free(refname); + Py_DECREF(trefname); if (err < 0) { Error_set_str(err, refname); return -1; @@ -318,11 +319,12 @@ PyObject * Repository_revparse_single(Repository *self, PyObject *py_spec) { git_object *c_obj; - char *c_spec; + const char *c_spec; + PyObject *tspec; int err; /* 1- Get the C revision spec */ - c_spec = py_str_to_c_str(py_spec, NULL); + c_spec = py_str_borrow_c_str(&tspec, py_spec, NULL); if (c_spec == NULL) return NULL; @@ -331,10 +333,10 @@ Repository_revparse_single(Repository *self, PyObject *py_spec) if (err < 0) { PyObject *err_obj = Error_set_str(err, c_spec); - free(c_spec); + Py_DECREF(tspec); return err_obj; } - free(c_spec); + Py_DECREF(tspec); return wrap_object(c_obj, self); } @@ -782,7 +784,8 @@ Repository_create_commit(Repository *self, PyObject *args) Signature *py_author, *py_committer; PyObject *py_oid, *py_message, *py_parents, *py_parent; PyObject *py_result = NULL; - char *message = NULL; + PyObject *tmessage; + const char *message = NULL; char *update_ref = NULL; char *encoding = NULL; git_oid oid; @@ -806,7 +809,7 @@ Repository_create_commit(Repository *self, PyObject *args) if (len == 0) goto out; - message = py_str_to_c_str(py_message, encoding); + message = py_str_borrow_c_str(&tmessage, py_message, encoding); if (message == NULL) goto out; @@ -846,7 +849,7 @@ Repository_create_commit(Repository *self, PyObject *args) py_result = git_oid_to_python(&oid); out: - free(message); + Py_DECREF(tmessage); git_tree_free(tree); while (i > 0) { i--; @@ -1048,7 +1051,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) err = git_reference_lookup(&c_reference, self->repo, c_name); if (err < 0) { PyObject *err_obj = Error_set_str(err, c_name); - free(c_name); + free(c_name); return err_obj; } free(c_name); From c8a4027affef36dc9caf4398961991e3c5f4bf75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 4 Feb 2014 12:35:07 +0100 Subject: [PATCH 0679/2237] signature: borrow the name's C string --- src/signature.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/signature.c b/src/signature.c index 687343b89..809e80285 100644 --- a/src/signature.c +++ b/src/signature.c @@ -37,8 +37,9 @@ int Signature_init(Signature *self, PyObject *args, PyObject *kwds) { char *keywords[] = {"name", "email", "time", "offset", "encoding", NULL}; - PyObject *py_name; - char *name, *email, *encoding = "ascii"; + PyObject *py_name, *tname; + char *email, *encoding = "ascii"; + const char *name; long long time = -1; int offset = 0; int err; @@ -49,7 +50,7 @@ Signature_init(Signature *self, PyObject *args, PyObject *kwds) &py_name, &email, &time, &offset, &encoding)) return -1; - name = py_str_to_c_str(py_name, encoding); + name = py_str_borrow_c_str(&tname, py_name, encoding); if (name == NULL) return -1; @@ -58,7 +59,8 @@ Signature_init(Signature *self, PyObject *args, PyObject *kwds) } else { err = git_signature_new(&signature, name, email, time, offset); } - free(name); + Py_DECREF(tname); + if (err < 0) { Error_set(err); return -1; From 659749510f17cc5cce03b3c585c687a131c880d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 4 Feb 2014 12:41:46 +0100 Subject: [PATCH 0680/2237] refspec: borrow the C string --- src/refspec.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/refspec.c b/src/refspec.c index a59350afd..3053fe60b 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -105,15 +105,16 @@ PyDoc_STRVAR(Refspec_src_matches__doc__, PyObject * Refspec_src_matches(Refspec *self, PyObject *py_str) { - char *str; + const char *str; + PyObject *tstr; int res; - str = py_str_to_c_str(py_str, NULL); + str = py_str_borrow_c_str(&tstr, py_str, NULL); if (!str) return NULL; res = git_refspec_src_matches(self->refspec, str); - free(str); + Py_DECREF(tstr); if (res) Py_RETURN_TRUE; @@ -129,15 +130,16 @@ PyDoc_STRVAR(Refspec_dst_matches__doc__, PyObject * Refspec_dst_matches(Refspec *self, PyObject *py_str) { - char *str; + const char *str; + PyObject *tstr; int res; - str = py_str_to_c_str(py_str, NULL); + str = py_str_borrow_c_str(&tstr, py_str, NULL); if (!str) return NULL; res = git_refspec_dst_matches(self->refspec, str); - free(str); + Py_DECREF(tstr); if (res) Py_RETURN_TRUE; @@ -153,24 +155,25 @@ PyDoc_STRVAR(Refspec_transform__doc__, PyObject * Refspec_transform(Refspec *self, PyObject *py_str) { - char *str, *trans; + const char *str; + char *trans; int err, len, alen; - PyObject *py_trans; + PyObject *py_trans, *tstr; - str = py_str_to_c_str(py_str, NULL); + str = py_str_borrow_c_str(&tstr, py_str, NULL); alen = len = strlen(str); do { alen *= alen; trans = malloc(alen); if (!trans) { - free(str); + Py_DECREF(tstr); return PyErr_NoMemory(); } err = git_refspec_transform(trans, alen, self->refspec, str); } while(err == GIT_EBUFS); - free(str); + Py_DECREF(tstr); if (err < 0) { free(trans); @@ -193,24 +196,25 @@ PyDoc_STRVAR(Refspec_rtransform__doc__, PyObject * Refspec_rtransform(Refspec *self, PyObject *py_str) { - char *str, *trans; + const char *str; + char *trans; int err, len, alen; - PyObject *py_trans; + PyObject *py_trans, *tstr; - str = py_str_to_c_str(py_str, NULL); + str = py_str_borrow_c_str(&tstr, py_str, NULL); alen = len = strlen(str); do { alen *= alen; trans = malloc(alen); if (!trans) { - free(str); + Py_DECREF(tstr); return PyErr_NoMemory(); } err = git_refspec_rtransform(trans, alen, self->refspec, str); } while(err == GIT_EBUFS); - free(str); + Py_DECREF(tstr); if (err < 0) { free(trans); From 824ac672c19c4de83a0fe41ea82714a0ef03ab6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 4 Feb 2014 12:43:54 +0100 Subject: [PATCH 0681/2237] remote: borrow the C string where possible --- src/remote.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/remote.c b/src/remote.c index f6ff1e3e5..89c5dda55 100644 --- a/src/remote.c +++ b/src/remote.c @@ -226,12 +226,13 @@ int Remote_name__set__(Remote *self, PyObject* py_name) { int err; - char* name; + const char* name; + PyObject *tname; - name = py_str_to_c_str(py_name, NULL); + name = py_str_borrow_c_str(&tname, py_name, NULL); if (name != NULL) { err = git_remote_rename(self->remote, name, NULL, NULL); - free(name); + Py_DECREF(tname); if (err == GIT_OK) return 0; @@ -404,12 +405,13 @@ int Remote_url__set__(Remote *self, PyObject* py_url) { int err; - char* url = NULL; + const char* url = NULL; + PyObject *turl; - url = py_str_to_c_str(py_url, NULL); + url = py_str_borrow_c_str(&turl, py_url, NULL); if (url != NULL) { err = git_remote_set_url(self->remote, url); - free(url); + Py_DECREF(turl); if (err == GIT_OK) return 0; @@ -440,12 +442,13 @@ int Remote_push_url__set__(Remote *self, PyObject* py_url) { int err; - char* url = NULL; + const char* url = NULL; + PyObject *turl; - url = py_str_to_c_str(py_url, NULL); + url = py_str_borrow_c_str(&turl, py_url, NULL); if (url != NULL) { err = git_remote_set_pushurl(self->remote, url); - free(url); + Py_DECREF(turl); if (err == GIT_OK) return 0; From dcd5acc34eacb2024a2b9e7e206717119eea0240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 4 Feb 2014 12:45:50 +0100 Subject: [PATCH 0682/2237] index entry: avoid extra copy --- src/index.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/index.c b/src/index.c index 5d70a1dcf..90fae7628 100644 --- a/src/index.c +++ b/src/index.c @@ -650,18 +650,12 @@ IndexEntry_path__get__(IndexEntry *self) int IndexEntry_path__set__(IndexEntry *self, PyObject *py_path) { - char *c_inner, *c_path; + char *c_path; - c_inner = py_str_to_c_str(py_path, NULL); - if (!c_inner) + c_path = py_str_to_c_str(py_path, NULL); + if (!c_path) return -1; - c_path = strdup(c_inner); - if (!c_path) { - PyErr_NoMemory(); - return -1; - } - free(self->entry.path); self->entry.path = c_path; From 140305e41011dc7ba37286052927509a93505ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 4 Feb 2014 22:26:46 +0100 Subject: [PATCH 0683/2237] Get ready to release 0.20.2 --- README.rst | 133 ++++++++++++++++++++++++---------------------- docs/conf.py | 2 +- docs/install.rst | 2 +- pygit2/version.py | 2 +- 4 files changed, 72 insertions(+), 67 deletions(-) diff --git a/README.rst b/README.rst index 3b6767a86..a76fee96e 100644 --- a/README.rst +++ b/README.rst @@ -44,42 +44,33 @@ for the topic), send a pull request. Authors ============== -56 developers have contributed at least 1 commit to pygit2:: - - J. David Ibáñez Andrey Devyatkin - Nico von Geyso Ben Davis - Carlos Martín Nieto Eric Schrijver - W. Trevor King Hervé Cauwelier - Dave Borowitz Huang Huang - Daniel Rodríguez Troitiño Jared Flatow - Richo Healey Jiunn Haur Lim - Christian Boos Sarath Lakshman - Julien Miotte Vicent Marti - Jose Plana Zoran Zaric - Martin Lenders Adam Spiers - Victor Garcia Andrew Chin - Xavier Delannoy András Veres-Szentkirályi - Yonggang Luo Benjamin Kircher - Petr Hosek Benjamin Pollack - Valentin Haenel Bryan O'Sullivan - Xu Tao David Fischer - Bernardo Heynemann David Sanders - John Szakmeister Eric Davis - Brodie Rao Erik van Zijst - David Versmisse Ferengee - Rémi Duraffort Gustavo Di Pietro - Sebastian Thiel Hugh Cole-Baker - Fraser Tweedale Josh Bleecher Snyder - Han-Wen Nienhuys Jun Omae - Petr Viktorin Óscar San José - Alex Chamberlain Ridge Kennedy - Amit Bakshi Rui Abreu Ferreira +57 developers have contributed at least 1 commit to pygit2:: + + J. David Ibáñez Brodie Rao Adam Spiers + Nico von Geyso David Versmisse Alexander Bayandin + Carlos Martín Nieto Rémi Duraffort Andrew Chin + W. Trevor King Sebastian Thiel András Veres-Szentkirályi + Dave Borowitz Fraser Tweedale Benjamin Kircher + Daniel Rodríguez Troitiño Han-Wen Nienhuys Benjamin Pollack + Richo Healey Petr Viktorin Bryan O'Sullivan + Christian Boos Alex Chamberlain David Fischer + Julien Miotte Amit Bakshi David Sanders + Xu Tao Andrey Devyatkin Eric Davis + Jose Plana Ben Davis Erik van Zijst + Martin Lenders Eric Schrijver Ferengee + Petr Hosek Hervé Cauwelier Gustavo Di Pietro + Victor Garcia Huang Huang Hugh Cole-Baker + Xavier Delannoy Jared Flatow Josh Bleecher Snyder + Yonggang Luo Jiunn Haur Lim Jun Omae + Valentin Haenel Sarath Lakshman Óscar San José + Bernardo Heynemann Vicent Marti Ridge Kennedy + John Szakmeister Zoran Zaric Rui Abreu Ferreira Changelog ============== -0.20.2 (2014-02-XX) +0.20.2 (2014-02-04) ------------------- - Support pypy @@ -87,23 +78,64 @@ Changelog `#327 `_ `#333 `_ -- New ``Blob.diff(...)`` and ``Blob.diff_to_buffer(...)`` - `#307 `_ +Repository: - New ``Repository.default_signature`` `#310 `_ +Oid: + +- New ``str(Oid)`` deprecates ``Oid.hex`` + `#322 `_ + +Object: + +- New ``Object.id`` deprecates ``Object.oid`` + `#322 `_ + +- New ``TreeEntry.id`` deprecates ``TreeEntry.oid`` + `#322 `_ + +- New ``Blob.diff(...)`` and ``Blob.diff_to_buffer(...)`` + `#307 `_ + - New ``Commit.tree_id`` and ``Commit.parent_ids`` `#73 `_ `#311 `_ +- New rich comparison between tree entries + `#305 `_ + `#313 `_ + +- Now ``Tree.__contains__(key)`` supports paths + `#306 `_ + `#316 `_ + +Index: + +- Now possible to create ``IndexEntry(...)`` + `#325 `_ + +- Now ``IndexEntry.path``, ``IndexEntry.oid`` and ``IndexEntry.mode`` are + writable + `#325 `_ + +- Now ``Index.add(...)`` accepts an ``IndexEntry`` too + `#325 `_ + +- Now ``Index.write_tree(...)`` is able to write to a different repository + `#325 `_ + +- Fix memory leak in ``IndexEntry.path`` setter + `#335 `_ + +Config: + - New ``Config`` iterator replaces ``Config.foreach`` `#183 `_ `#312 `_ -- New rich comparison between tree entries - `#305 `_ - `#313 `_ +Remote: - New type ``Refspec`` `#314 `_ @@ -111,10 +143,6 @@ Changelog - New ``Remote.push_url`` `#315 `_ -- Now ``path in Tree`` works - `#306 `_ - `#316 `_ - - New ``Remote.add_push`` and ``Remote.add_fetch`` `#255 `_ `#318 `_ @@ -127,15 +155,6 @@ Changelog ``Remote.set_push_refspecs(...)`` `#319 `_ -- New ``str(Oid)`` deprecates ``Oid.hex`` - `#322 `_ - -- New ``Object.id`` deprecates ``Object.oid`` - `#322 `_ - -- New ``TreeEntry.id`` deprecates ``TreeEntry.oid`` - `#322 `_ - - New ``Remote.progress``, ``Remote.transfer_progress`` and ``Remote.update_tips`` `#274 `_ @@ -145,25 +164,11 @@ Changelog `#274 `_ `#324 `_ -- Now possible to create ``IndexEntry(...)`` - `#325 `_ - -- Now ``IndexEntry.path``, ``IndexEntry.oid`` and ``IndexEntry.mode`` are - writable - `#325 `_ - -- Now ``Index.add(...)`` accepts an ``IndexEntry`` too - `#325 `_ - -- Now ``Index.write_tree(...)`` is able to write to a different repository - `#325 `_ - - Fix refcount leak in ``Repository.remotes`` `#321 `_ `#332 `_ -- Other non user visible changes: - `#331 `_ +Other: `#331 `_ 0.20.1 (2013-12-24) diff --git a/docs/conf.py b/docs/conf.py index addf93dd3..d7f2799c3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,7 +50,7 @@ # The short X.Y version. version = '0.20' # The full version, including alpha/beta/rc tags. -release = '0.20.1' +release = '0.20.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/install.rst b/docs/install.rst index e237037a2..f74e2d7ac 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -27,7 +27,7 @@ When those are installed, you can install pygit2: .. note:: A minor version of pygit2 must be used with the corresponding minor version of libgit2. For example, pygit2 v0.20.x must be used with libgit2 - v0.20.1. + v0.20.0 Building on \*nix (including OS X) =================================== diff --git a/pygit2/version.py b/pygit2/version.py index 71fbd6f5f..af8676feb 100644 --- a/pygit2/version.py +++ b/pygit2/version.py @@ -23,4 +23,4 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -__version__ = '0.20.1' +__version__ = '0.20.2' From 73170cc104aa2114e824fdece3b513f03866d26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 6 Feb 2014 10:27:21 +0100 Subject: [PATCH 0684/2237] walk: make the sorting mode optional Since the libgit2 has a default sorting method, which we also mention as default in the documentation, there is no particular need to make the user choose a sorting method when the order does not matter. We use sorting NONE in that case. --- src/repository.c | 8 ++++---- test/test_revwalk.py | 10 +++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/repository.c b/src/repository.c index 750517859..fa1d13d51 100644 --- a/src/repository.c +++ b/src/repository.c @@ -626,13 +626,13 @@ Repository_merge(Repository *self, PyObject *py_oid) } PyDoc_STRVAR(Repository_walk__doc__, - "walk(oid, sort_mode) -> iterator\n" + "walk(oid[, sort_mode]) -> iterator\n" "\n" "Generator that traverses the history starting from the given commit.\n" "The following types of sorting could be used to control traversing\n" "direction:\n" "\n" - "* GIT_SORT_NONE. This is the default sorting for new walkers\n" + "* GIT_SORT_NONE. This is the default sorting for new walkers.\n" " Sort the repository contents in no particular ordering\n" "* GIT_SORT_TOPOLOGICAL. Sort the repository contents in topological order\n" " (parents before children); this sorting mode can be combined with\n" @@ -656,13 +656,13 @@ PyObject * Repository_walk(Repository *self, PyObject *args) { PyObject *value; - unsigned int sort; + unsigned int sort = GIT_SORT_NONE; int err; git_oid oid; git_revwalk *walk; Walker *py_walker; - if (!PyArg_ParseTuple(args, "OI", &value, &sort)) + if (!PyArg_ParseTuple(args, "O|I", &value, &sort)) return NULL; err = git_revwalk_new(&walk, self->repo); diff --git a/test/test_revwalk.py b/test/test_revwalk.py index be4a60882..db08c65f9 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -31,7 +31,7 @@ from __future__ import unicode_literals import unittest -from pygit2 import GIT_SORT_TIME, GIT_SORT_REVERSE +from pygit2 import GIT_SORT_NONE, GIT_SORT_TIME, GIT_SORT_REVERSE from . import utils @@ -107,5 +107,13 @@ def test_simplify_first_parent(self): walker.simplify_first_parent() self.assertEqual(len(list(walker)), 3) + def test_default_sorting(self): + walker = self.repo.walk(log[0], GIT_SORT_NONE) + list1 = list([x.id for x in walker]) + walker = self.repo.walk(log[0]) + list2 = list([x.id for x in walker]) + + self.assertEqual(list1, list2) + if __name__ == '__main__': unittest.main() From fa7d24005bd8173d7e392d8fcf2c70da8084e7a0 Mon Sep 17 00:00:00 2001 From: Devaev Maxim Date: Fri, 7 Feb 2014 17:11:06 +0400 Subject: [PATCH 0685/2237] Fixed `undefined symbol: PyLong_AsSize_t` on PyPy --- src/utils.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/utils.h b/src/utils.h index 3b3c42a07..60575b1ea 100644 --- a/src/utils.h +++ b/src/utils.h @@ -61,6 +61,10 @@ #define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict") #endif +#ifdef PYPY_VERSION + #define PyLong_AsSize_t (size_t)PyLong_AsUnsignedLong +#endif + #ifndef Py_hash_t #define Py_hash_t long #endif From 1f3c0170aa0bd3a84e112fe16f7010147e41e992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 11 Feb 2014 17:37:42 +0100 Subject: [PATCH 0686/2237] test: clean up Remove the temporary files and directories we create as part of running the test suite. --- test/test_index.py | 11 +++++------ test/test_repository.py | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test_index.py b/test/test_index.py index 7076e2e12..ac8230b78 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -34,6 +34,7 @@ import tempfile import pygit2 +from pygit2 import Repository from . import utils @@ -154,12 +155,10 @@ def test_change_attributes(self): self.assertEqual(pygit2.GIT_FILEMODE_BLOB_EXECUTABLE, entry.mode) def test_write_tree_to(self): - path = tempfile.mkdtemp() - pygit2.init_repository(path) - nrepo = pygit2.Repository(path) - - id = self.repo.index.write_tree(nrepo) - self.assertNotEqual(None, nrepo[id]) + with utils.TemporaryRepository(('tar', 'emptyrepo')) as path: + nrepo = Repository(path) + id = self.repo.index.write_tree(nrepo) + self.assertNotEqual(None, nrepo[id]) class IndexEntryTest(utils.RepoTestCase): diff --git a/test/test_repository.py b/test/test_repository.py index bba6d76be..8e8101c66 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -165,6 +165,7 @@ def test_hashfile(self): with open(tempfile_path, 'w') as fh: fh.write(data) hashed_sha1 = hashfile(tempfile_path) + os.unlink(tempfile_path) written_sha1 = self.repo.create_blob(data) self.assertEqual(hashed_sha1, written_sha1) From de3dba668ada59d3ea8e87e7d61d05aa2fe5b3a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 11 Feb 2014 17:33:18 +0100 Subject: [PATCH 0687/2237] object: implement peel() This simplifies getting one type of object from a higher-level one as well as accepting a commit-ish or tree-ish. --- docs/objects.rst | 1 + src/object.c | 23 ++++++++++++++++ test/test_object.py | 67 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 test/test_object.py diff --git a/docs/objects.rst b/docs/objects.rst index ecb39e998..1515dc770 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -81,6 +81,7 @@ This is the common interface for all Git objects: .. autoattribute:: pygit2.Object.id .. autoattribute:: pygit2.Object.type .. automethod:: pygit2.Object.read_raw +.. automethod:: pygit2.Object.peel Blobs diff --git a/src/object.c b/src/object.c index e17ef3f12..56e9e4b87 100644 --- a/src/object.c +++ b/src/object.c @@ -127,6 +127,28 @@ Object_read_raw(Object *self) return aux; } +PyDoc_STRVAR(Object_peel__doc__, + "peel(target_type) -> Object\n" + "\n" + "Peel the current object and returns the first object of the given type\n"); + +PyObject * +Object_peel(Object *self, PyObject *py_type) +{ + int type, err; + git_object *peeled; + + type = PyLong_AsLong(py_type); + if (type == -1 && PyErr_Occurred()) + return NULL; + + err = git_object_peel(&peeled, self->obj, (git_otype)type); + if (err < 0) + return Error_set(err); + + return wrap_object(peeled, self->repo); +} + PyGetSetDef Object_getseters[] = { GETTER(Object, oid), GETTER(Object, id), @@ -137,6 +159,7 @@ PyGetSetDef Object_getseters[] = { PyMethodDef Object_methods[] = { METHOD(Object, read_raw, METH_NOARGS), + METHOD(Object, peel, METH_O), {NULL} }; diff --git a/test/test_object.py b/test/test_object.py new file mode 100644 index 000000000..495b17273 --- /dev/null +++ b/test/test_object.py @@ -0,0 +1,67 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2010-2014 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. + +"""Tests for Object objects.""" + +from __future__ import absolute_import +from __future__ import unicode_literals +from os.path import dirname, join +import unittest + +import pygit2 +from pygit2 import GIT_OBJ_TREE, GIT_OBJ_TAG +from . import utils + + +BLOB_SHA = 'a520c24d85fbfc815d385957eed41406ca5a860b' +BLOB_CONTENT = """hello world +hola mundo +bonjour le monde +""".encode() +BLOB_NEW_CONTENT = b'foo bar\n' +BLOB_FILE_CONTENT = b'bye world\n' + +class ObjectTest(utils.RepoTestCase): + + def test_peel_commit(self): + # start by looking up the commit + commit_id = self.repo.lookup_reference('refs/heads/master').target + commit = self.repo[commit_id] + # and peel to the tree + tree = commit.peel(GIT_OBJ_TREE) + + self.assertEqual(type(tree), pygit2.Tree) + self.assertEqual(str(tree.id), 'fd937514cb799514d4b81bb24c5fcfeb6472b245') + + def test_invalid(self): + commit_id = self.repo.lookup_reference('refs/heads/master').target + commit = self.repo[commit_id] + + self.assertRaises(ValueError, commit.peel, GIT_OBJ_TAG) + +if __name__ == '__main__': + unittest.main() From b2cd25cf002bf201eee5e127430703e7f6d46493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 11 Feb 2014 18:20:00 +0100 Subject: [PATCH 0688/2237] object: allow passing a type object to peel() Allow usage of a python type instead of having to use the libgit2 constants. --- src/object.c | 33 ++++++++++++++++++++++++++++++--- test/test_object.py | 19 +++++++++++++++++-- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/object.c b/src/object.c index 56e9e4b87..74c380129 100644 --- a/src/object.c +++ b/src/object.c @@ -127,6 +127,24 @@ Object_read_raw(Object *self) return aux; } +static git_otype +py_type_to_git_type(PyTypeObject *py_type) +{ + git_otype type = GIT_OBJ_BAD; + + if (py_type == &CommitType) { + type = GIT_OBJ_COMMIT; + } else if (py_type == &TreeType) { + type = GIT_OBJ_TREE; + } else if (py_type == &BlobType) { + type = GIT_OBJ_BLOB; + } else if (py_type == &TagType) { + type = GIT_OBJ_TAG; + } + + return type; +} + PyDoc_STRVAR(Object_peel__doc__, "peel(target_type) -> Object\n" "\n" @@ -135,12 +153,21 @@ PyDoc_STRVAR(Object_peel__doc__, PyObject * Object_peel(Object *self, PyObject *py_type) { - int type, err; + int type = -1, err; git_object *peeled; - type = PyLong_AsLong(py_type); - if (type == -1 && PyErr_Occurred()) + if (PyLong_Check(py_type)) { + type = PyLong_AsLong(py_type); + if (type == -1 && PyErr_Occurred()) + return NULL; + } else if (PyType_Check(py_type)) { + type = py_type_to_git_type((PyTypeObject *) py_type); + } + + if (type == -1) { + PyErr_SetString(PyExc_ValueError, "invalid target type"); return NULL; + } err = git_object_peel(&peeled, self->obj, (git_otype)type); if (err < 0) diff --git a/test/test_object.py b/test/test_object.py index 495b17273..663040418 100644 --- a/test/test_object.py +++ b/test/test_object.py @@ -33,7 +33,7 @@ import unittest import pygit2 -from pygit2 import GIT_OBJ_TREE, GIT_OBJ_TAG +from pygit2 import GIT_OBJ_TREE, GIT_OBJ_TAG, Tree, Tag from . import utils @@ -54,14 +54,29 @@ def test_peel_commit(self): # and peel to the tree tree = commit.peel(GIT_OBJ_TREE) - self.assertEqual(type(tree), pygit2.Tree) + self.assertEqual(type(tree), Tree) self.assertEqual(str(tree.id), 'fd937514cb799514d4b81bb24c5fcfeb6472b245') + def test_peel_commit_type(self): + commit_id = self.repo.lookup_reference('refs/heads/master').target + commit = self.repo[commit_id] + tree = commit.peel(Tree) + + self.assertEqual(type(tree), Tree) + self.assertEqual(str(tree.id), 'fd937514cb799514d4b81bb24c5fcfeb6472b245') + + def test_invalid(self): commit_id = self.repo.lookup_reference('refs/heads/master').target commit = self.repo[commit_id] self.assertRaises(ValueError, commit.peel, GIT_OBJ_TAG) + def test_invalid_type(self): + commit_id = self.repo.lookup_reference('refs/heads/master').target + commit = self.repo[commit_id] + + self.assertRaises(ValueError, commit.peel, Tag) + if __name__ == '__main__': unittest.main() From 92547db18dc17f60a39f9f77397286b1e75a603f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 11 Feb 2014 22:03:23 +0100 Subject: [PATCH 0689/2237] docs: clarify git-init recipe Fixes #336 --- docs/recipes/git-init.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/recipes/git-init.rst b/docs/recipes/git-init.rst index 75211a99a..9af6af0a8 100644 --- a/docs/recipes/git-init.rst +++ b/docs/recipes/git-init.rst @@ -12,11 +12,11 @@ Create bare repository .. code-block:: bash - $> git init --bare relative/path + $ git init --bare path/to/git .. code-block:: python - >>> pygit2.init_repository('relative/path', True) + >>> pygit2.init_repository('path/to/git', True) ====================================================================== @@ -25,17 +25,17 @@ Create standard repository .. code-block:: bash - $> git init relative/path + $ git init path/to/git .. code-block:: python - >>> pygit2.init_repository('relative/path', False) + >>> pygit2.init_repository('path/to/git', False) ---------------------------------------------------------------------- References ---------------------------------------------------------------------- -- git-init_. +- git-init_ .. _git-init: https://www.kernel.org/pub/software/scm/git/docs/git-init.html From a794d66558e390d2e0cbe85ac65bde9dcca37fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 11 Feb 2014 22:48:34 +0100 Subject: [PATCH 0690/2237] docs: document TreeBuilder.get Fixes #302 --- docs/objects.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/objects.rst b/docs/objects.rst index 1515dc770..3583c17ec 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -214,6 +214,7 @@ Creating trees .. automethod:: pygit2.TreeBuilder.remove .. automethod:: pygit2.TreeBuilder.clear .. automethod:: pygit2.TreeBuilder.write +.. automethod:: pygit2.TreeBuilder.get Commits From db940931facf01ae24b42ec7efd2f3b559e19b78 Mon Sep 17 00:00:00 2001 From: Erik Meusel Date: Wed, 12 Feb 2014 07:46:53 +0100 Subject: [PATCH 0691/2237] make remote.c build with VS compiler 2008 --- src/remote.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index 89c5dda55..39a593d1b 100644 --- a/src/remote.c +++ b/src/remote.c @@ -267,6 +267,7 @@ get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) Py_ssize_t index, n; PyObject *item; void *ptr; + char *str; if (!PyList_Check(pylist)) { PyErr_SetString(PyExc_TypeError, "Value must be a list"); @@ -287,7 +288,7 @@ get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) for (index = 0; index < n; index++) { item = PyList_GetItem(pylist, index); - char *str = py_str_to_c_str(item, NULL); + str = py_str_to_c_str(item, NULL); if (!str) goto on_error; From 1e6062932ced54d6ac171a27cf463cba929cb784 Mon Sep 17 00:00:00 2001 From: Leonardo Rhodes Date: Wed, 12 Feb 2014 21:47:41 +0100 Subject: [PATCH 0692/2237] Added git_add_all Moved git strarray conversion to utils --- src/index.c | 26 ++++++++++++++++++- src/index.h | 1 + src/remote.c | 64 ----------------------------------------------- src/utils.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/utils.h | 2 ++ 5 files changed, 98 insertions(+), 65 deletions(-) diff --git a/src/index.c b/src/index.c index 90fae7628..6e6f1e113 100644 --- a/src/index.c +++ b/src/index.c @@ -81,7 +81,6 @@ Index_traverse(Index *self, visitproc visit, void *arg) return 0; } - PyDoc_STRVAR(Index_add__doc__, "add([path|entry])\n" "\n" @@ -114,6 +113,31 @@ Index_add(Index *self, PyObject *args) } +PyDoc_STRVAR(Index_add_all__doc__, + "add_all([file names|glob pattern])\n" + "\n" + "Add or update index entries matching files in the working directory."); + +PyObject * +Index_add_all(Index *self, PyObject *pylist) +{ + int err; + git_strarray pathspec; + + if (get_strarraygit_from_pylist(&pathspec, pylist) < 0) + return -1; + + err = git_index_add_all(self->index, &pathspec, 0, NULL, NULL); + git_strarray_free(&pathspec); + + if (err < 0) { + Error_set(err); + return -1; + } + + return 0; +} + PyDoc_STRVAR(Index_clear__doc__, "clear()\n" "\n" diff --git a/src/index.h b/src/index.h index cd570aa0c..44c150d5f 100644 --- a/src/index.h +++ b/src/index.h @@ -33,6 +33,7 @@ #include PyObject* Index_add(Index *self, PyObject *args); +PyObject* Index_add_all(Index *self, PyObject *pylist); PyObject* Index_clear(Index *self); PyObject* Index_find(Index *self, PyObject *py_path); PyObject* Index_read(Index *self, PyObject *args); diff --git a/src/remote.c b/src/remote.c index 39a593d1b..ead4b1495 100644 --- a/src/remote.c +++ b/src/remote.c @@ -243,70 +243,6 @@ Remote_name__set__(Remote *self, PyObject* py_name) return -1; } - -PyObject * -get_pylist_from_git_strarray(git_strarray *strarray) -{ - int index; - PyObject *new_list; - - new_list = PyList_New(strarray->count); - if (new_list == NULL) - return NULL; - - for (index = 0; index < strarray->count; index++) - PyList_SET_ITEM(new_list, index, - to_unicode(strarray->strings[index], NULL, NULL)); - - return new_list; -} - -int -get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) -{ - Py_ssize_t index, n; - PyObject *item; - void *ptr; - char *str; - - if (!PyList_Check(pylist)) { - PyErr_SetString(PyExc_TypeError, "Value must be a list"); - return -1; - } - - n = PyList_Size(pylist); - - /* allocate new git_strarray */ - ptr = calloc(n, sizeof(char *)); - if (!ptr) { - PyErr_SetNone(PyExc_MemoryError); - return -1; - } - - array->strings = ptr; - array->count = n; - - for (index = 0; index < n; index++) { - item = PyList_GetItem(pylist, index); - str = py_str_to_c_str(item, NULL); - if (!str) - goto on_error; - - array->strings[index] = str; - } - - return 0; - -on_error: - n = index; - for (index = 0; index < n; index++) { - free(array->strings[index]); - } - free(array->strings); - - return -1; -} - PyDoc_STRVAR(Remote_fetch_refspecs__doc__, "Fetch refspecs"); PyObject * diff --git a/src/utils.c b/src/utils.c index e63ea9dbf..44acf5c6a 100644 --- a/src/utils.c +++ b/src/utils.c @@ -83,3 +83,73 @@ py_str_borrow_c_str(PyObject **tvalue, PyObject *value, const char *encoding) Py_TYPE(value)->tp_name); return NULL; } + +/** + * Converts the (struct) git_strarray to a Python list + */ +PyObject * +get_pylist_from_git_strarray(git_strarray *strarray) +{ + int index; + PyObject *new_list; + + new_list = PyList_New(strarray->count); + if (new_list == NULL) + return NULL; + + for (index = 0; index < strarray->count; index++) + PyList_SET_ITEM(new_list, index, + to_unicode(strarray->strings[index], NULL, NULL)); + + return new_list; +} + +/** + * Converts the Python list to struct git_strarray + * returns -1 if conversion failed + */ +int +get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) +{ + Py_ssize_t index, n; + PyObject *item; + void *ptr; + char *str; + + if (!PyList_Check(pylist)) { + PyErr_SetString(PyExc_TypeError, "Value must be a list"); + return -1; + } + + n = PyList_Size(pylist); + + /* allocate new git_strarray */ + ptr = calloc(n, sizeof(char *)); + if (!ptr) { + PyErr_SetNone(PyExc_MemoryError); + return -1; + } + + array->strings = ptr; + array->count = n; + + for (index = 0; index < n; index++) { + item = PyList_GetItem(pylist, index); + str = py_str_to_c_str(item, NULL); + if (!str) + goto on_error; + + array->strings[index] = str; + } + + return 0; + +on_error: + n = index; + for (index = 0; index < n; index++) { + free(array->strings[index]); + } + free(array->strings); + + return -1; +} diff --git a/src/utils.h b/src/utils.h index 60575b1ea..81744f686 100644 --- a/src/utils.h +++ b/src/utils.h @@ -114,6 +114,8 @@ to_bytes(const char * value) char * py_str_to_c_str(PyObject *value, const char *encoding); const char *py_str_borrow_c_str(PyObject **tvaue, PyObject *value, const char *encoding); +PyObject * get_pylist_from_git_strarray(git_strarray *strarray); +int get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist); #define py_path_to_c_str(py_path) \ py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding) From 7fd633239f27b77f602478fa9a4ae8da639d81c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 12 Feb 2014 22:11:10 +0100 Subject: [PATCH 0693/2237] Some coding-style fixes, like not using tabs --- src/remote.c | 37 +++++++++++++++++++++++-------------- src/repository.c | 4 ++-- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/remote.c b/src/remote.c index 39a593d1b..964cc0703 100644 --- a/src/remote.c +++ b/src/remote.c @@ -67,23 +67,32 @@ TransferProgress_dealloc(TransferProgress *self) } PyMemberDef TransferProgress_members[] = { - RMEMBER(TransferProgress, total_objects, T_UINT, "Total number objects to download"), - RMEMBER(TransferProgress, indexed_objects, T_UINT, "Objects which have been indexed"), - RMEMBER(TransferProgress, received_objects, T_UINT, "Objects which have been received up to now"), - RMEMBER(TransferProgress, local_objects, T_UINT, "Local objects which were used to fix the thin pack"), - RMEMBER(TransferProgress, total_deltas, T_UINT, "Total number of deltas in the pack"), - RMEMBER(TransferProgress, indexed_deltas, T_UINT, "Deltas which have been indexed"), - /* FIXME: technically this is unsigned, but there's no value for size_t here. */ - RMEMBER(TransferProgress, received_bytes, T_PYSSIZET, "Number of bytes received up to now"), + RMEMBER(TransferProgress, total_objects, T_UINT, + "Total number objects to download"), + RMEMBER(TransferProgress, indexed_objects, T_UINT, + "Objects which have been indexed"), + RMEMBER(TransferProgress, received_objects, T_UINT, + "Objects which have been received up to now"), + RMEMBER(TransferProgress, local_objects, T_UINT, + "Local objects which were used to fix the thin pack"), + RMEMBER(TransferProgress, total_deltas, T_UINT, + "Total number of deltas in the pack"), + RMEMBER(TransferProgress, indexed_deltas, T_UINT, + "Deltas which have been indexed"), + /* FIXME: technically this is unsigned, but there's no value for size_t + * here. */ + RMEMBER(TransferProgress, received_bytes, T_PYSSIZET, + "Number of bytes received up to now"), {NULL}, }; -PyDoc_STRVAR(TransferProgress__doc__, "Progress downloading and indexing data during a fetch"); +PyDoc_STRVAR(TransferProgress__doc__, + "Progress downloading and indexing data during a fetch"); PyTypeObject TransferProgressType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.TransferProgress", /* tp_name */ - sizeof(TransferProgress), /* tp_basicsize */ + sizeof(TransferProgress), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)TransferProgress_dealloc, /* tp_dealloc */ 0, /* tp_print */ @@ -101,7 +110,7 @@ PyTypeObject TransferProgressType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ - TransferProgress__doc__, /* tp_doc */ + TransferProgress__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -232,7 +241,7 @@ Remote_name__set__(Remote *self, PyObject* py_name) name = py_str_borrow_c_str(&tname, py_name, NULL); if (name != NULL) { err = git_remote_rename(self->remote, name, NULL, NULL); - Py_DECREF(tname); + Py_DECREF(tname); if (err == GIT_OK) return 0; @@ -412,7 +421,7 @@ Remote_url__set__(Remote *self, PyObject* py_url) url = py_str_borrow_c_str(&turl, py_url, NULL); if (url != NULL) { err = git_remote_set_url(self->remote, url); - Py_DECREF(turl); + Py_DECREF(turl); if (err == GIT_OK) return 0; @@ -449,7 +458,7 @@ Remote_push_url__set__(Remote *self, PyObject* py_url) url = py_str_borrow_c_str(&turl, py_url, NULL); if (url != NULL) { err = git_remote_set_pushurl(self->remote, url); - Py_DECREF(turl); + Py_DECREF(turl); if (err == GIT_OK) return 0; diff --git a/src/repository.c b/src/repository.c index fa1d13d51..1e654e28b 100644 --- a/src/repository.c +++ b/src/repository.c @@ -333,7 +333,7 @@ Repository_revparse_single(Repository *self, PyObject *py_spec) if (err < 0) { PyObject *err_obj = Error_set_str(err, c_spec); - Py_DECREF(tspec); + Py_DECREF(tspec); return err_obj; } Py_DECREF(tspec); @@ -1051,7 +1051,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) err = git_reference_lookup(&c_reference, self->repo, c_name); if (err < 0) { PyObject *err_obj = Error_set_str(err, c_name); - free(c_name); + free(c_name); return err_obj; } free(c_name); From 9223d75bb0ebff8ef263158978c4829eeec5aacb Mon Sep 17 00:00:00 2001 From: Leonardo Rhodes Date: Wed, 12 Feb 2014 22:20:09 +0100 Subject: [PATCH 0694/2237] fixed wrong return value --- src/index.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/index.c b/src/index.c index 6e6f1e113..cc1b365df 100644 --- a/src/index.c +++ b/src/index.c @@ -125,17 +125,17 @@ Index_add_all(Index *self, PyObject *pylist) git_strarray pathspec; if (get_strarraygit_from_pylist(&pathspec, pylist) < 0) - return -1; + return NULL; err = git_index_add_all(self->index, &pathspec, 0, NULL, NULL); git_strarray_free(&pathspec); if (err < 0) { Error_set(err); - return -1; + return NULL; } - return 0; + Py_RETURN_NONE; } PyDoc_STRVAR(Index_clear__doc__, @@ -481,6 +481,7 @@ Index_write_tree(Index *self, PyObject *args) PyMethodDef Index_methods[] = { METHOD(Index, add, METH_VARARGS), + METHOD(Index, add_all, METH_O), METHOD(Index, remove, METH_VARARGS), METHOD(Index, clear, METH_NOARGS), METHOD(Index, diff_to_workdir, METH_VARARGS), From e322a3a678903fb9b801079ff4216e877519ba29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 12 Feb 2014 23:05:51 +0100 Subject: [PATCH 0695/2237] Silence a couple of valgrind warnings --- src/branch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/branch.c b/src/branch.c index 8f78de8bb..bcf763677 100644 --- a/src/branch.c +++ b/src/branch.c @@ -161,7 +161,7 @@ Branch_remote_name__get__(Branch *self) return Error_set(err); } - py_name = to_unicode(c_name, NULL, NULL); + py_name = to_unicode_n(c_name, err - 1, NULL, NULL); free(c_name); return py_name; @@ -253,7 +253,7 @@ Branch_upstream_name__get__(Branch *self) return Error_set(err); } - py_name = to_unicode(c_name, NULL, NULL); + py_name = to_unicode_n(c_name, err - 1, NULL, NULL); free(c_name); return py_name; From 1a17256ee31f9954eeb3f2726c6cc512b1c69ab1 Mon Sep 17 00:00:00 2001 From: Leonardo Rhodes Date: Thu, 13 Feb 2014 23:09:40 +0100 Subject: [PATCH 0696/2237] added unit test --- test/test_index.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/test_index.py b/test/test_index.py index ac8230b78..7bed86bb3 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -74,6 +74,42 @@ def test_add(self): self.assertEqual(len(index), 3) self.assertEqual(index['bye.txt'].hex, sha) + def test_add_all(self): + self.test_clear() + + index = self.repo.index + + sha_bye = '0907563af06c7464d62a70cdd135a6ba7d2b41d8' + sha_hello = 'a520c24d85fbfc815d385957eed41406ca5a860b' + + index.add_all(['*.txt']) + + self.assertTrue('bye.txt' in index) + self.assertTrue('hello.txt' in index) + + self.assertEqual(index['bye.txt'].hex, sha_bye) + self.assertEqual(index['hello.txt'].hex, sha_hello) + + self.test_clear() + + index.add_all(['bye.t??', 'hello.*']) + + self.assertTrue('bye.txt' in index) + self.assertTrue('hello.txt' in index) + + self.assertEqual(index['bye.txt'].hex, sha_bye) + self.assertEqual(index['hello.txt'].hex, sha_hello) + + self.test_clear() + + index.add_all(['[byehlo]*.txt']) + + self.assertTrue('bye.txt' in index) + self.assertTrue('hello.txt' in index) + + self.assertEqual(index['bye.txt'].hex, sha_bye) + self.assertEqual(index['hello.txt'].hex, sha_hello) + def test_clear(self): index = self.repo.index self.assertEqual(len(index), 2) From 0e4a27284f64a390911316cc48f2c4b344099a49 Mon Sep 17 00:00:00 2001 From: earl Date: Fri, 21 Feb 2014 10:21:32 +0100 Subject: [PATCH 0697/2237] diff: Fix for memory leak in git_patch --- src/diff.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/diff.c b/src/diff.c index 8e59ebaa0..b3a32ddc2 100644 --- a/src/diff.c +++ b/src/diff.c @@ -127,6 +127,7 @@ wrap_patch(git_patch *patch) } } } + git_patch_free(patch); return (PyObject*) py_patch; } From ab441967302b0d2eafb84a1c0a7bad90edc99156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 23 Mar 2014 12:55:12 +0100 Subject: [PATCH 0698/2237] Introduce libgit2 options Allow setting and getting the mwindow size and search paths. --- src/options.c | 185 +++++++++++++++++++++++++++++++++++++++++++ src/options.h | 51 ++++++++++++ src/pygit2.c | 13 +++ test/test_options.py | 57 +++++++++++++ 4 files changed, 306 insertions(+) create mode 100644 src/options.c create mode 100644 src/options.h create mode 100644 test/test_options.py diff --git a/src/options.c b/src/options.c new file mode 100644 index 000000000..abfa4656a --- /dev/null +++ b/src/options.c @@ -0,0 +1,185 @@ +/* + * Copyright 2010-2014 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 "error.h" +#include "types.h" +#include "utils.h" + +extern PyObject *GitError; + +static PyObject * +get_search_path(long level) +{ + char *buf = NULL; + size_t len = 64; + PyObject *py_path; + int error; + + do { + len *= 2; + char *tmp = realloc(buf, len); + if (!tmp) { + free(buf); + PyErr_NoMemory(); + return NULL; + } + buf = tmp; + + error = git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, level, buf, len); + } while(error == GIT_EBUFS); + + if (error < 0) { + free(buf); + Error_set(error); + return NULL; + } + + if (!buf) + return NULL; + + py_path = to_unicode(buf, NULL, NULL); + free(buf); + + if (!py_path) + return NULL; + + return py_path; +} + +PyObject * +option(PyObject *self, PyObject *args) +{ + long option; + int error; + PyObject *py_option; + + py_option = PyTuple_GetItem(args, 0); + if (!py_option) + return NULL; + + if (!PyLong_Check(py_option)) + goto on_non_integer; + + option = PyLong_AsLong(py_option); + + switch (option) { + case GIT_OPT_GET_SEARCH_PATH: + { + PyObject *py_level; + + py_level = PyTuple_GetItem(args, 1); + if (!py_level) + return NULL; + + if (!PyLong_Check(py_level)) + goto on_non_integer; + + return get_search_path(PyLong_AsLong(py_level)); + break; + } + + case GIT_OPT_SET_SEARCH_PATH: + { + PyObject *py_level, *py_path, *tpath; + const char *path; + int err; + + py_level = PyTuple_GetItem(args, 1); + if (!py_level) + return NULL; + + py_path = PyTuple_GetItem(args, 2); + if (!py_path) + return NULL; + + if (!PyLong_Check(py_level)) + goto on_non_integer; + + path = py_str_borrow_c_str(&tpath, py_path, NULL); + if (!path) + return NULL; + + err = git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, PyLong_AsLong(py_level), path); + Py_DECREF(tpath); + + if (err < 0) { + Error_set(err); + return NULL; + } + + Py_RETURN_NONE; + break; + } + + case GIT_OPT_GET_MWINDOW_SIZE: + { + size_t size; + + if ((error = git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &size)) < 0) { + Error_set(error); + return NULL; + } + + return PyLong_FromSize_t(size); + + break; + } + + case GIT_OPT_SET_MWINDOW_SIZE: + { + size_t size; + PyObject *py_size; + + py_size = PyTuple_GetItem(args, 1); + if (!py_size) + return NULL; + + if (!PyLong_Check(py_size)) + goto on_non_integer; + + + size = PyLong_AsSize_t(py_size); + if ((error = git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, size)) < 0) { + Error_set(error); + return NULL; + } + + Py_RETURN_NONE; + break; + } + } + + PyErr_SetString(PyExc_ValueError, "unknown/unsupported option value"); + return NULL; + +on_non_integer: + PyErr_SetString(PyExc_TypeError, "option is not an integer"); + return NULL; +} diff --git a/src/options.h b/src/options.h new file mode 100644 index 000000000..8dccda11c --- /dev/null +++ b/src/options.h @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2014 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. + */ + +#ifndef INCLUDE_pygit2_blame_h +#define INCLUDE_pygit2_blame_h + +#define PY_SSIZE_T_CLEAN +#include +#include +#include "types.h" + +PyDoc_STRVAR(option__doc__, + "Get or set a libgit2 option\n" + "Arguments:\n" + " (GIT_OPT_GET_SEARCH_PATH, level)\n" + " Get the config search path for the given level\n" + " (GIT_OPT_SET_SEARCH_PATH, level, path)\n" + " Set the config search path for the given level\n" + " (GIT_OPT_GET_MWINDOW_SIZE)\n" + " Get the maximum mmap window size\n" + " (GIT_OPT_SET_MWINDOW_SIZE, size)\n" + " Set the maximum mmap window size\n"); + + +PyObject *option(PyObject *self, PyObject *args); + +#endif diff --git a/src/pygit2.c b/src/pygit2.c index 77535c735..9b6319e6d 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -39,6 +39,7 @@ #include "utils.h" #include "repository.h" #include "oid.h" +#include "options.h" /* FIXME: This is for pypy */ #ifndef MAXPATHLEN @@ -244,6 +245,7 @@ PyMethodDef module_methods[] = { discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, {"hash", hash, METH_VARARGS, hash__doc__}, + {"option", option, METH_VARARGS, option__doc__}, {NULL} }; @@ -259,6 +261,12 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, LIBGIT2_VER_REVISION) ADD_CONSTANT_STR(m, LIBGIT2_VERSION) + /* libgit2 options */ + ADD_CONSTANT_INT(m, GIT_OPT_GET_SEARCH_PATH); + ADD_CONSTANT_INT(m, GIT_OPT_SET_SEARCH_PATH); + ADD_CONSTANT_INT(m, GIT_OPT_GET_MWINDOW_SIZE); + ADD_CONSTANT_INT(m, GIT_OPT_SET_MWINDOW_SIZE); + /* Errors */ GitError = PyErr_NewException("_pygit2.GitError", NULL, NULL); Py_INCREF(GitError); @@ -424,6 +432,11 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_DIFF_FIND_AND_BREAK_REWRITES) /* Config */ + ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_LOCAL); + ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_GLOBAL); + ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_XDG); + ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_SYSTEM); + INIT_TYPE(ConfigType, NULL, PyType_GenericNew) INIT_TYPE(ConfigIterType, NULL, NULL) ADD_TYPE(m, Config) diff --git a/test/test_options.py b/test/test_options.py new file mode 100644 index 000000000..e35f52008 --- /dev/null +++ b/test/test_options.py @@ -0,0 +1,57 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2010-2014 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. + +"""Tests for Blame objects.""" + +from __future__ import absolute_import +from __future__ import unicode_literals +import unittest +import pygit2 +from pygit2 import GIT_OPT_GET_MWINDOW_SIZE, GIT_OPT_SET_MWINDOW_SIZE +from pygit2 import GIT_OPT_GET_SEARCH_PATH, GIT_OPT_SET_SEARCH_PATH +from pygit2 import GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_XDG, GIT_CONFIG_LEVEL_GLOBAL +from pygit2 import option +from . import utils + +class OptionsTest(utils.NoRepoTestCase): + + def test_mwindow_size(self): + new_size = 200 * 1024 + option(GIT_OPT_SET_MWINDOW_SIZE, new_size) + self.assertEqual(new_size, option(GIT_OPT_GET_MWINDOW_SIZE)) + + def test_search_path(self): + paths = [(GIT_CONFIG_LEVEL_GLOBAL, '/tmp/global'), + (GIT_CONFIG_LEVEL_XDG, '/tmp/xdg'), + (GIT_CONFIG_LEVEL_SYSTEM, '/tmp/etc')] + + for level, path in paths: + option(GIT_OPT_SET_SEARCH_PATH, level, path) + self.assertEqual(path, option(GIT_OPT_GET_SEARCH_PATH, level)) + +if __name__ == '__main__': + unittest.main() From 48ff3a8983467fd86f64636b8751ef2e2beda0f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 23 Mar 2014 15:53:32 +0100 Subject: [PATCH 0699/2237] Create proxy object for libgit2 options pygit2.settings proxies though the lower-level varargs option() via getters and setters which provide a more natural abstraction. --- pygit2/__init__.py | 3 +++ pygit2/settings.py | 52 ++++++++++++++++++++++++++++++++++++++++++++ test/test_options.py | 15 +++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 pygit2/settings.py diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 244fdad1e..8d40fc487 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -35,6 +35,7 @@ # High level API from .repository import Repository from .version import __version__ +from .settings import Settings def init_repository(path, bare=False): @@ -75,3 +76,5 @@ def clone_repository( _pygit2.clone_repository( url, path, bare, ignore_cert_errors, remote_name, checkout_branch) return Repository(path) + +settings = Settings() diff --git a/pygit2/settings.py b/pygit2/settings.py new file mode 100644 index 000000000..d6e41998c --- /dev/null +++ b/pygit2/settings.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2010-2014 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. + +from _pygit2 import option +from _pygit2 import GIT_OPT_GET_SEARCH_PATH, GIT_OPT_SET_SEARCH_PATH +from _pygit2 import GIT_OPT_GET_MWINDOW_SIZE, GIT_OPT_SET_MWINDOW_SIZE + +class SearchPathList(object): + + def __getitem__(self, key): + return option(GIT_OPT_GET_SEARCH_PATH, key) + + def __setitem__(self, key, value): + option(GIT_OPT_SET_SEARCH_PATH, key, value) + +class Settings(object): + + __slots__ = ['mwindow_size', 'search_path'] + + search_path = SearchPathList() + + @property + def mwindow_size(self): + return option(GIT_OPT_GET_MWINDOW_SIZE) + + @mwindow_size.setter + def mwindow_size(self, value): + option(GIT_OPT_SET_MWINDOW_SIZE, value) diff --git a/test/test_options.py b/test/test_options.py index e35f52008..a6bc22b46 100644 --- a/test/test_options.py +++ b/test/test_options.py @@ -44,6 +44,12 @@ def test_mwindow_size(self): option(GIT_OPT_SET_MWINDOW_SIZE, new_size) self.assertEqual(new_size, option(GIT_OPT_GET_MWINDOW_SIZE)) + def test_mwindow_size_proxy(self): + new_size = 300 * 1024 + pygit2.settings.mwindow_size = new_size + + self.assertEqual(new_size, pygit2.settings.mwindow_size) + def test_search_path(self): paths = [(GIT_CONFIG_LEVEL_GLOBAL, '/tmp/global'), (GIT_CONFIG_LEVEL_XDG, '/tmp/xdg'), @@ -53,5 +59,14 @@ def test_search_path(self): option(GIT_OPT_SET_SEARCH_PATH, level, path) self.assertEqual(path, option(GIT_OPT_GET_SEARCH_PATH, level)) + def test_search_path_proxy(self): + paths = [(GIT_CONFIG_LEVEL_GLOBAL, '/tmp2/global'), + (GIT_CONFIG_LEVEL_XDG, '/tmp2/xdg'), + (GIT_CONFIG_LEVEL_SYSTEM, '/tmp2/etc')] + + for level, path in paths: + pygit2.settings.search_path[level] = path + self.assertEqual(path, pygit2.settings.search_path[level]) + if __name__ == '__main__': unittest.main() From 6bdb0135924fe8370f5382b3d01648b5b265ee90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 23 Mar 2014 21:21:01 +0100 Subject: [PATCH 0700/2237] settings: python 3.3 compat Python 3.3 doesn't like us overriding properties in __slots__, so set it to an empty list. --- pygit2/settings.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pygit2/settings.py b/pygit2/settings.py index d6e41998c..6455de17e 100644 --- a/pygit2/settings.py +++ b/pygit2/settings.py @@ -39,9 +39,13 @@ def __setitem__(self, key, value): class Settings(object): - __slots__ = ['mwindow_size', 'search_path'] + __slots__ = [] - search_path = SearchPathList() + _search_path = SearchPathList() + + @property + def search_path(self): + return self._search_path @property def mwindow_size(self): From 87c8aef7d999ac52302c76373a66317f1e194803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 23 Mar 2014 21:33:10 +0100 Subject: [PATCH 0701/2237] Settings: add documentation The search_path attribute is now inside __init__() so it shows properly in the generated documentation. --- docs/index.rst | 1 + docs/settings.rst | 8 ++++++++ pygit2/settings.py | 8 ++++++++ 3 files changed, 17 insertions(+) create mode 100644 docs/settings.rst diff --git a/docs/index.rst b/docs/index.rst index f838b3518..6eccbd564 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -45,6 +45,7 @@ Usage guide: config remotes blame + settings Indices and tables diff --git a/docs/settings.rst b/docs/settings.rst new file mode 100644 index 000000000..f1d7a9871 --- /dev/null +++ b/docs/settings.rst @@ -0,0 +1,8 @@ +********************************************************************** +Settings +********************************************************************** + +.. contents:: + +.. autoclass:: pygit2.Settings + :members: diff --git a/pygit2/settings.py b/pygit2/settings.py index 6455de17e..b5805637b 100644 --- a/pygit2/settings.py +++ b/pygit2/settings.py @@ -38,6 +38,7 @@ def __setitem__(self, key, value): option(GIT_OPT_SET_SEARCH_PATH, key, value) class Settings(object): + """Library-wide settings""" __slots__ = [] @@ -45,10 +46,17 @@ class Settings(object): @property def search_path(self): + """Configuration file search path. + + This behaves like an array whose indices correspond to the + GIT_CONFIG_LEVEL_* values. The local search path cannot be + changed. + """ return self._search_path @property def mwindow_size(self): + """Maximum mmap window size""" return option(GIT_OPT_GET_MWINDOW_SIZE) @mwindow_size.setter From af2528418a1bb627ea066c1534230b036c915426 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Tue, 25 Mar 2014 15:13:10 -0700 Subject: [PATCH 0702/2237] More informative repr for Repository objects --- pygit2/repository.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pygit2/repository.py b/pygit2/repository.py index 4b7876579..e6c750f31 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -56,6 +56,8 @@ def __getitem__(self, key): def __contains__(self, key): return self.git_object_lookup_prefix(key) is not None + def __repr__(self): + return "pygit2.Repository(%r)" % self.path # # References From b49da5396228526a1a9ca7400437c28027662200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 25 Mar 2014 18:31:43 +0100 Subject: [PATCH 0703/2237] Introduce credentials --- pygit2/__init__.py | 2 +- pygit2/credentials.py | 52 +++++++++ src/credentials.c | 225 +++++++++++++++++++++++++++++++++++++++ src/pygit2.c | 9 ++ src/remote.c | 77 ++++++++++++++ src/types.h | 20 ++++ test/test_credentials.py | 98 +++++++++++++++++ 7 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 pygit2/credentials.py create mode 100644 src/credentials.c create mode 100644 test/test_credentials.py diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 8d40fc487..60e8942c3 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -36,7 +36,7 @@ from .repository import Repository from .version import __version__ from .settings import Settings - +from .credentials import * def init_repository(path, bare=False): """ diff --git a/pygit2/credentials.py b/pygit2/credentials.py new file mode 100644 index 000000000..a3a3c25c1 --- /dev/null +++ b/pygit2/credentials.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2010-2014 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. + +# Import from pygit2 +from _pygit2 import CredUsernamePassword, CredSshKey +from _pygit2 import GIT_CREDTYPE_USERPASS_PLAINTEXT + +class UserPass(CredUsernamePassword): + """Username/Password credentials + + This is an object suitable for passing to a remote's credentials + callback. + + """ + + def __call__(self, _url, _username, _allowed): + return self + +class Keypair(CredSshKey): + """SSH key pair credentials + + This is an object suitable for passing to a remote's credentials + callback. + + """ + + def __call__(self, _url, _username, _allowed): + return self diff --git a/src/credentials.c b/src/credentials.c new file mode 100644 index 000000000..5711f044b --- /dev/null +++ b/src/credentials.c @@ -0,0 +1,225 @@ +/* + * Copyright 2010-2014 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 "error.h" +#include "types.h" +#include "utils.h" +#include "oid.h" +#include "refspec.h" +#include "remote.h" + +int +CredUsernamePassword_init(CredUsernamePassword *self, PyObject *args, PyObject *kwds) +{ + char *username, *password; + + if (kwds && PyDict_Size(kwds) > 0) { + PyErr_SetString(PyExc_TypeError, "CredUsernamePassword takes no keyword arguments"); + return -1; + } + + if (!PyArg_ParseTuple(args, "ss", &username, &password)) + return -1; + + self->parent.credtype = GIT_CREDTYPE_USERPASS_PLAINTEXT; + + self->username = strdup(username); + if (!self->username) { + PyErr_NoMemory(); + return -1; + } + + self->password = strdup(password); + if (!self->password) { + free(self->username); + PyErr_NoMemory(); + return -1; + } + + return 0; +} + +void +CredUsernamePassword_dealloc(CredUsernamePassword *self) +{ + free(self->username); + free(self->password); + + PyObject_Del(self); +} + +PyMemberDef CredUsernamePassword_members[] = { + MEMBER(CredUsernamePassword, username, T_STRING, "username"), + MEMBER(CredUsernamePassword, password, T_STRING, "password"), + {NULL}, +}; + +PyDoc_STRVAR(CredUsernamePassword__doc__, + "Credential type for username/password combination"); + +PyTypeObject CredUsernamePasswordType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.CredUsernamePassword", /* tp_name */ + sizeof(CredUsernamePassword), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)CredUsernamePassword_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + CredUsernamePassword__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + CredUsernamePassword_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)CredUsernamePassword_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +int +CredSshKey_init(CredSshKey *self, PyObject *args, PyObject *kwds) +{ + char *username, *pubkey, *privkey, *passphrase; + + if (kwds && PyDict_Size(kwds) > 0) { + PyErr_SetString(PyExc_TypeError, "CredSshKey takes no keyword arguments"); + return -1; + } + + if (!PyArg_ParseTuple(args, "ssss", &username, &pubkey, + &privkey, &passphrase)) + return -1; + + self->parent.credtype = GIT_CREDTYPE_SSH_KEY; + self->username = self->pubkey = self->privkey = self->passphrase = NULL; + + self->username = strdup(username); + self->pubkey = strdup(pubkey); + self->privkey = strdup(privkey); + self->passphrase = strdup(passphrase); + + if (!(self->username && self->pubkey && self->privkey && self->passphrase)) + goto on_oom; + + return 0; + + on_oom: + free(self->username); + free(self->pubkey); + free(self->privkey); + free(self->passphrase); + PyErr_NoMemory(); + return -1; +} + +void +CredSshKey_dealloc(CredSshKey *self) +{ + free(self->username); + free(self->pubkey); + free(self->privkey); + free(self->passphrase); + + PyObject_Del(self); +} + +PyMemberDef CredSshKey_members[] = { + MEMBER(CredSshKey, username, T_STRING, "username"), + MEMBER(CredSshKey, pubkey, T_STRING, "pubkey"), + MEMBER(CredSshKey, privkey, T_STRING, "privkey"), + MEMBER(CredSshKey, passphrase, T_STRING, "passphrase"), + {NULL}, +}; + +PyDoc_STRVAR(CredSshKey__doc__, + "Credential type for an SSH keypair"); + +PyTypeObject CredSshKeyType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.CredSshKey", /* tp_name */ + sizeof(CredSshKey), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)CredSshKey_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + CredSshKey__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + CredSshKey_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)CredSshKey_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; diff --git a/src/pygit2.c b/src/pygit2.c index 9b6319e6d..ea6435435 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -74,6 +74,8 @@ extern PyTypeObject RefLogEntryType; extern PyTypeObject BranchType; extern PyTypeObject SignatureType; extern PyTypeObject RemoteType; +extern PyTypeObject CredUsernamePasswordType; +extern PyTypeObject CredSshKeyType; extern PyTypeObject RefspecType; extern PyTypeObject TransferProgressType; extern PyTypeObject NoteType; @@ -444,14 +446,21 @@ moduleinit(PyObject* m) /* Remotes */ INIT_TYPE(RemoteType, NULL, NULL) + INIT_TYPE(CredUsernamePasswordType, NULL, PyType_GenericNew) + INIT_TYPE(CredSshKeyType, NULL, PyType_GenericNew) INIT_TYPE(RefspecType, NULL, NULL) INIT_TYPE(TransferProgressType, NULL, NULL) ADD_TYPE(m, Remote) + ADD_TYPE(m, CredUsernamePassword) + ADD_TYPE(m, CredSshKey) ADD_TYPE(m, Refspec) ADD_TYPE(m, TransferProgress) /* Direction for the refspec */ ADD_CONSTANT_INT(m, GIT_DIRECTION_FETCH) ADD_CONSTANT_INT(m, GIT_DIRECTION_PUSH) + /* Credential types */ + ADD_CONSTANT_INT(m, GIT_CREDTYPE_USERPASS_PLAINTEXT) + ADD_CONSTANT_INT(m, GIT_CREDTYPE_SSH_KEY) /* Blame */ INIT_TYPE(BlameType, NULL, NULL) diff --git a/src/remote.c b/src/remote.c index c73878423..9c7cce275 100644 --- a/src/remote.c +++ b/src/remote.c @@ -39,6 +39,8 @@ extern PyObject *GitError; extern PyTypeObject RepositoryType; extern PyTypeObject TransferProgressType; +extern PyTypeObject CredUsernamePasswordType; +extern PyTypeObject CredSshKeyType; PyObject * wrap_transfer_progress(const git_transfer_progress *stats) @@ -156,6 +158,78 @@ progress_cb(const char *str, int len, void *data) return 0; } +static int +py_cred_to_git_cred(git_cred **out, PyObject *py_cred, unsigned int allowed) +{ + Cred *base_cred; + int err; + + if (!PyObject_TypeCheck(py_cred, &CredUsernamePasswordType) && + !PyObject_TypeCheck(py_cred, &CredSshKeyType)) { + PyErr_SetString(PyExc_TypeError, "unkown credential type"); + return -1; + } + + base_cred = (Cred *) py_cred; + + /* Sanity check, make sure we're given credentials we can use */ + if (!(allowed & base_cred->credtype)) { + PyErr_SetString(PyExc_TypeError, "invalid credential type"); + return -1; + } + + switch (base_cred->credtype) { + case GIT_CREDTYPE_USERPASS_PLAINTEXT: + { + CredUsernamePassword *cred = (CredUsernamePassword *) base_cred; + err = git_cred_userpass_plaintext_new(out, cred->username, cred->password); + break; + } + case GIT_CREDTYPE_SSH_KEY: + { + CredSshKey *cred = (CredSshKey *) base_cred; + err = git_cred_ssh_key_new(out, cred->username, cred->pubkey, cred->privkey, cred->passphrase); + break; + } + default: + PyErr_SetString(PyExc_TypeError, "unsupported credential type"); + err = -1; + break; + } + + return err; +} + +static int +credentials_cb(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *data) +{ + Remote *remote = (Remote *) data; + PyObject *arglist, *py_cred; + int err; + + if (remote->credentials == NULL) + return 0; + + if (!PyCallable_Check(remote->credentials)) { + PyErr_SetString(PyExc_TypeError, "credentials callback is not callable"); + return -1; + } + + arglist = Py_BuildValue("(szI)", url, username_from_url, allowed_types); + py_cred = PyObject_CallObject(remote->credentials, arglist); + Py_DECREF(arglist); + + if (!py_cred) + return -1; + + err = py_cred_to_git_cred(out, py_cred, allowed_types); + + + Py_DECREF(py_cred); + + return err; +} + static int transfer_progress_cb(const git_transfer_progress *stats, void *data) { @@ -631,6 +705,7 @@ PyGetSetDef Remote_getseters[] = { PyMemberDef Remote_members[] = { MEMBER(Remote, progress, T_OBJECT_EX, "Progress output callback"), + MEMBER(Remote, credentials, T_OBJECT_EX, "Credentials callback"), MEMBER(Remote, transfer_progress, T_OBJECT_EX, "Transfer progress callback"), MEMBER(Remote, update_tips, T_OBJECT_EX, "update tips callback"), {NULL}, @@ -691,10 +766,12 @@ wrap_remote(git_remote *c_remote, Repository *repo) py_remote->repo = repo; py_remote->remote = c_remote; py_remote->progress = NULL; + py_remote->credentials = NULL; py_remote->transfer_progress = NULL; py_remote->update_tips = NULL; callbacks.progress = progress_cb; + callbacks.credentials = credentials_cb; callbacks.transfer_progress = transfer_progress_cb; callbacks.update_tips = update_tips_cb; callbacks.payload = py_remote; diff --git a/src/types.h b/src/types.h index e6a318994..1a73fc5ea 100644 --- a/src/types.h +++ b/src/types.h @@ -202,10 +202,30 @@ typedef struct { git_remote *remote; /* Callbacks for network events */ PyObject *progress; + PyObject *credentials; PyObject *transfer_progress; PyObject *update_tips; } Remote; +typedef struct { + PyObject_HEAD + git_credtype_t credtype; +} Cred; + +typedef struct { + Cred parent; + char *username; + char *password; +} CredUsernamePassword; + +typedef struct { + Cred parent; + char *username; + char *pubkey; + char *privkey; + char *passphrase; +} CredSshKey; + /* git_refspec */ typedef struct { PyObject_HEAD diff --git a/test/test_credentials.py b/test/test_credentials.py new file mode 100644 index 000000000..c1b95e548 --- /dev/null +++ b/test/test_credentials.py @@ -0,0 +1,98 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2010-2014 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. + +"""Tests for credentials""" + + +import unittest +import pygit2 +from pygit2 import CredUsernamePassword, CredSshKey +from pygit2 import GIT_CREDTYPE_USERPASS_PLAINTEXT +from pygit2 import UserPass, Keypair +from . import utils + +REMOTE_NAME = 'origin' +REMOTE_URL = 'git://github.com/libgit2/pygit2.git' +REMOTE_FETCHSPEC_SRC = 'refs/heads/*' +REMOTE_FETCHSPEC_DST = 'refs/remotes/origin/*' +REMOTE_REPO_OBJECTS = 30 +REMOTE_REPO_BYTES = 2758 + +ORIGIN_REFSPEC = '+refs/heads/*:refs/remotes/origin/*' + +class CredentialCreateTest(utils.NoRepoTestCase): + def test_userpass(self): + username = "git" + password = "sekkrit" + + cred = CredUsernamePassword(username, password) + self.assertEqual(username, cred.username) + self.assertEqual(password, cred.password) + + def test_ssh_key(self): + username = "git" + pubkey = "id_rsa.pub" + privkey = "id_rsa" + passphrase = "bad wolf" + + cred = CredSshKey(username, pubkey, privkey, passphrase) + self.assertEqual(username, cred.username) + self.assertEqual(pubkey, cred.pubkey) + self.assertEqual(privkey, cred.privkey) + self.assertEqual(passphrase, cred.passphrase) + +class CredentialCallback(utils.RepoTestCase): + def test_callback(self): + def credentials_cb(url, username, allowed): + self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT) + raise Exception("I don't know the password") + + remote = self.repo.create_remote("github", "https://github.com/github/github") + remote.credentials = credentials_cb + + self.assertRaises(Exception, remote.fetch) + + def test_bad_cred_type(self): + def credentials_cb(url, username, allowed): + self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT) + return CredSshKey("git", "foo.pub", "foo", "sekkrit") + + remote = self.repo.create_remote("github", "https://github.com/github/github") + remote.credentials = credentials_cb + + self.assertRaises(TypeError, remote.fetch) + +class CallableCredentialTest(utils.RepoTestCase): + + def test_user_pass(self): + remote = self.repo.create_remote("bb", "https://bitbucket.org/libgit2/testgitrepository.git") + remote.credentials = UserPass("libgit2", "libgit2") + + remote.fetch() + +if __name__ == '__main__': + unittest.main() From 4c3c706cb9e4ae2cb8934a876c85f70cff20b431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 26 Mar 2014 12:37:28 +0100 Subject: [PATCH 0704/2237] Repository: don't jump to cleanup unecessarily Until we have successfully borrowed the message, we have not used up any resources, we can skip jumping to 'out' and we can return NULL directly. This also avoid dereferencing an uninitialised 'tmessage'. --- src/repository.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/repository.c b/src/repository.c index 1e654e28b..031e6e0ec 100644 --- a/src/repository.c +++ b/src/repository.c @@ -807,11 +807,11 @@ Repository_create_commit(Repository *self, PyObject *args) len = py_oid_to_git_oid(py_oid, &oid); if (len == 0) - goto out; + return NULL; message = py_str_borrow_c_str(&tmessage, py_message, encoding); if (message == NULL) - goto out; + return NULL; err = git_tree_lookup_prefix(&tree, self->repo, &oid, len); if (err < 0) { From fc0cdaebd6a4ecc92cc3ca59db755a394bde145d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 26 Mar 2014 14:03:29 +0100 Subject: [PATCH 0705/2237] Remote: add documentation for credentials --- docs/remotes.rst | 16 ++++++++++++++++ pygit2/credentials.py | 4 ++-- src/remote.c | 13 ++++++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/docs/remotes.rst b/docs/remotes.rst index f77bd7393..58ab8ef5d 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -52,3 +52,19 @@ The Refspec type .. automethod:: pygit2.Refspec.dst_matches .. automethod:: pygit2.Refspec.transform .. automethod:: pygit2.Refspec.rtransform + +Credentials +================ + +.. automethod:: pygit2.Remote.credentials + +There are two types of credentials: username/password and SSH key +pairs. Both :py:class:`pygit2.UserPass` and :py:class:`pygit2.Keypair` +are callable objects, with the appropriate signature for the +credentials callback. They will ignore all the arguments and return +themselves. This is useful for scripts where the credentials are known +ahead of time. More complete interfaces would want to look up in their +keychain or ask the user for the data to use in the credentials. + +.. autoclass:: pygit2.UserPass +.. autoclass:: pygit2.Keypair diff --git a/pygit2/credentials.py b/pygit2/credentials.py index a3a3c25c1..dad966b3e 100644 --- a/pygit2/credentials.py +++ b/pygit2/credentials.py @@ -33,7 +33,7 @@ class UserPass(CredUsernamePassword): """Username/Password credentials This is an object suitable for passing to a remote's credentials - callback. + callback and for returning from said callback. """ @@ -44,7 +44,7 @@ class Keypair(CredSshKey): """SSH key pair credentials This is an object suitable for passing to a remote's credentials - callback. + callback and for returning from said callback. """ diff --git a/src/remote.c b/src/remote.c index 9c7cce275..65a195ef6 100644 --- a/src/remote.c +++ b/src/remote.c @@ -705,7 +705,18 @@ PyGetSetDef Remote_getseters[] = { PyMemberDef Remote_members[] = { MEMBER(Remote, progress, T_OBJECT_EX, "Progress output callback"), - MEMBER(Remote, credentials, T_OBJECT_EX, "Credentials callback"), + MEMBER(Remote, credentials, T_OBJECT_EX, + "credentials(url, username_from_url, allowed_types) -> credential\n" + "\n" + "Credentials callback\n" + "\n" + "If the remote server requires authentication, this function will\n" + "be called and its return value used for authentication.\n" + "\n" + ":param str url: The url of the remote\n" + ":param username_from_url: Username extracted from the url, if any\n" + ":type username_from_url: str or None\n" + ":param int allowed_types: credential types supported by the remote "), MEMBER(Remote, transfer_progress, T_OBJECT_EX, "Transfer progress callback"), MEMBER(Remote, update_tips, T_OBJECT_EX, "update tips callback"), {NULL}, From 75f9f883352b1ad8cff069a538607adf79bd7fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 26 Mar 2014 14:19:38 +0100 Subject: [PATCH 0706/2237] Update clone_repository's docs It claims you need to checkout a branch after clone, which is not the case currently (the clone function will do it for you). While here, format the docstring for sphinx to make it pretty. --- pygit2/__init__.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 60e8942c3..f4f471ec8 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -52,24 +52,20 @@ def init_repository(path, bare=False): def clone_repository( url, path, bare=False, ignore_cert_errors=False, remote_name="origin", checkout_branch=None): - """ - Clones a new Git repository from *url* in the given *path*. + """Clones a new Git repository from *url* in the given *path*. - **bare** indicates whether a bare git repository should be created. + Returns a Repository class pointing to the newly cloned repository. - **remote_name** is the name given to the "origin" remote. - The default is "origin". + :param str url: URL of the repository to clone - **checkout_branch** gives the name of the branch to checkout. - None means use the remote's *HEAD*. + :param str path: Local path to clone into - Returns a Repository class pointing to the newly cloned repository. + :param bool bare: Whether the local repository should be bare - If you wish to use the repo, you need to do a checkout for one of - the available branches, like this: + :param str remote_name: Name to give the remote at *url*. - >>> repo = repo.clone_repository("url", "path") - >>> repo.checkout(branch) # i.e.: refs/heads/master + :param str checkout_branch: Branch to checkout after the + clone. The default is to use the remote's default branch. """ From b3ce1b5da6200a027a38d70a845cb79ab1ff9787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 26 Mar 2014 14:50:25 +0100 Subject: [PATCH 0707/2237] Add credentials support to clone_repository() --- pygit2/__init__.py | 9 ++++-- src/pygit2.c | 16 +++++++++-- src/remote.c | 66 +----------------------------------------- src/utils.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++ src/utils.h | 2 ++ 5 files changed, 95 insertions(+), 69 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index f4f471ec8..fc2e69366 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -51,7 +51,7 @@ def init_repository(path, bare=False): def clone_repository( url, path, bare=False, ignore_cert_errors=False, - remote_name="origin", checkout_branch=None): + remote_name="origin", checkout_branch=None, credentials=None): """Clones a new Git repository from *url* in the given *path*. Returns a Repository class pointing to the newly cloned repository. @@ -67,10 +67,15 @@ def clone_repository( :param str checkout_branch: Branch to checkout after the clone. The default is to use the remote's default branch. + :param callable credentials: authentication to use if the remote + requires it + + :rtype: Repository + """ _pygit2.clone_repository( - url, path, bare, ignore_cert_errors, remote_name, checkout_branch) + url, path, bare, ignore_cert_errors, remote_name, checkout_branch, credentials) return Repository(path) settings = Settings() diff --git a/src/pygit2.c b/src/pygit2.c index ea6435435..0126dc8d9 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -118,6 +118,14 @@ init_repository(PyObject *self, PyObject *args) { Py_RETURN_NONE; }; +static int +credentials_cb(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *data) +{ + PyObject *credentials = (PyObject *) data; + + return callable_to_credentials(out, url, username_from_url, allowed_types, credentials); +} + PyDoc_STRVAR(clone_repository__doc__, "clone_repository(url, path, bare, remote_name, checkout_branch)\n" "\n" @@ -146,11 +154,12 @@ clone_repository(PyObject *self, PyObject *args) { const char *path; unsigned int bare, ignore_cert_errors; const char *remote_name, *checkout_branch; + PyObject *credentials; int err; git_clone_options opts = GIT_CLONE_OPTIONS_INIT; - if (!PyArg_ParseTuple(args, "zzIIzz", - &url, &path, &bare, &ignore_cert_errors, &remote_name, &checkout_branch)) + if (!PyArg_ParseTuple(args, "zzIIzzO", + &url, &path, &bare, &ignore_cert_errors, &remote_name, &checkout_branch, &credentials)) return NULL; opts.bare = bare; @@ -158,6 +167,9 @@ clone_repository(PyObject *self, PyObject *args) { opts.remote_name = remote_name; opts.checkout_branch = checkout_branch; + opts.remote_callbacks.credentials = credentials_cb; + opts.remote_callbacks.payload = credentials; + err = git_clone(&repo, url, path, &opts); if (err < 0) return Error_set(err); diff --git a/src/remote.c b/src/remote.c index 65a195ef6..b654a8cbf 100644 --- a/src/remote.c +++ b/src/remote.c @@ -158,76 +158,12 @@ progress_cb(const char *str, int len, void *data) return 0; } -static int -py_cred_to_git_cred(git_cred **out, PyObject *py_cred, unsigned int allowed) -{ - Cred *base_cred; - int err; - - if (!PyObject_TypeCheck(py_cred, &CredUsernamePasswordType) && - !PyObject_TypeCheck(py_cred, &CredSshKeyType)) { - PyErr_SetString(PyExc_TypeError, "unkown credential type"); - return -1; - } - - base_cred = (Cred *) py_cred; - - /* Sanity check, make sure we're given credentials we can use */ - if (!(allowed & base_cred->credtype)) { - PyErr_SetString(PyExc_TypeError, "invalid credential type"); - return -1; - } - - switch (base_cred->credtype) { - case GIT_CREDTYPE_USERPASS_PLAINTEXT: - { - CredUsernamePassword *cred = (CredUsernamePassword *) base_cred; - err = git_cred_userpass_plaintext_new(out, cred->username, cred->password); - break; - } - case GIT_CREDTYPE_SSH_KEY: - { - CredSshKey *cred = (CredSshKey *) base_cred; - err = git_cred_ssh_key_new(out, cred->username, cred->pubkey, cred->privkey, cred->passphrase); - break; - } - default: - PyErr_SetString(PyExc_TypeError, "unsupported credential type"); - err = -1; - break; - } - - return err; -} - static int credentials_cb(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *data) { Remote *remote = (Remote *) data; - PyObject *arglist, *py_cred; - int err; - - if (remote->credentials == NULL) - return 0; - - if (!PyCallable_Check(remote->credentials)) { - PyErr_SetString(PyExc_TypeError, "credentials callback is not callable"); - return -1; - } - - arglist = Py_BuildValue("(szI)", url, username_from_url, allowed_types); - py_cred = PyObject_CallObject(remote->credentials, arglist); - Py_DECREF(arglist); - - if (!py_cred) - return -1; - - err = py_cred_to_git_cred(out, py_cred, allowed_types); - - - Py_DECREF(py_cred); - return err; + return callable_to_credentials(out, url, username_from_url, allowed_types, remote->credentials); } static int diff --git a/src/utils.c b/src/utils.c index 44acf5c6a..66e546d09 100644 --- a/src/utils.c +++ b/src/utils.c @@ -31,6 +31,8 @@ #include "utils.h" extern PyTypeObject ReferenceType; +extern PyTypeObject CredUsernamePasswordType; +extern PyTypeObject CredSshKeyType; /** * py_str_to_c_str() returns a newly allocated C string holding the string @@ -153,3 +155,72 @@ get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) return -1; } + +static int +py_cred_to_git_cred(git_cred **out, PyObject *py_cred, unsigned int allowed) +{ + Cred *base_cred; + int err; + + if (!PyObject_TypeCheck(py_cred, &CredUsernamePasswordType) && + !PyObject_TypeCheck(py_cred, &CredSshKeyType)) { + PyErr_SetString(PyExc_TypeError, "unkown credential type"); + return -1; + } + + base_cred = (Cred *) py_cred; + + /* Sanity check, make sure we're given credentials we can use */ + if (!(allowed & base_cred->credtype)) { + PyErr_SetString(PyExc_TypeError, "invalid credential type"); + return -1; + } + + switch (base_cred->credtype) { + case GIT_CREDTYPE_USERPASS_PLAINTEXT: + { + CredUsernamePassword *cred = (CredUsernamePassword *) base_cred; + err = git_cred_userpass_plaintext_new(out, cred->username, cred->password); + break; + } + case GIT_CREDTYPE_SSH_KEY: + { + CredSshKey *cred = (CredSshKey *) base_cred; + err = git_cred_ssh_key_new(out, cred->username, cred->pubkey, cred->privkey, cred->passphrase); + break; + } + default: + PyErr_SetString(PyExc_TypeError, "unsupported credential type"); + err = -1; + break; + } + + return err; +} + +int +callable_to_credentials(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, PyObject *credentials) +{ + int err; + PyObject *py_cred, *arglist; + + if (credentials == NULL) + return 0; + + if (!PyCallable_Check(credentials)) { + PyErr_SetString(PyExc_TypeError, "credentials callback is not callable"); + return -1; + } + + arglist = Py_BuildValue("(szI)", url, username_from_url, allowed_types); + py_cred = PyObject_CallObject(credentials, arglist); + Py_DECREF(arglist); + + if (!py_cred) + return -1; + + err = py_cred_to_git_cred(out, py_cred, allowed_types); + Py_DECREF(py_cred); + + return err; +} diff --git a/src/utils.h b/src/utils.h index 81744f686..7f95d73ee 100644 --- a/src/utils.h +++ b/src/utils.h @@ -117,6 +117,8 @@ const char *py_str_borrow_c_str(PyObject **tvaue, PyObject *value, const char *e PyObject * get_pylist_from_git_strarray(git_strarray *strarray); int get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist); +int callable_to_credentials(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, PyObject *credentials); + #define py_path_to_c_str(py_path) \ py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding) From 77acb11cd0bb5d9c2be8fc046e71296b501de874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 26 Mar 2014 19:14:57 +0100 Subject: [PATCH 0708/2237] credentials: memory safety The docs say to use tp_free() to free the memory, and even though we use PyObject_Del() everywhere else, using this in the credentials does cause issues. --- src/credentials.c | 4 ++-- src/pygit2.c | 8 +++++--- src/utils.c | 4 ++-- test/test_repository.py | 7 +++++++ 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/credentials.c b/src/credentials.c index 5711f044b..c3127ae1d 100644 --- a/src/credentials.c +++ b/src/credentials.c @@ -72,7 +72,7 @@ CredUsernamePassword_dealloc(CredUsernamePassword *self) free(self->username); free(self->password); - PyObject_Del(self); + Py_TYPE(self)->tp_free(self); } PyMemberDef CredUsernamePassword_members[] = { @@ -169,7 +169,7 @@ CredSshKey_dealloc(CredSshKey *self) free(self->privkey); free(self->passphrase); - PyObject_Del(self); + Py_TYPE(self)->tp_free(self); } PyMemberDef CredSshKey_members[] = { diff --git a/src/pygit2.c b/src/pygit2.c index 0126dc8d9..199bbac11 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -154,7 +154,7 @@ clone_repository(PyObject *self, PyObject *args) { const char *path; unsigned int bare, ignore_cert_errors; const char *remote_name, *checkout_branch; - PyObject *credentials; + PyObject *credentials = NULL; int err; git_clone_options opts = GIT_CLONE_OPTIONS_INIT; @@ -167,8 +167,10 @@ clone_repository(PyObject *self, PyObject *args) { opts.remote_name = remote_name; opts.checkout_branch = checkout_branch; - opts.remote_callbacks.credentials = credentials_cb; - opts.remote_callbacks.payload = credentials; + if (credentials != Py_None) { + opts.remote_callbacks.credentials = credentials_cb; + opts.remote_callbacks.payload = credentials; + } err = git_clone(&repo, url, path, &opts); if (err < 0) diff --git a/src/utils.c b/src/utils.c index 66e546d09..59e7ad5f7 100644 --- a/src/utils.c +++ b/src/utils.c @@ -202,9 +202,9 @@ int callable_to_credentials(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, PyObject *credentials) { int err; - PyObject *py_cred, *arglist; + PyObject *py_cred = NULL, *arglist = NULL; - if (credentials == NULL) + if (credentials == NULL || credentials == Py_None) return 0; if (!PyCallable_Check(credentials)) { diff --git a/test/test_repository.py b/test/test_repository.py index 8e8101c66..71214ccef 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -461,6 +461,13 @@ def test_clone_remote_name(self): self.assertFalse(repo.is_empty) self.assertEqual(repo.remotes[0].name, "custom_remote") + def test_clone_with_credentials(self): + credentials = pygit2.UserPass("libgit2", "libgit2") + repo = clone_repository( + "https://bitbucket.org/libgit2/testgitrepository.git", + self._temp_dir, credentials=credentials) + + self.assertFalse(repo.is_empty) # FIXME The tests below are commented because they are broken: # From 82d88191bb450a153a217ed8227dd1865c48016d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 26 Mar 2014 20:23:04 +0100 Subject: [PATCH 0709/2237] credentials: use more ducks Instead of making everyone inherit from our credential types, use an interface with two attributes, which makes the C code much shorter and simpler. --- pygit2/credentials.py | 33 +++++- src/credentials.c | 225 --------------------------------------- src/pygit2.c | 6 -- src/remote.c | 2 - src/types.h | 19 ---- src/utils.c | 52 ++++++--- test/test_credentials.py | 15 +-- 7 files changed, 69 insertions(+), 283 deletions(-) delete mode 100644 src/credentials.c diff --git a/pygit2/credentials.py b/pygit2/credentials.py index dad966b3e..1789022d6 100644 --- a/pygit2/credentials.py +++ b/pygit2/credentials.py @@ -26,10 +26,9 @@ # Boston, MA 02110-1301, USA. # Import from pygit2 -from _pygit2 import CredUsernamePassword, CredSshKey -from _pygit2 import GIT_CREDTYPE_USERPASS_PLAINTEXT +from _pygit2 import GIT_CREDTYPE_USERPASS_PLAINTEXT, GIT_CREDTYPE_SSH_KEY -class UserPass(CredUsernamePassword): +class UserPass: """Username/Password credentials This is an object suitable for passing to a remote's credentials @@ -37,10 +36,22 @@ class UserPass(CredUsernamePassword): """ + def __init__(self, username, password): + self._username = username + self._password = password + + @property + def credential_type(self): + return GIT_CREDTYPE_USERPASS_PLAINTEXT + + @property + def credential_tuple(self): + return (self._username, self._password) + def __call__(self, _url, _username, _allowed): return self -class Keypair(CredSshKey): +class Keypair: """SSH key pair credentials This is an object suitable for passing to a remote's credentials @@ -48,5 +59,19 @@ class Keypair(CredSshKey): """ + def __init__(self, username, pubkey, privkey, passphrase): + self._username = username + self._pubkey = pubkey + self._privkey = privkey + self._passphrase = passphrase + + @property + def credential_type(self): + return GIT_CREDTYPE_SSH_KEY + + @property + def credential_tuple(self): + return (self._username, self._pubkey, self._privkey, self._passphrase) + def __call__(self, _url, _username, _allowed): return self diff --git a/src/credentials.c b/src/credentials.c deleted file mode 100644 index c3127ae1d..000000000 --- a/src/credentials.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright 2010-2014 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 "error.h" -#include "types.h" -#include "utils.h" -#include "oid.h" -#include "refspec.h" -#include "remote.h" - -int -CredUsernamePassword_init(CredUsernamePassword *self, PyObject *args, PyObject *kwds) -{ - char *username, *password; - - if (kwds && PyDict_Size(kwds) > 0) { - PyErr_SetString(PyExc_TypeError, "CredUsernamePassword takes no keyword arguments"); - return -1; - } - - if (!PyArg_ParseTuple(args, "ss", &username, &password)) - return -1; - - self->parent.credtype = GIT_CREDTYPE_USERPASS_PLAINTEXT; - - self->username = strdup(username); - if (!self->username) { - PyErr_NoMemory(); - return -1; - } - - self->password = strdup(password); - if (!self->password) { - free(self->username); - PyErr_NoMemory(); - return -1; - } - - return 0; -} - -void -CredUsernamePassword_dealloc(CredUsernamePassword *self) -{ - free(self->username); - free(self->password); - - Py_TYPE(self)->tp_free(self); -} - -PyMemberDef CredUsernamePassword_members[] = { - MEMBER(CredUsernamePassword, username, T_STRING, "username"), - MEMBER(CredUsernamePassword, password, T_STRING, "password"), - {NULL}, -}; - -PyDoc_STRVAR(CredUsernamePassword__doc__, - "Credential type for username/password combination"); - -PyTypeObject CredUsernamePasswordType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.CredUsernamePassword", /* tp_name */ - sizeof(CredUsernamePassword), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)CredUsernamePassword_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - CredUsernamePassword__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - CredUsernamePassword_members, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)CredUsernamePassword_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - -int -CredSshKey_init(CredSshKey *self, PyObject *args, PyObject *kwds) -{ - char *username, *pubkey, *privkey, *passphrase; - - if (kwds && PyDict_Size(kwds) > 0) { - PyErr_SetString(PyExc_TypeError, "CredSshKey takes no keyword arguments"); - return -1; - } - - if (!PyArg_ParseTuple(args, "ssss", &username, &pubkey, - &privkey, &passphrase)) - return -1; - - self->parent.credtype = GIT_CREDTYPE_SSH_KEY; - self->username = self->pubkey = self->privkey = self->passphrase = NULL; - - self->username = strdup(username); - self->pubkey = strdup(pubkey); - self->privkey = strdup(privkey); - self->passphrase = strdup(passphrase); - - if (!(self->username && self->pubkey && self->privkey && self->passphrase)) - goto on_oom; - - return 0; - - on_oom: - free(self->username); - free(self->pubkey); - free(self->privkey); - free(self->passphrase); - PyErr_NoMemory(); - return -1; -} - -void -CredSshKey_dealloc(CredSshKey *self) -{ - free(self->username); - free(self->pubkey); - free(self->privkey); - free(self->passphrase); - - Py_TYPE(self)->tp_free(self); -} - -PyMemberDef CredSshKey_members[] = { - MEMBER(CredSshKey, username, T_STRING, "username"), - MEMBER(CredSshKey, pubkey, T_STRING, "pubkey"), - MEMBER(CredSshKey, privkey, T_STRING, "privkey"), - MEMBER(CredSshKey, passphrase, T_STRING, "passphrase"), - {NULL}, -}; - -PyDoc_STRVAR(CredSshKey__doc__, - "Credential type for an SSH keypair"); - -PyTypeObject CredSshKeyType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.CredSshKey", /* tp_name */ - sizeof(CredSshKey), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)CredSshKey_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - CredSshKey__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - CredSshKey_members, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)CredSshKey_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; diff --git a/src/pygit2.c b/src/pygit2.c index 199bbac11..bda9545ec 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -74,8 +74,6 @@ extern PyTypeObject RefLogEntryType; extern PyTypeObject BranchType; extern PyTypeObject SignatureType; extern PyTypeObject RemoteType; -extern PyTypeObject CredUsernamePasswordType; -extern PyTypeObject CredSshKeyType; extern PyTypeObject RefspecType; extern PyTypeObject TransferProgressType; extern PyTypeObject NoteType; @@ -460,13 +458,9 @@ moduleinit(PyObject* m) /* Remotes */ INIT_TYPE(RemoteType, NULL, NULL) - INIT_TYPE(CredUsernamePasswordType, NULL, PyType_GenericNew) - INIT_TYPE(CredSshKeyType, NULL, PyType_GenericNew) INIT_TYPE(RefspecType, NULL, NULL) INIT_TYPE(TransferProgressType, NULL, NULL) ADD_TYPE(m, Remote) - ADD_TYPE(m, CredUsernamePassword) - ADD_TYPE(m, CredSshKey) ADD_TYPE(m, Refspec) ADD_TYPE(m, TransferProgress) /* Direction for the refspec */ diff --git a/src/remote.c b/src/remote.c index b654a8cbf..ffbe17d7b 100644 --- a/src/remote.c +++ b/src/remote.c @@ -39,8 +39,6 @@ extern PyObject *GitError; extern PyTypeObject RepositoryType; extern PyTypeObject TransferProgressType; -extern PyTypeObject CredUsernamePasswordType; -extern PyTypeObject CredSshKeyType; PyObject * wrap_transfer_progress(const git_transfer_progress *stats) diff --git a/src/types.h b/src/types.h index 1a73fc5ea..a36fad6b5 100644 --- a/src/types.h +++ b/src/types.h @@ -207,25 +207,6 @@ typedef struct { PyObject *update_tips; } Remote; -typedef struct { - PyObject_HEAD - git_credtype_t credtype; -} Cred; - -typedef struct { - Cred parent; - char *username; - char *password; -} CredUsernamePassword; - -typedef struct { - Cred parent; - char *username; - char *pubkey; - char *privkey; - char *passphrase; -} CredSshKey; - /* git_refspec */ typedef struct { PyObject_HEAD diff --git a/src/utils.c b/src/utils.c index 59e7ad5f7..24b6bbd47 100644 --- a/src/utils.c +++ b/src/utils.c @@ -31,8 +31,6 @@ #include "utils.h" extern PyTypeObject ReferenceType; -extern PyTypeObject CredUsernamePasswordType; -extern PyTypeObject CredSshKeyType; /** * py_str_to_c_str() returns a newly allocated C string holding the string @@ -159,42 +157,62 @@ get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) static int py_cred_to_git_cred(git_cred **out, PyObject *py_cred, unsigned int allowed) { - Cred *base_cred; - int err; + PyObject *py_type, *py_tuple; + long type; + int err = -1; - if (!PyObject_TypeCheck(py_cred, &CredUsernamePasswordType) && - !PyObject_TypeCheck(py_cred, &CredSshKeyType)) { - PyErr_SetString(PyExc_TypeError, "unkown credential type"); - return -1; + py_type = PyObject_GetAttrString(py_cred, "credential_type"); + py_tuple = PyObject_GetAttrString(py_cred, "credential_tuple"); + + if (!py_type || !py_tuple) { + printf("py_type %p, py_tuple %p\n", py_type, py_tuple); + PyErr_SetString(PyExc_TypeError, "credential doesn't implement the interface"); + goto cleanup; } - base_cred = (Cred *) py_cred; + if (!PyLong_Check(py_type)) { + PyErr_SetString(PyExc_TypeError, "credential type is not a long"); + goto cleanup; + } + + type = PyLong_AsLong(py_type); /* Sanity check, make sure we're given credentials we can use */ - if (!(allowed & base_cred->credtype)) { + if (!(allowed & type)) { PyErr_SetString(PyExc_TypeError, "invalid credential type"); - return -1; + goto cleanup; } - switch (base_cred->credtype) { + switch (type) { case GIT_CREDTYPE_USERPASS_PLAINTEXT: { - CredUsernamePassword *cred = (CredUsernamePassword *) base_cred; - err = git_cred_userpass_plaintext_new(out, cred->username, cred->password); + const char *username, *password; + + if (!PyArg_ParseTuple(py_tuple, "ss", &username, &password)) + goto cleanup; + + err = git_cred_userpass_plaintext_new(out, username, password); break; } case GIT_CREDTYPE_SSH_KEY: { - CredSshKey *cred = (CredSshKey *) base_cred; - err = git_cred_ssh_key_new(out, cred->username, cred->pubkey, cred->privkey, cred->passphrase); + const char *username, *pubkey, *privkey, *passphrase; + + if (!PyArg_ParseTuple(py_tuple, "ssss", &username, &pubkey, &privkey, &passphrase)) + goto cleanup; + + err = git_cred_ssh_key_new(out, username, pubkey, privkey, passphrase); break; } default: PyErr_SetString(PyExc_TypeError, "unsupported credential type"); - err = -1; break; } +cleanup: + Py_XDECREF(py_type); + Py_XDECREF(py_tuple); + return err; } diff --git a/test/test_credentials.py b/test/test_credentials.py index c1b95e548..3bdeb6fc4 100644 --- a/test/test_credentials.py +++ b/test/test_credentials.py @@ -30,7 +30,6 @@ import unittest import pygit2 -from pygit2 import CredUsernamePassword, CredSshKey from pygit2 import GIT_CREDTYPE_USERPASS_PLAINTEXT from pygit2 import UserPass, Keypair from . import utils @@ -49,9 +48,8 @@ def test_userpass(self): username = "git" password = "sekkrit" - cred = CredUsernamePassword(username, password) - self.assertEqual(username, cred.username) - self.assertEqual(password, cred.password) + cred = UserPass(username, password) + self.assertEqual((username, password), cred.credential_tuple) def test_ssh_key(self): username = "git" @@ -59,11 +57,8 @@ def test_ssh_key(self): privkey = "id_rsa" passphrase = "bad wolf" - cred = CredSshKey(username, pubkey, privkey, passphrase) - self.assertEqual(username, cred.username) - self.assertEqual(pubkey, cred.pubkey) - self.assertEqual(privkey, cred.privkey) - self.assertEqual(passphrase, cred.passphrase) + cred = Keypair(username, pubkey, privkey, passphrase) + self.assertEqual((username, pubkey, privkey, passphrase), cred.credential_tuple) class CredentialCallback(utils.RepoTestCase): def test_callback(self): @@ -79,7 +74,7 @@ def credentials_cb(url, username, allowed): def test_bad_cred_type(self): def credentials_cb(url, username, allowed): self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT) - return CredSshKey("git", "foo.pub", "foo", "sekkrit") + return Keypair("git", "foo.pub", "foo", "sekkrit") remote = self.repo.create_remote("github", "https://github.com/github/github") remote.credentials = credentials_cb From 940a6da92987acaba9954890be00e14cc71bbd4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 27 Mar 2014 09:45:10 +0100 Subject: [PATCH 0710/2237] Make dealloc use tp_free or don't allow inheritance When a class is a base type, it must use its type's tp_free function to trigger the real free, instead of PyObjec_Del(). We do not always follow this convention, so let's give it a once-over and make sure we do that or that it's not a base type. Many of the types have the flag set in the struct, but do not pass the allocator function at init time, which makes them not really be a base. Remove the flag for those types. --- TODO.txt | 4 ---- src/blame.c | 6 +++--- src/blob.c | 2 +- src/commit.c | 2 +- src/config.c | 2 +- src/diff.c | 4 ++-- src/index.c | 4 ++-- src/note.c | 4 ++-- src/object.c | 2 +- src/reference.c | 2 +- src/remote.c | 2 +- src/repository.c | 2 +- src/tag.c | 2 +- src/tree.c | 6 +++--- 14 files changed, 20 insertions(+), 24 deletions(-) diff --git a/TODO.txt b/TODO.txt index 4a48a284c..a648a06e8 100644 --- a/TODO.txt +++ b/TODO.txt @@ -14,7 +14,3 @@ Other - Make the Py_LOCAL_INLINE macro to work with Python 2.6, 2.7 and 3.1 - Use surrogateescape in Python 3, see PEP-383 - Expose the ODB (Repository.odb) -- According to Python documentation, tp_dealloc must call tp_free (instead of - PyObject_Del or similar) if the type is subclassable. So, go through the - code and switch to tp_free, or make the type not subclassable, on a case by - case basis. diff --git a/src/blame.c b/src/blame.c index f0ee18f2d..80b82089a 100644 --- a/src/blame.c +++ b/src/blame.c @@ -170,7 +170,7 @@ PyTypeObject BlameHunkType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ BlameHunk__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ @@ -233,7 +233,7 @@ PyTypeObject BlameIterType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ BlameIter__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ @@ -362,7 +362,7 @@ PyTypeObject BlameType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ Blame__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ diff --git a/src/blob.c b/src/blob.c index 94352dfda..133d49423 100644 --- a/src/blob.c +++ b/src/blob.c @@ -181,7 +181,7 @@ PyTypeObject BlobType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ Blob__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ diff --git a/src/commit.c b/src/commit.c index 8a61d29e7..85f59cdbf 100644 --- a/src/commit.c +++ b/src/commit.c @@ -259,7 +259,7 @@ PyTypeObject CommitType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ Commit__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ diff --git a/src/config.c b/src/config.c index 8cbb05451..9b2625d6a 100644 --- a/src/config.c +++ b/src/config.c @@ -92,7 +92,7 @@ void Config_dealloc(Config *self) { git_config_free(self->config); - PyObject_Del(self); + Py_TYPE(self)->tp_free(self); } PyDoc_STRVAR(Config_get_global_config__doc__, diff --git a/src/diff.c b/src/diff.c index b3a32ddc2..adea3cdd8 100644 --- a/src/diff.c +++ b/src/diff.c @@ -207,7 +207,7 @@ PyTypeObject PatchType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ Patch__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ @@ -269,7 +269,7 @@ PyTypeObject DiffIterType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ DiffIter__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ diff --git a/src/index.c b/src/index.c index cc1b365df..9fb587817 100644 --- a/src/index.c +++ b/src/index.c @@ -71,7 +71,7 @@ Index_dealloc(Index* self) PyObject_GC_UnTrack(self); Py_XDECREF(self->repo); git_index_free(self->index); - PyObject_GC_Del(self); + Py_TYPE(self)->tp_free(self); } int @@ -561,7 +561,7 @@ void IndexIter_dealloc(IndexIter *self) { Py_CLEAR(self->owner); - PyObject_Del(self); + Py_TYPE(self)->tp_free(self); } PyObject * diff --git a/src/note.c b/src/note.c index 2e5c9b149..e14e30862 100644 --- a/src/note.c +++ b/src/note.c @@ -134,7 +134,7 @@ PyTypeObject NoteType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ Note__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ @@ -200,7 +200,7 @@ PyTypeObject NoteIterType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ NoteIter__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ diff --git a/src/object.c b/src/object.c index 74c380129..f737e8bd2 100644 --- a/src/object.c +++ b/src/object.c @@ -45,7 +45,7 @@ Object_dealloc(Object* self) { Py_CLEAR(self->repo); git_object_free(self->obj); - PyObject_Del(self); + Py_TYPE(self)->tp_free(self); } diff --git a/src/reference.c b/src/reference.c index 2efbca3c5..e597e353a 100644 --- a/src/reference.c +++ b/src/reference.c @@ -97,7 +97,7 @@ PyTypeObject RefLogIterType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ RefLogIterType__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ diff --git a/src/remote.c b/src/remote.c index c73878423..ab085c8ba 100644 --- a/src/remote.c +++ b/src/remote.c @@ -658,7 +658,7 @@ PyTypeObject RemoteType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ Remote__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ diff --git a/src/repository.c b/src/repository.c index 031e6e0ec..d42264a9c 100644 --- a/src/repository.c +++ b/src/repository.c @@ -105,7 +105,7 @@ Repository_dealloc(Repository *self) Py_CLEAR(self->index); Py_CLEAR(self->config); git_repository_free(self->repo); - PyObject_GC_Del(self); + Py_TYPE(self)->tp_free(self); } int diff --git a/src/tag.c b/src/tag.c index 6224d4c80..d6507ae2f 100644 --- a/src/tag.c +++ b/src/tag.c @@ -151,7 +151,7 @@ PyTypeObject TagType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ Tag__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ diff --git a/src/tree.c b/src/tree.c index 8c83d3aa7..d1968faed 100644 --- a/src/tree.c +++ b/src/tree.c @@ -170,7 +170,7 @@ PyTypeObject TreeEntryType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ TreeEntry__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ @@ -518,7 +518,7 @@ PyTypeObject TreeType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ Tree__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ @@ -589,7 +589,7 @@ PyTypeObject TreeIterType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ TreeIter__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ From b37d1c9c1f055e5f08ad20f71d9ade9f25ac03f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 27 Mar 2014 13:03:42 +0100 Subject: [PATCH 0711/2237] Plug memory leaks This patch is mostly about making sure that we free the copies of what we have, as well as making sure that we can free it. The IndexEntry forgot to free its path, but it also used a pointer to python-owned memory, which could be freed at any time. MergeResult completely lacked a deallocator. Signature needs to make sure we can free the enocoding, and not to set an owner when we own the memory (in this case for the default signature). The repository needs to get rid of its reference to the object list when returning. The transfer progress callback needs to decref the stats object. --- src/index.c | 6 ++++-- src/mergeresult.c | 10 +++++++++- src/remote.c | 1 + src/repository.c | 8 ++++++-- src/signature.c | 30 +++++++++++++++++++++--------- src/types.h | 2 +- 6 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/index.c b/src/index.c index cc1b365df..bc72c5994 100644 --- a/src/index.c +++ b/src/index.c @@ -623,8 +623,9 @@ IndexEntry_init(IndexEntry *self, PyObject *args, PyObject *kwds) return -1; memset(&self->entry, 0, sizeof(struct git_index_entry)); - if (c_path) - self->entry.path = c_path; + self->entry.path = strdup(c_path); + if (!self->entry.path) + return -1; if (id) git_oid_cpy(&self->entry.oid, &id->oid); @@ -638,6 +639,7 @@ IndexEntry_init(IndexEntry *self, PyObject *args, PyObject *kwds) void IndexEntry_dealloc(IndexEntry *self) { + free(self->entry.path); PyObject_Del(self); } diff --git a/src/mergeresult.c b/src/mergeresult.c index ada872fb4..345e56ee6 100644 --- a/src/mergeresult.c +++ b/src/mergeresult.c @@ -50,6 +50,14 @@ git_merge_result_to_python(git_merge_result *merge_result) return (PyObject*) py_merge_result; } +void +MergeResult_dealloc(MergeResult *self) +{ + git_merge_result_free(self->result); + PyObject_Del(self); +} + + PyDoc_STRVAR(MergeResult_is_uptodate__doc__, "Is up to date"); PyObject * @@ -99,7 +107,7 @@ PyTypeObject MergeResultType = { "_pygit2.MergeResult", /* tp_name */ sizeof(MergeResult), /* tp_basicsize */ 0, /* tp_itemsize */ - 0, /* tp_dealloc */ + (destructor)MergeResult_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ diff --git a/src/remote.c b/src/remote.c index c73878423..379584ddd 100644 --- a/src/remote.c +++ b/src/remote.c @@ -175,6 +175,7 @@ transfer_progress_cb(const git_transfer_progress *stats, void *data) return -1; ret = PyObject_CallFunctionObjArgs(remote->transfer_progress, py_stats, NULL); + Py_DECREF(py_stats); if (!ret) return -1; diff --git a/src/repository.c b/src/repository.c index 031e6e0ec..b10dd5839 100644 --- a/src/repository.c +++ b/src/repository.c @@ -139,6 +139,7 @@ Repository_as_iter(Repository *self) git_odb *odb; int err; PyObject *accum = PyList_New(0); + PyObject *ret; err = git_repository_odb(&odb, self->repo); if (err < 0) @@ -151,7 +152,10 @@ Repository_as_iter(Repository *self) if (err < 0) return Error_set(err); - return PyObject_GetIter(accum); + ret = PyObject_GetIter(accum); + Py_DECREF(accum); + + return ret; } @@ -1345,7 +1349,7 @@ Repository_default_signature__get__(Repository *self) if ((err = git_signature_default(&sig, self->repo)) < 0) return Error_set(err); - return build_signature((Object*) self, sig, "utf-8"); + return build_signature(NULL, sig, "utf-8"); } PyDoc_STRVAR(Repository_checkout_head__doc__, diff --git a/src/signature.c b/src/signature.c index 809e80285..727178d9e 100644 --- a/src/signature.c +++ b/src/signature.c @@ -83,11 +83,12 @@ Signature_init(Signature *self, PyObject *args, PyObject *kwds) void Signature_dealloc(Signature *self) { - if (self->obj) + /* self->obj is the owner of the git_signature, so we musn't free it */ + if (self->obj) { Py_CLEAR(self->obj); - else { - git_signature_free((git_signature*)self->signature); - free((char*)self->encoding); + } else { + git_signature_free((git_signature *) self->signature); + free(self->encoding); } PyObject_Del(self); @@ -224,12 +225,23 @@ build_signature(Object *obj, const git_signature *signature, Signature *py_signature; py_signature = PyObject_New(Signature, &SignatureType); + if (!py_signature) + goto on_error; - if (py_signature) { - Py_INCREF(obj); - py_signature->obj = obj; - py_signature->signature = signature; - py_signature->encoding = encoding; + py_signature->encoding = NULL; + if (encoding) { + py_signature->encoding = strdup(encoding); + if (!py_signature->encoding) + goto on_error; } + + Py_XINCREF(obj); + py_signature->obj = obj; + py_signature->signature = signature; + return (PyObject*)py_signature; + +on_error: + git_signature_free((git_signature *) signature); + return NULL; } diff --git a/src/types.h b/src/types.h index e6a318994..3e704a7aa 100644 --- a/src/types.h +++ b/src/types.h @@ -191,7 +191,7 @@ typedef struct { PyObject_HEAD Object *obj; const git_signature *signature; - const char *encoding; + char *encoding; } Signature; From 687dc5388e7f75ee71ea683006ddd122d0fd2647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 27 Mar 2014 16:53:16 +0100 Subject: [PATCH 0712/2237] config: make type conversion explicit The type of a config value depends on the tool that interprets it. Parsing eagerly can lead to a situation where we return a bool instead of a string or a number. Let the user specify the type themselves by passing in a (str, type) tuple into the mapping interface. --- docs/config.rst | 16 +++++++++++++++ src/config.c | 49 +++++++++++++++++++++++++++++++++++---------- src/utils.h | 2 ++ test/test_config.py | 12 +++++------ 4 files changed, 62 insertions(+), 17 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index 348c34743..4bdaaa1cc 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -23,3 +23,19 @@ The Config type set multiple times in the configuration files. The :class:`Config` Mapping interface. + +Parsing the values +=================== + +Instead of a string, a tuple of `(str,type)` can be used to look up a +key and parse it through the Git rules. E.g. + + config['core.bare',bool] + +will return True if 'core.bare' is truthy. + +Truty values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false', +0, 'off' and 'no'. + +Available types are `bool` and `int`. Not specifying a type returns a +string. diff --git a/src/config.c b/src/config.c index 9b2625d6a..58fdeaaa8 100644 --- a/src/config.c +++ b/src/config.c @@ -170,29 +170,56 @@ Config_contains(Config *self, PyObject *py_key) { PyObject * -Config_getitem(Config *self, PyObject *py_key) +Config_getitem(Config *self, PyObject *py_input_key) { - int64_t value_int; - int err, value_bool; + int err; const char *value_str; const char *key; - PyObject* py_value, *tmp; + PyObject *py_key, *py_value, *tkey, *tmp_type = NULL; + PyTypeObject *py_type = NULL; + + if (PyTuple_Check(py_input_key) && PyTuple_Size(py_input_key) == 2) { + py_key = PyTuple_GetItem(py_input_key, 0); + tmp_type = PyTuple_GetItem(py_input_key, 1); + } else { + py_key = py_input_key; + } + + /* If passed a tuple, make sure the second item is a type */ + if (tmp_type) { + if (!PyType_Check(tmp_type)) + return NULL; + else + py_type = (PyTypeObject *) tmp_type; + } - key = py_str_borrow_c_str(&tmp, py_key, NULL); + key = py_str_borrow_c_str(&tkey, py_key, NULL); if (key == NULL) return NULL; err = git_config_get_string(&value_str, self->config, key); - Py_CLEAR(tmp); + Py_CLEAR(tkey); if (err < 0) goto cleanup; - if (git_config_parse_int64(&value_int, value_str) == 0) - py_value = PyLong_FromLongLong(value_int); - else if(git_config_parse_bool(&value_bool, value_str) == 0) - py_value = PyBool_FromLong(value_bool); - else + /* If the user specified a type, let's parse it */ + if (py_type) { + if (py_type == &PyBool_Type) { + int value; + if ((err = git_config_parse_bool(&value, value_str)) < 0) + goto cleanup; + + py_value = PyBool_FromLong(value); + } else if (py_type == &PyInteger_Type) { + int64_t value; + if ((err = git_config_parse_int64(&value, value_str)) < 0) + goto cleanup; + + py_value = PyLong_FromLongLong(value); + } + } else { py_value = to_unicode(value_str, NULL, NULL); + } cleanup: if (err < 0) { diff --git a/src/utils.h b/src/utils.h index 81744f686..e321ea12e 100644 --- a/src/utils.h +++ b/src/utils.h @@ -47,6 +47,7 @@ #undef PyLong_Check #define PyLong_Check PyInt_Check #define PyLong_FromLong PyInt_FromLong + #define PyInteger_Type PyInt_Type #define PyBytes_AS_STRING PyString_AS_STRING #define PyBytes_AsString PyString_AsString #define PyBytes_AsStringAndSize PyString_AsStringAndSize @@ -57,6 +58,7 @@ #define to_path(x) to_bytes(x) #define to_encoding(x) to_bytes(x) #else + #define PyInteger_Type PyLong_Type #define to_path(x) to_unicode(x, Py_FileSystemDefaultEncoding, "strict") #define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict") #endif diff --git a/test/test_config.py b/test/test_config.py index 9f4d460a7..f29c703e1 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -74,7 +74,7 @@ def test_new(self): config_read = Config(CONFIG_FILENAME) self.assertTrue('core.bare' in config_read) - self.assertFalse(config_read['core.bare']) + self.assertFalse(config_read['core.bare',bool]) self.assertTrue('core.editor' in config_read) self.assertEqual(config_read['core.editor'], 'ed') @@ -88,9 +88,9 @@ def test_add(self): config.add_file(CONFIG_FILENAME, 0) self.assertTrue('this.that' in config) - self.assertTrue(config['this.that']) + self.assertTrue(config['this.that',bool]) self.assertTrue('something.other.here' in config) - self.assertFalse(config['something.other.here']) + self.assertFalse(config['something.other.here',bool]) def test_read(self): config = self.repo.config @@ -103,11 +103,11 @@ def test_read(self): lambda: config['abc.def']) self.assertTrue('core.bare' in config) - self.assertFalse(config['core.bare']) + self.assertFalse(config['core.bare',bool]) self.assertTrue('core.editor' in config) self.assertEqual(config['core.editor'], 'ed') self.assertTrue('core.repositoryformatversion' in config) - self.assertEqual(config['core.repositoryformatversion'], 0) + self.assertEqual(config['core.repositoryformatversion',int], 0) new_file = open(CONFIG_FILENAME, "w") new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n") @@ -129,7 +129,7 @@ def test_write(self): self.assertFalse('core.dummy1' in config) config['core.dummy1'] = 42 self.assertTrue('core.dummy1' in config) - self.assertEqual(config['core.dummy1'], 42) + self.assertEqual(config['core.dummy1',int], 42) self.assertFalse('core.dummy2' in config) config['core.dummy2'] = 'foobar' From f0874cc1ea3f270813d5a4699a96459bdc337db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 27 Mar 2014 21:00:09 +0100 Subject: [PATCH 0713/2237] Always use new-style classes in Python 2 --- pygit2/credentials.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygit2/credentials.py b/pygit2/credentials.py index 1789022d6..cad215a57 100644 --- a/pygit2/credentials.py +++ b/pygit2/credentials.py @@ -28,7 +28,7 @@ # Import from pygit2 from _pygit2 import GIT_CREDTYPE_USERPASS_PLAINTEXT, GIT_CREDTYPE_SSH_KEY -class UserPass: +class UserPass(object): """Username/Password credentials This is an object suitable for passing to a remote's credentials @@ -51,7 +51,7 @@ def credential_tuple(self): def __call__(self, _url, _username, _allowed): return self -class Keypair: +class Keypair(object): """SSH key pair credentials This is an object suitable for passing to a remote's credentials From 60b4cb537ae93e2a4d81b731d7f69a443939df81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 1 Apr 2014 18:44:39 +0200 Subject: [PATCH 0714/2237] Writing changelog for v0.20.3 --- README.rst | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.rst b/README.rst index a76fee96e..272b149dd 100644 --- a/README.rst +++ b/README.rst @@ -70,6 +70,38 @@ Authors Changelog ============== +0.20.3 (2014-04-XX) +------------------- + +- A number of memory issues fixed + `#328 `_ + `#348 `_ + `#353 `_ + `#355 `_ + `#356 `_ +- Compatibility fixes for + PyPy (`#338 `_), + Visual Studio 2008 (`#343 `_) + and Python 3.3 (`#351 `_) +- Make the sort mode parameter in ``Repository.walk(...)`` optional + `#337 `_ +- New ``Object.peel(...)`` + `#342 `_ +- New ``Index.add_all(...)`` + `#344 `_ +- Introduce support for libgit2 options + `#350 `_ +- More informative repr for ``Repository`` objects + `#352 `_ +- Introduce support for credentials + `#354 `_ +- Several documentation fixes + `#302 `_ + `#336 `_ +- Tests, remove temporary files + `#341 `_ + + 0.20.2 (2014-02-04) ------------------- From 7a606b9b1c87788e60f3ce3822533937cf204263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 19 Jan 2014 01:27:26 +0100 Subject: [PATCH 0715/2237] Adjust to libgit2 dup changes --- src/blame.c | 14 ++++++++++---- src/reference.c | 3 +-- src/tree.c | 20 ++++++++++---------- src/treebuilder.c | 10 +++++----- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/blame.c b/src/blame.c index 80b82089a..b238f050a 100644 --- a/src/blame.c +++ b/src/blame.c @@ -69,14 +69,20 @@ wrap_blame_hunk(const git_blame_hunk *hunk, Blame *blame) py_hunk->lines_in_hunk = hunk->lines_in_hunk; py_hunk->final_commit_id = git_oid_allocfmt(&hunk->final_commit_id); py_hunk->final_start_line_number = hunk->final_start_line_number; - py_hunk->final_signature = hunk->final_signature != NULL ? - git_signature_dup(hunk->final_signature) : NULL; + + py_hunk->final_signature = NULL; + if (hunk->final_signature) + git_signature_dup(&py_hunk->final_signature, hunk->final_signature); + py_hunk->orig_commit_id = git_oid_allocfmt(&hunk->orig_commit_id); py_hunk->orig_path = hunk->orig_path != NULL ? strdup(hunk->orig_path) : NULL; py_hunk->orig_start_line_number = hunk->orig_start_line_number; - py_hunk->orig_signature = hunk->orig_signature != NULL ? - git_signature_dup(hunk->orig_signature) : NULL; + + py_hunk->orig_signature = NULL; + if (hunk->orig_signature) + git_signature_dup(&py_hunk->orig_signature, hunk->orig_signature); + py_hunk->boundary = hunk->boundary; } diff --git a/src/reference.c b/src/reference.c index e597e353a..1c78d4199 100644 --- a/src/reference.c +++ b/src/reference.c @@ -62,8 +62,7 @@ RefLogIter_iternext(RefLogIter *self) py_entry->oid_old = git_oid_allocfmt(git_reflog_entry_id_old(entry)); py_entry->oid_new = git_oid_allocfmt(git_reflog_entry_id_new(entry)); py_entry->message = strdup(git_reflog_entry_message(entry)); - py_entry->signature = git_signature_dup( - git_reflog_entry_committer(entry)); + git_signature_dup(&py_entry->signature, git_reflog_entry_committer(entry)); ++(self->i); diff --git a/src/tree.c b/src/tree.c index d1968faed..b72d57858 100644 --- a/src/tree.c +++ b/src/tree.c @@ -284,20 +284,20 @@ TreeEntry * Tree_getitem_by_index(Tree *self, PyObject *py_index) { int index; - const git_tree_entry *entry; + const git_tree_entry *entry_src; + git_tree_entry *entry; index = Tree_fix_index(self, py_index); if (PyErr_Occurred()) return NULL; - entry = git_tree_entry_byindex(self->tree, index); - if (!entry) { + entry_src = git_tree_entry_byindex(self->tree, index); + if (!entry_src) { PyErr_SetObject(PyExc_IndexError, py_index); return NULL; } - entry = git_tree_entry_dup(entry); - if (entry == NULL) { + if (git_tree_entry_dup(&entry, entry_src) < 0) { PyErr_SetNone(PyExc_MemoryError); return NULL; } @@ -550,16 +550,16 @@ TreeIter_dealloc(TreeIter *self) TreeEntry * TreeIter_iternext(TreeIter *self) { - const git_tree_entry *entry; + const git_tree_entry *entry_src; + git_tree_entry *entry; - entry = git_tree_entry_byindex(self->owner->tree, self->i); - if (!entry) + entry_src = git_tree_entry_byindex(self->owner->tree, self->i); + if (!entry_src) return NULL; self->i += 1; - entry = git_tree_entry_dup(entry); - if (entry == NULL) { + if (git_tree_entry_dup(&entry, entry_src) < 0) { PyErr_SetNone(PyExc_MemoryError); return NULL; } diff --git a/src/treebuilder.c b/src/treebuilder.c index 8354b1b5e..5957040ce 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -105,19 +105,19 @@ PyObject * TreeBuilder_get(TreeBuilder *self, PyObject *py_filename) { char *filename; - const git_tree_entry *entry; + const git_tree_entry *entry_src; + git_tree_entry *entry; filename = py_path_to_c_str(py_filename); if (filename == NULL) return NULL; - entry = git_treebuilder_get(self->bld, filename); + entry_src = git_treebuilder_get(self->bld, filename); free(filename); - if (entry == NULL) + if (entry_src == NULL) Py_RETURN_NONE; - entry = git_tree_entry_dup(entry); - if (entry == NULL) { + if (git_tree_entry_dup(&entry, entry_src) < 0) { PyErr_SetNone(PyExc_MemoryError); return NULL; } From 423b912fae0e17bd50b6989a354048b03a28a16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 19 Jan 2014 01:29:03 +0100 Subject: [PATCH 0716/2237] Adjust to reference creation signatures --- src/branch.c | 2 +- src/reference.c | 6 +++--- src/remote.c | 4 ++-- src/repository.c | 10 +++++----- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/branch.c b/src/branch.c index bcf763677..32879f346 100644 --- a/src/branch.c +++ b/src/branch.c @@ -101,7 +101,7 @@ Branch_rename(Branch *self, PyObject *args) if (!PyArg_ParseTuple(args, "s|i", &c_name, &force)) return NULL; - err = git_branch_move(&c_out, self->reference, c_name, force); + err = git_branch_move(&c_out, self->reference, c_name, force, NULL, NULL); if (err == GIT_OK) return wrap_branch(c_out, self->repo); else diff --git a/src/reference.c b/src/reference.c index 1c78d4199..2c5e0ff52 100644 --- a/src/reference.c +++ b/src/reference.c @@ -159,7 +159,7 @@ Reference_rename(Reference *self, PyObject *py_name) return NULL; /* Rename */ - err = git_reference_rename(&new_reference, self->reference, c_name, 0); + err = git_reference_rename(&new_reference, self->reference, c_name, 0, NULL, NULL); git_reference_free(self->reference); free(c_name); if (err < 0) @@ -239,7 +239,7 @@ Reference_target__set__(Reference *self, PyObject *py_target) if (err < 0) return err; - err = git_reference_set_target(&new_ref, self->reference, &oid); + err = git_reference_set_target(&new_ref, self->reference, &oid, NULL, NULL); if (err < 0) goto error; @@ -253,7 +253,7 @@ Reference_target__set__(Reference *self, PyObject *py_target) if (c_name == NULL) return -1; - err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name); + err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name, NULL, NULL); free(c_name); if (err < 0) goto error; diff --git a/src/remote.c b/src/remote.c index dee9b5a76..deb832d6c 100644 --- a/src/remote.c +++ b/src/remote.c @@ -467,7 +467,7 @@ Remote_fetch(Remote *self, PyObject *args) int err; PyErr_Clear(); - err = git_remote_fetch(self->remote); + err = git_remote_fetch(self->remote, NULL, NULL); /* * XXX: We should be checking for GIT_EUSER, but on v0.20, this does not * make it all the way to us for update_tips @@ -558,7 +558,7 @@ Remote_push(Remote *self, PyObject *args) return NULL; } - err = git_push_update_tips(push); + err = git_push_update_tips(push, NULL, NULL); if (err < 0) goto error; diff --git a/src/repository.c b/src/repository.c index bb2a11b96..738627fd3 100644 --- a/src/repository.c +++ b/src/repository.c @@ -192,7 +192,7 @@ Repository_head__set__(Repository *self, PyObject *py_refname) if (refname == NULL) return -1; - err = git_repository_set_head(self->repo, refname); + err = git_repository_set_head(self->repo, refname, NULL, NULL); Py_DECREF(trefname); if (err < 0) { Error_set_str(err, refname); @@ -929,7 +929,7 @@ Repository_create_branch(Repository *self, PyObject *args) if (!PyArg_ParseTuple(args, "sO!|i", &c_name, &CommitType, &py_commit, &force)) return NULL; - err = git_branch_create(&c_reference, self->repo, c_name, py_commit->commit, force); + err = git_branch_create(&c_reference, self->repo, c_name, py_commit->commit, force, NULL, NULL); if (err < 0) return Error_set(err); @@ -1096,7 +1096,7 @@ Repository_create_reference_direct(Repository *self, PyObject *args, if (err < 0) return NULL; - err = git_reference_create(&c_reference, self->repo, c_name, &oid, force); + err = git_reference_create(&c_reference, self->repo, c_name, &oid, force, NULL, NULL); if (err < 0) return Error_set(err); @@ -1130,7 +1130,7 @@ Repository_create_reference_symbolic(Repository *self, PyObject *args, return NULL; err = git_reference_symbolic_create(&c_reference, self->repo, c_name, - c_target, force); + c_target, force, NULL, NULL); if (err < 0) return Error_set(err); @@ -1607,7 +1607,7 @@ Repository_reset(Repository *self, PyObject* args) err = git_object_lookup_prefix(&target, self->repo, &oid, len, GIT_OBJ_ANY); - err = err < 0 ? err : git_reset(self->repo, target, reset_type); + err = err < 0 ? err : git_reset(self->repo, target, reset_type, NULL, NULL); git_object_free(target); if (err < 0) return Error_set_oid(err, &oid, len); From 9cec62d2e1faeb0bb7e3e72af08335e3b08f634f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 19 Jan 2014 02:01:50 +0100 Subject: [PATCH 0717/2237] Blame: orig_ is now filled --- test/test_blame.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/test/test_blame.py b/test/test_blame.py index dbe9e6bf0..cbdba9c6a 100644 --- a/test/test_blame.py +++ b/test/test_blame.py @@ -65,11 +65,10 @@ def test_blame_index(self): self.assertEqual(HUNKS[i][0], hunk.final_commit_id) self.assertEqual(HUNKS[i][1], hunk.final_start_line_number) self.assertEqualSignature(HUNKS[i][2], hunk.final_committer) - self.assertEqual(hunk.orig_commit_id, - '0000000000000000000000000000000000000000') + self.assertEqual(HUNKS[i][0], hunk.orig_commit_id) self.assertEqual(hunk.orig_path, PATH) self.assertEqual(HUNKS[i][1], hunk.orig_start_line_number) - self.assertTrue(hunk.orig_committer is None) + self.assertEqualSignature(HUNKS[i][2], hunk.orig_committer) self.assertEqual(HUNKS[i][3], hunk.boundary) def test_blame_with_invalid_index(self): @@ -93,11 +92,10 @@ def test_blame_for_line(self): self.assertEqual(HUNKS[i][0], hunk.final_commit_id) self.assertEqual(HUNKS[i][1], hunk.final_start_line_number) self.assertEqualSignature(HUNKS[i][2], hunk.final_committer) - self.assertEqual(hunk.orig_commit_id, - '0000000000000000000000000000000000000000') + self.assertEqual(HUNKS[i][0], hunk.orig_commit_id) self.assertEqual(hunk.orig_path, PATH) self.assertEqual(HUNKS[i][1], hunk.orig_start_line_number) - self.assertTrue(hunk.orig_committer is None) + self.assertEqualSignature(HUNKS[i][2], hunk.orig_committer) self.assertEqual(HUNKS[i][3], hunk.boundary) def test_blame_with_invalid_line(self): @@ -131,11 +129,10 @@ def test_blame_newest(self): self.assertEqual(HUNKS[i][0], hunk.final_commit_id) self.assertEqual(HUNKS[i][1], hunk.final_start_line_number) self.assertEqualSignature(HUNKS[i][2], hunk.final_committer) - self.assertEqual(hunk.orig_commit_id, - '0000000000000000000000000000000000000000') + self.assertEqual(HUNKS[i][0], hunk.orig_commit_id) self.assertEqual(hunk.orig_path, PATH) self.assertEqual(HUNKS[i][1], hunk.orig_start_line_number) - self.assertTrue(hunk.orig_committer is None) + self.assertEqualSignature(HUNKS[i][2], hunk.orig_committer) self.assertEqual(HUNKS[i][3], hunk.boundary) if __name__ == '__main__': From dd6fc972dd3b47668e3352af202cb33b76544a70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 2 Feb 2014 19:40:48 +0100 Subject: [PATCH 0718/2237] Adjust to the git_buf changes We no longer need the max-path define, so we can get rid of that and the pypy checking. --- src/branch.c | 47 +++++++++-------------------------------------- src/config.c | 20 ++++++++++++++------ src/diff.c | 20 ++++++-------------- src/pygit2.c | 22 ++++++++-------------- src/refspec.c | 46 ++++++++++++---------------------------------- 5 files changed, 49 insertions(+), 106 deletions(-) diff --git a/src/branch.c b/src/branch.c index 32879f346..39ddd675d 100644 --- a/src/branch.c +++ b/src/branch.c @@ -135,34 +135,19 @@ PyObject * Branch_remote_name__get__(Branch *self) { int err; + git_buf name = {NULL}; const char *branch_name; - char *c_name = NULL; PyObject *py_name; CHECK_REFERENCE(self); branch_name = git_reference_name(self->reference); - /* Get the length of the remote name */ - err = git_branch_remote_name(NULL, 0, self->repo->repo, branch_name); + err = git_branch_remote_name(&name, self->repo->repo, branch_name); if (err < GIT_OK) return Error_set(err); - /* Get the actual remote name */ - c_name = calloc(err, sizeof(char)); - if (c_name == NULL) - return PyErr_NoMemory(); - - err = git_branch_remote_name(c_name, - err * sizeof(char), - self->repo->repo, - branch_name); - if (err < GIT_OK) { - free(c_name); - return Error_set(err); - } - - py_name = to_unicode_n(c_name, err - 1, NULL, NULL); - free(c_name); + py_name = to_unicode(name.ptr, NULL, NULL); + git_buf_free(&name); return py_name; } @@ -227,34 +212,20 @@ PyObject * Branch_upstream_name__get__(Branch *self) { int err; + git_buf name = {NULL}; const char *branch_name; - char *c_name = NULL; PyObject *py_name; CHECK_REFERENCE(self); branch_name = git_reference_name(self->reference); - /* Get the length of the upstream name */ - err = git_branch_upstream_name(NULL, 0, self->repo->repo, branch_name); - if (err < GIT_OK) - return Error_set(err); - - /* Get the actual upstream name */ - c_name = calloc(err, sizeof(char)); - if (c_name == NULL) - return PyErr_NoMemory(); - err = git_branch_upstream_name(c_name, - err * sizeof(char), - self->repo->repo, - branch_name); - if (err < GIT_OK) { - free(c_name); + err = git_branch_upstream_name(&name, self->repo->repo, branch_name); + if (err < GIT_OK) return Error_set(err); - } - py_name = to_unicode_n(c_name, err - 1, NULL, NULL); - free(c_name); + py_name = to_unicode(name.ptr, NULL, NULL); + git_buf_free(&name); return py_name; } diff --git a/src/config.c b/src/config.c index 9b2625d6a..393fded93 100644 --- a/src/config.c +++ b/src/config.c @@ -103,10 +103,11 @@ PyDoc_STRVAR(Config_get_global_config__doc__, PyObject * Config_get_global_config(void) { - char path[GIT_PATH_MAX]; + git_buf path = {NULL}; + PyObject *py_config; int err; - err = git_config_find_global(path, GIT_PATH_MAX); + err = git_config_find_global(&path); if (err < 0) { if (err == GIT_ENOTFOUND) { PyErr_SetString(PyExc_IOError, "Global config file not found."); @@ -116,7 +117,10 @@ Config_get_global_config(void) return Error_set(err); } - return wrap_config(path); + py_config = wrap_config(path.ptr); + + git_buf_free(&path); + return py_config; } @@ -128,10 +132,11 @@ PyDoc_STRVAR(Config_get_system_config__doc__, PyObject * Config_get_system_config(void) { - char path[GIT_PATH_MAX]; + git_buf path = {NULL}; + PyObject *py_config; int err; - err = git_config_find_system(path, GIT_PATH_MAX); + err = git_config_find_system(&path); if (err < 0) { if (err == GIT_ENOTFOUND) { PyErr_SetString(PyExc_IOError, "System config file not found."); @@ -140,7 +145,10 @@ Config_get_system_config(void) return Error_set(err); } - return wrap_config(path); + py_config = wrap_config(path.ptr); + + git_buf_free(&path); + return py_config; } diff --git a/src/diff.c b/src/diff.c index adea3cdd8..da0887406 100644 --- a/src/diff.c +++ b/src/diff.c @@ -292,8 +292,7 @@ PyObject * Diff_patch__get__(Diff *self) { git_patch* patch; - char **strings = NULL; - char *buffer = NULL; + git_buf buf = {NULL}; int err = GIT_ERROR; size_t i, len, num; PyObject *py_patch = NULL; @@ -301,32 +300,25 @@ Diff_patch__get__(Diff *self) num = git_diff_num_deltas(self->list); if (num == 0) Py_RETURN_NONE; - MALLOC(strings, num * sizeof(char*), cleanup); for (i = 0, len = 1; i < num ; ++i) { err = git_patch_from_diff(&patch, self->list, i); if (err < 0) goto cleanup; - err = git_patch_to_str(&(strings[i]), patch); + /* This appends to the current buf, so we can simply keep passing it */ + err = git_patch_to_buf(&buf, patch); if (err < 0) goto cleanup; - len += strlen(strings[i]); git_patch_free(patch); } - CALLOC(buffer, (len + 1), sizeof(char), cleanup); - for (i = 0; i < num; ++i) { - strcat(buffer, strings[i]); - free(strings[i]); - } - free(strings); - - py_patch = to_unicode(buffer, NULL, NULL); - free(buffer); + py_patch = to_unicode(buf.ptr, NULL, NULL); + git_buf_free(&buf); cleanup: + git_buf_free(&buf); return (err < 0) ? Error_set(err) : py_patch; } diff --git a/src/pygit2.c b/src/pygit2.c index bda9545ec..2e80cc5b1 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -28,11 +28,6 @@ #define PY_SSIZE_T_CLEAN #include -/* Pypy does not provide this header */ -#ifndef PYPY_VERSION -# include -#endif - #include #include "error.h" #include "types.h" @@ -41,11 +36,6 @@ #include "oid.h" #include "options.h" -/* FIXME: This is for pypy */ -#ifndef MAXPATHLEN -# define MAXPATHLEN 1024 -#endif - extern PyObject *GitError; extern PyTypeObject RepositoryType; @@ -187,21 +177,25 @@ PyDoc_STRVAR(discover_repository__doc__, PyObject * discover_repository(PyObject *self, PyObject *args) { + git_buf repo_path = {NULL}; const char *path; + PyObject *py_repo_path; int across_fs = 0; const char *ceiling_dirs = NULL; - char repo_path[MAXPATHLEN]; int err; if (!PyArg_ParseTuple(args, "s|Is", &path, &across_fs, &ceiling_dirs)) return NULL; - err = git_repository_discover(repo_path, sizeof(repo_path), - path, across_fs, ceiling_dirs); + memset(&repo_path, 0, sizeof(git_buf)); + err = git_repository_discover(&repo_path, path, across_fs, ceiling_dirs); if (err < 0) return Error_set_str(err, path); - return to_path(repo_path); + py_repo_path = to_path(repo_path.ptr); + git_buf_free(&repo_path); + + return py_repo_path; }; PyDoc_STRVAR(hashfile__doc__, diff --git a/src/refspec.c b/src/refspec.c index 3053fe60b..008aa6648 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -155,35 +155,24 @@ PyDoc_STRVAR(Refspec_transform__doc__, PyObject * Refspec_transform(Refspec *self, PyObject *py_str) { + git_buf trans = {NULL}; const char *str; - char *trans; - int err, len, alen; + int err; PyObject *py_trans, *tstr; str = py_str_borrow_c_str(&tstr, py_str, NULL); - alen = len = strlen(str); - - do { - alen *= alen; - trans = malloc(alen); - if (!trans) { - Py_DECREF(tstr); - return PyErr_NoMemory(); - } - - err = git_refspec_transform(trans, alen, self->refspec, str); - } while(err == GIT_EBUFS); + + err = git_refspec_transform(&trans, self->refspec, str); Py_DECREF(tstr); if (err < 0) { - free(trans); Error_set(err); return NULL; } - py_trans = to_unicode(trans, NULL, NULL); + py_trans = to_unicode(trans.ptr, NULL, NULL); - free(trans); + git_buf_free(&trans); return py_trans; } @@ -196,35 +185,24 @@ PyDoc_STRVAR(Refspec_rtransform__doc__, PyObject * Refspec_rtransform(Refspec *self, PyObject *py_str) { + git_buf trans = {NULL}; const char *str; - char *trans; - int err, len, alen; + int err; PyObject *py_trans, *tstr; str = py_str_borrow_c_str(&tstr, py_str, NULL); - alen = len = strlen(str); - - do { - alen *= alen; - trans = malloc(alen); - if (!trans) { - Py_DECREF(tstr); - return PyErr_NoMemory(); - } - - err = git_refspec_rtransform(trans, alen, self->refspec, str); - } while(err == GIT_EBUFS); + + err = git_refspec_rtransform(&trans, self->refspec, str); Py_DECREF(tstr); if (err < 0) { - free(trans); Error_set(err); return NULL; } - py_trans = to_unicode(trans, NULL, NULL); + py_trans = to_unicode(trans.ptr, NULL, NULL); - free(trans); + git_buf_free(&trans); return py_trans; } From e5f6798f67cb7de2edbfbfc0b20571d6979c7650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 2 Feb 2014 20:28:23 +0100 Subject: [PATCH 0719/2237] Adjust to oid -> id renaming --- docs/diff.rst | 4 ++-- docs/merge.rst | 8 ++++---- docs/objects.rst | 24 ++++++++++++------------ docs/references.rst | 6 +++--- docs/working-copy.rst | 6 +++--- src/diff.c | 12 ++++++------ src/index.c | 16 ++++++++-------- src/mergeresult.c | 12 ++++++------ src/note.c | 8 ++++---- src/repository.c | 6 +++--- src/types.h | 4 ++-- test/test_diff.py | 6 +++--- test/test_index.py | 8 ++++---- test/test_note.py | 4 ++-- test/test_repository.py | 14 +++++++------- 15 files changed, 69 insertions(+), 69 deletions(-) diff --git a/docs/diff.rst b/docs/diff.rst index 09fc9da21..dc7bf4dc0 100644 --- a/docs/diff.rst +++ b/docs/diff.rst @@ -50,8 +50,8 @@ Attributes: .. autoattribute:: pygit2.Patch.old_file_path .. autoattribute:: pygit2.Patch.new_file_path -.. autoattribute:: pygit2.Patch.old_oid -.. autoattribute:: pygit2.Patch.new_oid +.. autoattribute:: pygit2.Patch.old_id +.. autoattribute:: pygit2.Patch.new_id .. autoattribute:: pygit2.Patch.status .. autoattribute:: pygit2.Patch.similarity .. autoattribute:: pygit2.Patch.hunks diff --git a/docs/merge.rst b/docs/merge.rst index 4d98590aa..47984a868 100644 --- a/docs/merge.rst +++ b/docs/merge.rst @@ -22,8 +22,8 @@ merge with the default ones defined in GIT_MERGE_OPTS_INIT libgit2 constant. Example:: >>> branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533' - >>> branch_oid = self.repo.get(branch_head_hex).id - >>> merge_result = self.repo.merge(branch_oid) + >>> branch_id = self.repo.get(branch_head_hex).id + >>> merge_result = self.repo.merge(branch_id) The MergeResult object ====================== @@ -33,5 +33,5 @@ Represents the result of a merge and contains these fields: - is_uptodate: bool, if there wasn't any merge because the repo was already up to date - is_fastforward: bool, whether the merge was fastforward or not -- fastforward_oid: Oid, in the case it was a fastforward, this is the - forwarded Oid. +- fastforward_id: Oid, in the case it was a fastforward, this is the + forwarded id. diff --git a/docs/objects.rst b/docs/objects.rst index 3583c17ec..b9d038bbc 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -14,14 +14,14 @@ type. Object lookup ================= -In the previous chapter we learnt about Object IDs. With an oid we can ask the +In the previous chapter we learnt about Object IDs. With an Oid we can ask the repository to get the associated object. To do that the ``Repository`` class implementes a subset of the mapping interface. .. automethod:: pygit2.Repository.get - Return the Git object for the given *oid*, returns the *default* value if - there's no object in the repository with that oid. The oid can be an Oid + Return the Git object for the given *id*, returns the *default* value if + there's no object in the repository with that id. The id can be an Oid object, or an hexadecimal string. Example:: @@ -32,16 +32,16 @@ implementes a subset of the mapping interface. >>> obj <_pygit2.Commit object at 0x7ff27a6b60f0> -.. method:: Repository.__getitem__(oid) +.. method:: Repository.__getitem__(id) - Return the Git object for the given oid, raise ``KeyError`` if there's no - object in the repository with that oid. The oid can be an Oid object, or + Return the Git object for the given id, raise ``KeyError`` if there's no + object in the repository with that id. The id can be an Oid object, or an hexadecimal string. -.. method:: Repository.__contains__(oid) +.. method:: Repository.__contains__(id) - Returns True if there is an object in the Repository with that oid, False - if there is not. The oid can be an Oid object, or an hexadecimal string. + Returns True if there is an object in the Repository with that id, False + if there is not. The id can be an Oid object, or an hexadecimal string. The Object base type @@ -125,15 +125,15 @@ them to the Git object database: Example: - >>> oid = repo.create_blob('foo bar') # Creates blob from bytes string - >>> blob = repo[oid] + >>> id = repo.create_blob('foo bar') # Creates blob from bytes string + >>> blob = repo[id] >>> blob.data 'foo bar' .. automethod:: pygit2.Repository.create_blob_fromworkdir .. automethod:: pygit2.Repository.create_blob_fromdisk -There are also some functions to calculate the oid for a byte string without +There are also some functions to calculate the id for a byte string without creating the blob object: .. autofunction:: pygit2.hash diff --git a/docs/references.rst b/docs/references.rst index 363e3eebe..049fa8122 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -92,8 +92,8 @@ Example:: >>> for entry in head.log(): ... print(entry.message) -.. autoattribute:: pygit2.RefLogEntry.oid_new -.. autoattribute:: pygit2.RefLogEntry.oid_old +.. autoattribute:: pygit2.RefLogEntry.id_new +.. autoattribute:: pygit2.RefLogEntry.id_old .. autoattribute:: pygit2.RefLogEntry.message .. autoattribute:: pygit2.RefLogEntry.committer @@ -109,6 +109,6 @@ The Note type -------------------- .. autoattribute:: pygit2.Note.annotated_id -.. autoattribute:: pygit2.Note.oid +.. autoattribute:: pygit2.Note.id .. autoattribute:: pygit2.Note.message .. automethod:: pygit2.Note.remove diff --git a/docs/working-copy.rst b/docs/working-copy.rst index 76bcada87..cc1853d1d 100644 --- a/docs/working-copy.rst +++ b/docs/working-copy.rst @@ -8,8 +8,8 @@ Index read:: >>> index = repo.index >>> index.read() - >>> oid = index['path/to/file'].id # from path to object id - >>> blob = repo[oid] # from object id to object + >>> id = index['path/to/file'].id # from path to object id + >>> blob = repo[id] # from object id to object Iterate over all entries of the index:: @@ -43,7 +43,7 @@ The Index type The IndexEntry type -------------------- -.. autoattribute:: pygit2.IndexEntry.oid +.. autoattribute:: pygit2.IndexEntry.id .. autoattribute:: pygit2.IndexEntry.hex .. autoattribute:: pygit2.IndexEntry.path .. autoattribute:: pygit2.IndexEntry.mode diff --git a/src/diff.c b/src/diff.c index da0887406..3aeddeb85 100644 --- a/src/diff.c +++ b/src/diff.c @@ -80,8 +80,8 @@ wrap_patch(git_patch *patch) py_patch->status = git_diff_status_char(delta->status); py_patch->similarity = delta->similarity; py_patch->flags = delta->flags; - py_patch->old_oid = git_oid_allocfmt(&delta->old_file.oid); - py_patch->new_oid = git_oid_allocfmt(&delta->new_file.oid); + py_patch->old_id = git_oid_allocfmt(&delta->old_file.id); + py_patch->new_id = git_oid_allocfmt(&delta->new_file.id); git_patch_line_stats(NULL, &additions, &deletions, patch); py_patch->additions = additions; @@ -149,8 +149,8 @@ static void Patch_dealloc(Patch *self) { Py_CLEAR(self->hunks); - free(self->old_oid); - free(self->new_oid); + free(self->old_id); + free(self->new_id); /* We do not have to free old_file_path and new_file_path, they will * be freed by git_diff_list_free in Diff_dealloc */ PyObject_Del(self); @@ -159,8 +159,8 @@ Patch_dealloc(Patch *self) PyMemberDef Patch_members[] = { MEMBER(Patch, old_file_path, T_STRING, "old file path"), MEMBER(Patch, new_file_path, T_STRING, "new file path"), - MEMBER(Patch, old_oid, T_STRING, "old oid"), - MEMBER(Patch, new_oid, T_STRING, "new oid"), + MEMBER(Patch, old_id, T_STRING, "old oid"), + MEMBER(Patch, new_id, T_STRING, "new oid"), MEMBER(Patch, status, T_CHAR, "status"), MEMBER(Patch, similarity, T_INT, "similarity"), MEMBER(Patch, hunks, T_OBJECT, "hunks"), diff --git a/src/index.c b/src/index.c index 726974986..1ae3a1bdd 100644 --- a/src/index.c +++ b/src/index.c @@ -628,7 +628,7 @@ IndexEntry_init(IndexEntry *self, PyObject *args, PyObject *kwds) return -1; if (id) - git_oid_cpy(&self->entry.oid, &id->oid); + git_oid_cpy(&self->entry.id, &id->oid); if (mode) self->entry.mode = mode; @@ -689,18 +689,18 @@ IndexEntry_path__set__(IndexEntry *self, PyObject *py_path) return 0; } -PyDoc_STRVAR(IndexEntry_oid__doc__, "Object id."); +PyDoc_STRVAR(IndexEntry_id__doc__, "Object id."); PyObject * -IndexEntry_oid__get__(IndexEntry *self) +IndexEntry_id__get__(IndexEntry *self) { - return git_oid_to_python(&self->entry.oid); + return git_oid_to_python(&self->entry.id); } int -IndexEntry_oid__set__(IndexEntry *self, PyObject *py_id) +IndexEntry_id__set__(IndexEntry *self, PyObject *py_id) { - if (!py_oid_to_git_oid(py_id, &self->entry.oid)) + if (!py_oid_to_git_oid(py_id, &self->entry.id)) return -1; return 0; @@ -711,13 +711,13 @@ PyDoc_STRVAR(IndexEntry_hex__doc__, "Hex id."); PyObject * IndexEntry_hex__get__(IndexEntry *self) { - return git_oid_to_py_str(&self->entry.oid); + return git_oid_to_py_str(&self->entry.id); } PyGetSetDef IndexEntry_getseters[] = { GETSET(IndexEntry, mode), GETSET(IndexEntry, path), - GETSET(IndexEntry, oid), + GETSET(IndexEntry, id), GETTER(IndexEntry, hex), {NULL}, }; diff --git a/src/mergeresult.c b/src/mergeresult.c index 345e56ee6..4579e4b02 100644 --- a/src/mergeresult.c +++ b/src/mergeresult.c @@ -80,15 +80,15 @@ MergeResult_is_fastforward__get__(MergeResult *self) Py_RETURN_FALSE; } -PyDoc_STRVAR(MergeResult_fastforward_oid__doc__, "Fastforward Oid"); +PyDoc_STRVAR(MergeResult_fastforward_id__doc__, "Fastforward Oid"); PyObject * -MergeResult_fastforward_oid__get__(MergeResult *self) +MergeResult_fastforward_id__get__(MergeResult *self) { if (git_merge_result_is_fastforward(self->result)) { - git_oid fastforward_oid; - git_merge_result_fastforward_oid(&fastforward_oid, self->result); - return git_oid_to_python((const git_oid *)&fastforward_oid); + git_oid fastforward_id; + git_merge_result_fastforward_id(&fastforward_id, self->result); + return git_oid_to_python((const git_oid *)&fastforward_id); } else Py_RETURN_NONE; } @@ -96,7 +96,7 @@ MergeResult_fastforward_oid__get__(MergeResult *self) PyGetSetDef MergeResult_getseters[] = { GETTER(MergeResult, is_uptodate), GETTER(MergeResult, is_fastforward), - GETTER(MergeResult, fastforward_oid), + GETTER(MergeResult, fastforward_id), {NULL}, }; diff --git a/src/note.c b/src/note.c index e14e30862..9762ef61e 100644 --- a/src/note.c +++ b/src/note.c @@ -66,13 +66,13 @@ Note_remove(Note *self, PyObject* args) } -PyDoc_STRVAR(Note_oid__doc__, +PyDoc_STRVAR(Note_id__doc__, "Gets the id of the blob containing the note message\n"); PyObject * -Note_oid__get__(Note *self) +Note_id__get__(Note *self) { - return git_oid_to_python(git_note_oid(self->note)); + return git_oid_to_python(git_note_id(self->note)); } @@ -108,7 +108,7 @@ PyMemberDef Note_members[] = { PyGetSetDef Note_getseters[] = { GETTER(Note, message), - GETTER(Note, oid), + GETTER(Note, id), {NULL} }; diff --git a/src/repository.c b/src/repository.c index 738627fd3..53f9649db 100644 --- a/src/repository.c +++ b/src/repository.c @@ -588,9 +588,9 @@ Repository_merge_base(Repository *self, PyObject *args) } PyDoc_STRVAR(Repository_merge__doc__, - "merge(oid) -> MergeResult\n" + "merge(id) -> MergeResult\n" "\n" - "Merges the given oid and returns the MergeResult.\n" + "Merges the given id and returns the MergeResult.\n" "\n" "If the merge is fastforward the MergeResult will contain the new\n" "fastforward oid.\n" @@ -614,7 +614,7 @@ Repository_merge(Repository *self, PyObject *py_oid) if (len == 0) return NULL; - err = git_merge_head_from_oid(&oid_merge_head, self->repo, &oid); + err = git_merge_head_from_id(&oid_merge_head, self->repo, &oid); if (err < 0) return Error_set(err); diff --git a/src/types.h b/src/types.h index cfd00092c..f1273990f 100644 --- a/src/types.h +++ b/src/types.h @@ -114,8 +114,8 @@ typedef struct { PyObject* hunks; const char * old_file_path; const char * new_file_path; - char* old_oid; - char* new_oid; + char* old_id; + char* new_id; char status; unsigned similarity; unsigned additions; diff --git a/test/test_diff.py b/test/test_diff.py index f982d0194..2f4314d98 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -257,13 +257,13 @@ def test_diff_patch(self): self.assertEqual(diff.patch, PATCH) self.assertEqual(len(diff), len([patch for patch in diff])) - def test_diff_oids(self): + def test_diff_ids(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] patch = commit_a.tree.diff_to_tree(commit_b.tree)[0] - self.assertEqual(patch.old_oid, + self.assertEqual(patch.old_id, '7f129fd57e31e935c6d60a0c794efe4e6927664b') - self.assertEqual(patch.new_oid, + self.assertEqual(patch.new_id, 'af431f20fc541ed6d5afede3e2dc7160f6f01f16') def test_hunk_content(self): diff --git a/test/test_index.py b/test/test_index.py index 7bed86bb3..1426e7fa2 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -181,13 +181,13 @@ def test_change_attributes(self): index = self.repo.index entry = index['hello.txt'] ign_entry = index['.gitignore'] - self.assertNotEqual(ign_entry.oid, entry.oid) + self.assertNotEqual(ign_entry.id, entry.id) self.assertNotEqual(entry.mode, pygit2.GIT_FILEMODE_BLOB_EXECUTABLE) entry.path = 'foo.txt' - entry.oid = ign_entry.oid + entry.id = ign_entry.id entry.mode = pygit2.GIT_FILEMODE_BLOB_EXECUTABLE self.assertEqual('foo.txt', entry.path) - self.assertEqual(ign_entry.oid, entry.oid) + self.assertEqual(ign_entry.id, entry.id) self.assertEqual(pygit2.GIT_FILEMODE_BLOB_EXECUTABLE, entry.mode) def test_write_tree_to(self): @@ -201,7 +201,7 @@ class IndexEntryTest(utils.RepoTestCase): def test_create_entry(self): index = self.repo.index hello_entry = index['hello.txt'] - entry = pygit2.IndexEntry('README.md', hello_entry.oid, hello_entry.mode) + entry = pygit2.IndexEntry('README.md', hello_entry.id, hello_entry.mode) index.add(entry) tree_id = index.write_tree() self.assertEqual('60e769e57ae1d6a2ab75d8d253139e6260e1f912', str(tree_id)) diff --git a/test/test_note.py b/test/test_note.py index 74f08940d..4fcead56e 100644 --- a/test/test_note.py +++ b/test/test_note.py @@ -58,7 +58,7 @@ def test_create_note(self): def test_lookup_note(self): annotated_id = self.repo.head.target.hex note = self.repo.lookup_note(annotated_id) - self.assertEqual(NOTES[0][0], note.oid.hex) + self.assertEqual(NOTES[0][0], note.id.hex) self.assertEqual(NOTES[0][1], note.message) def test_remove_note(self): @@ -70,7 +70,7 @@ def test_remove_note(self): def test_iterate_notes(self): for i, note in enumerate(self.repo.notes()): - entry = (note.oid.hex, note.message, note.annotated_id) + entry = (note.id.hex, note.message, note.annotated_id) self.assertEqual(NOTES[i], entry) def test_iterate_non_existing_ref(self): diff --git a/test/test_repository.py b/test/test_repository.py index 71214ccef..8da731158 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -314,7 +314,7 @@ def test_merge_uptodate(self): merge_result = self.repo.merge(branch_oid) self.assertTrue(merge_result.is_uptodate) self.assertFalse(merge_result.is_fastforward) - self.assertEqual(None, merge_result.fastforward_oid) + self.assertEqual(None, merge_result.fastforward_id) self.assertEqual({}, self.repo.status()) def test_merge_fastforward(self): @@ -324,8 +324,8 @@ def test_merge_fastforward(self): self.assertFalse(merge_result.is_uptodate) self.assertTrue(merge_result.is_fastforward) # Asking twice to assure the reference counting is correct - self.assertEqual(branch_head_hex, merge_result.fastforward_oid.hex) - self.assertEqual(branch_head_hex, merge_result.fastforward_oid.hex) + self.assertEqual(branch_head_hex, merge_result.fastforward_id.hex) + self.assertEqual(branch_head_hex, merge_result.fastforward_id.hex) self.assertEqual({}, self.repo.status()) def test_merge_no_fastforward_no_conflicts(self): @@ -335,8 +335,8 @@ def test_merge_no_fastforward_no_conflicts(self): self.assertFalse(merge_result.is_uptodate) self.assertFalse(merge_result.is_fastforward) # Asking twice to assure the reference counting is correct - self.assertEqual(None, merge_result.fastforward_oid) - self.assertEqual(None, merge_result.fastforward_oid) + self.assertEqual(None, merge_result.fastforward_id) + self.assertEqual(None, merge_result.fastforward_id) self.assertEqual({'bye.txt': 1}, self.repo.status()) self.assertEqual({'bye.txt': 1}, self.repo.status()) # Checking the index works as expected @@ -351,8 +351,8 @@ def test_merge_no_fastforward_conflicts(self): self.assertFalse(merge_result.is_uptodate) self.assertFalse(merge_result.is_fastforward) # Asking twice to assure the reference counting is correct - self.assertEqual(None, merge_result.fastforward_oid) - self.assertEqual(None, merge_result.fastforward_oid) + self.assertEqual(None, merge_result.fastforward_id) + self.assertEqual(None, merge_result.fastforward_id) self.assertEqual({'.gitignore': 132}, self.repo.status()) self.assertEqual({'.gitignore': 132}, self.repo.status()) # Checking the index works as expected From a870a59f2a0216163f9b93378f2cf4704c2c9ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 23 Mar 2014 14:51:51 +0100 Subject: [PATCH 0720/2237] Adjust to option struct naming change --- src/repository.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/repository.c b/src/repository.c index 53f9649db..4c7ba006f 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1360,7 +1360,7 @@ PyDoc_STRVAR(Repository_checkout_head__doc__, PyObject * Repository_checkout_head(Repository *self, PyObject *args) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; unsigned int strategy; int err; @@ -1384,7 +1384,7 @@ PyDoc_STRVAR(Repository_checkout_index__doc__, PyObject * Repository_checkout_index(Repository *self, PyObject *args) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; unsigned int strategy; int err; @@ -1408,7 +1408,7 @@ PyDoc_STRVAR(Repository_checkout_tree__doc__, PyObject * Repository_checkout_tree(Repository *self, PyObject *args) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; unsigned int strategy; Object *py_object; int err; From 55037c23a34421c28558b44a0726539baa9f371a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 25 Mar 2014 03:14:02 +0100 Subject: [PATCH 0721/2237] Adjust to the merge changes There is no more MergeResult type. Instead, the user can use Repository.merge_analysis() to get an overview of their options and call git_merge() when they mean to merge. The git_merge() function now also performs a checkout. --- src/mergeresult.c | 145 ---------------------------------------- src/mergeresult.h | 37 ---------- src/pygit2.c | 8 ++- src/repository.c | 65 +++++++++++++----- src/types.h | 6 -- test/test_repository.py | 65 +++++++++--------- 6 files changed, 84 insertions(+), 242 deletions(-) delete mode 100644 src/mergeresult.c delete mode 100644 src/mergeresult.h diff --git a/src/mergeresult.c b/src/mergeresult.c deleted file mode 100644 index 4579e4b02..000000000 --- a/src/mergeresult.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2010-2014 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 "utils.h" -#include "types.h" -#include "oid.h" -#include "repository.h" -#include "mergeresult.h" - -extern PyTypeObject MergeResultType; -extern PyTypeObject IndexType; - -PyObject * -git_merge_result_to_python(git_merge_result *merge_result) -{ - MergeResult *py_merge_result; - - py_merge_result = PyObject_New(MergeResult, &MergeResultType); - if (!py_merge_result) - return NULL; - - py_merge_result->result = merge_result; - - return (PyObject*) py_merge_result; -} - -void -MergeResult_dealloc(MergeResult *self) -{ - git_merge_result_free(self->result); - PyObject_Del(self); -} - - -PyDoc_STRVAR(MergeResult_is_uptodate__doc__, "Is up to date"); - -PyObject * -MergeResult_is_uptodate__get__(MergeResult *self) -{ - if (git_merge_result_is_uptodate(self->result)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - -PyDoc_STRVAR(MergeResult_is_fastforward__doc__, "Is fastforward"); - -PyObject * -MergeResult_is_fastforward__get__(MergeResult *self) -{ - if (git_merge_result_is_fastforward(self->result)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - -PyDoc_STRVAR(MergeResult_fastforward_id__doc__, "Fastforward Oid"); - -PyObject * -MergeResult_fastforward_id__get__(MergeResult *self) -{ - if (git_merge_result_is_fastforward(self->result)) { - git_oid fastforward_id; - git_merge_result_fastforward_id(&fastforward_id, self->result); - return git_oid_to_python((const git_oid *)&fastforward_id); - } - else Py_RETURN_NONE; -} - -PyGetSetDef MergeResult_getseters[] = { - GETTER(MergeResult, is_uptodate), - GETTER(MergeResult, is_fastforward), - GETTER(MergeResult, fastforward_id), - {NULL}, -}; - -PyDoc_STRVAR(MergeResult__doc__, "MergeResult object."); - -PyTypeObject MergeResultType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.MergeResult", /* tp_name */ - sizeof(MergeResult), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)MergeResult_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - MergeResult__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 */ - MergeResult_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/mergeresult.h b/src/mergeresult.h deleted file mode 100644 index 74161ea9d..000000000 --- a/src/mergeresult.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2010-2014 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. - */ - -#ifndef INCLUDE_pygit2_merge_result_h -#define INCLUDE_pygit2_merge_result_h - -#define PY_SSIZE_T_CLEAN -#include -#include - -PyObject* git_merge_result_to_python(git_merge_result *merge_result); - -#endif diff --git a/src/pygit2.c b/src/pygit2.c index 2e80cc5b1..6fedc5d44 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -71,7 +71,6 @@ extern PyTypeObject NoteIterType; extern PyTypeObject BlameType; extern PyTypeObject BlameIterType; extern PyTypeObject BlameHunkType; -extern PyTypeObject MergeResultType; @@ -477,8 +476,11 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES) /* Merge */ - INIT_TYPE(MergeResultType, NULL, NULL) - ADD_TYPE(m, MergeResult) + ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_NONE) + ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_NORMAL) + ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_UP_TO_DATE) + ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_FASTFORWARD) + ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_UNBORN) /* Global initialization of libgit2 */ git_threads_init(); diff --git a/src/repository.c b/src/repository.c index 4c7ba006f..ef9c8e38a 100644 --- a/src/repository.c +++ b/src/repository.c @@ -38,7 +38,6 @@ #include "remote.h" #include "branch.h" #include "blame.h" -#include "mergeresult.h" #include "signature.h" #include @@ -587,28 +586,61 @@ Repository_merge_base(Repository *self, PyObject *args) return git_oid_to_python(&oid); } +PyDoc_STRVAR(Repository_merge_analysis__doc__, + "merge_analysis(id) -> Integer\n" + "\n" + "Analyzes the given branch and determines the opportunities for merging\n" + "them into the HEAD of the repository\n" + "\n" + "The returned value is a mixture of the GIT_MERGE_ANALYSIS_NONE, _NORMAL,\n" + " _UP_TO_DATE, _FASTFORWARD and _UNBORN flags"); + +PyObject * +Repository_merge_analysis(Repository *self, PyObject *py_id) +{ + int err; + size_t len; + git_oid id; + git_merge_head *merge_head; + git_merge_analysis_t analysis; + + len = py_oid_to_git_oid(py_id, &id); + if (len == 0) + return NULL; + + err = git_merge_head_from_id(&merge_head, self->repo, &id); + if (err < 0) + return Error_set(err); + + err = git_merge_analysis(&analysis, self->repo, (const git_merge_head **) &merge_head, 1); + git_merge_head_free(merge_head); + + if (err < 0) + return Error_set(err); + + return PyLong_FromLong(analysis); +} + PyDoc_STRVAR(Repository_merge__doc__, - "merge(id) -> MergeResult\n" + "merge(id)\n" "\n" - "Merges the given id and returns the MergeResult.\n" + "Merges the given id into HEAD.\n" "\n" - "If the merge is fastforward the MergeResult will contain the new\n" - "fastforward oid.\n" - "If the branch is uptodate, nothing to merge, the MergeResult will\n" - "have the fastforward oid as None.\n" - "If the merge is not fastforward the MergeResult will have the status\n" - "produced by the merge, even if there are conflicts."); + "Merges the given commit(s) into HEAD, writing the results into the\n" + "working directory. Any changes are staged for commit and any conflicts\n" + "are written to the index. Callers should inspect the repository's\n" + "index after this completes, resolve any conflicts and prepare a\n" + "commit."); PyObject * Repository_merge(Repository *self, PyObject *py_oid) { - git_merge_result *merge_result; git_merge_head *oid_merge_head; git_oid oid; - const git_merge_opts default_opts = GIT_MERGE_OPTS_INIT; int err; size_t len; - PyObject *py_merge_result; + git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; len = py_oid_to_git_oid(py_oid, &oid); if (len == 0) @@ -618,15 +650,15 @@ Repository_merge(Repository *self, PyObject *py_oid) if (err < 0) return Error_set(err); - err = git_merge(&merge_result, self->repo, + err = git_merge(self->repo, (const git_merge_head **)&oid_merge_head, 1, - &default_opts); + &merge_opts, &checkout_opts); + git_merge_head_free(oid_merge_head); if (err < 0) return Error_set(err); - py_merge_result = git_merge_result_to_python(merge_result); - return py_merge_result; + Py_RETURN_NONE; } PyDoc_STRVAR(Repository_walk__doc__, @@ -1623,6 +1655,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, TreeBuilder, METH_VARARGS), METHOD(Repository, walk, METH_VARARGS), METHOD(Repository, merge_base, METH_VARARGS), + METHOD(Repository, merge_analysis, METH_O), METHOD(Repository, merge, METH_O), METHOD(Repository, read, METH_O), METHOD(Repository, write, METH_VARARGS), diff --git a/src/types.h b/src/types.h index f1273990f..15ff74094 100644 --- a/src/types.h +++ b/src/types.h @@ -249,10 +249,4 @@ typedef struct { char boundary; } BlameHunk; -/* git_merge */ -typedef struct { - PyObject_HEAD - git_merge_result *result; -} MergeResult; - #endif diff --git a/test/test_repository.py b/test/test_repository.py index 8da731158..c35b0fd99 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -40,6 +40,8 @@ # Import from pygit2 from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT +from pygit2 import GIT_MERGE_ANALYSIS_NONE, GIT_MERGE_ANALYSIS_NORMAL, GIT_MERGE_ANALYSIS_UP_TO_DATE +from pygit2 import GIT_MERGE_ANALYSIS_FASTFORWARD, GIT_MERGE_ANALYSIS_UNBORN from pygit2 import init_repository, clone_repository, discover_repository from pygit2 import Oid, Reference, hashfile import pygit2 @@ -308,57 +310,50 @@ class RepositoryTest_III(utils.RepoTestCaseForMerging): def test_merge_none(self): self.assertRaises(TypeError, self.repo.merge, None) - def test_merge_uptodate(self): + def test_merge_analysis_uptodate(self): branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533' - branch_oid = self.repo.get(branch_head_hex).id - merge_result = self.repo.merge(branch_oid) - self.assertTrue(merge_result.is_uptodate) - self.assertFalse(merge_result.is_fastforward) - self.assertEqual(None, merge_result.fastforward_id) + branch_id = self.repo.get(branch_head_hex).id + analysis = self.repo.merge_analysis(branch_id) + + self.assertTrue(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) + self.assertFalse(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) self.assertEqual({}, self.repo.status()) - def test_merge_fastforward(self): + def test_merge_analysis_fastforward(self): branch_head_hex = 'e97b4cfd5db0fb4ebabf4f203979ca4e5d1c7c87' - branch_oid = self.repo.get(branch_head_hex).id - merge_result = self.repo.merge(branch_oid) - self.assertFalse(merge_result.is_uptodate) - self.assertTrue(merge_result.is_fastforward) - # Asking twice to assure the reference counting is correct - self.assertEqual(branch_head_hex, merge_result.fastforward_id.hex) - self.assertEqual(branch_head_hex, merge_result.fastforward_id.hex) + branch_id = self.repo.get(branch_head_hex).id + analysis = self.repo.merge_analysis(branch_id) + self.assertFalse(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) + self.assertTrue(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) self.assertEqual({}, self.repo.status()) def test_merge_no_fastforward_no_conflicts(self): branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' - branch_oid = self.repo.get(branch_head_hex).id - merge_result = self.repo.merge(branch_oid) - self.assertFalse(merge_result.is_uptodate) - self.assertFalse(merge_result.is_fastforward) + branch_id = self.repo.get(branch_head_hex).id + analysis= self.repo.merge_analysis(branch_id) + self.assertFalse(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) + self.assertFalse(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) # Asking twice to assure the reference counting is correct - self.assertEqual(None, merge_result.fastforward_id) - self.assertEqual(None, merge_result.fastforward_id) - self.assertEqual({'bye.txt': 1}, self.repo.status()) - self.assertEqual({'bye.txt': 1}, self.repo.status()) - # Checking the index works as expected - self.repo.index.remove('bye.txt') - self.repo.index.write() - self.assertEqual({'bye.txt': 128}, self.repo.status()) + self.assertEqual({}, self.repo.status()) + self.assertEqual({}, self.repo.status()) def test_merge_no_fastforward_conflicts(self): branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' - branch_oid = self.repo.get(branch_head_hex).id - merge_result = self.repo.merge(branch_oid) - self.assertFalse(merge_result.is_uptodate) - self.assertFalse(merge_result.is_fastforward) + branch_id = self.repo.get(branch_head_hex).id + + analysis = self.repo.merge_analysis(branch_id) + self.assertFalse(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) + self.assertFalse(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) + + self.repo.merge(branch_id) + status = pygit2.GIT_STATUS_WT_NEW | pygit2.GIT_STATUS_INDEX_DELETED # Asking twice to assure the reference counting is correct - self.assertEqual(None, merge_result.fastforward_id) - self.assertEqual(None, merge_result.fastforward_id) - self.assertEqual({'.gitignore': 132}, self.repo.status()) - self.assertEqual({'.gitignore': 132}, self.repo.status()) + self.assertEqual({'.gitignore': status}, self.repo.status()) + self.assertEqual({'.gitignore': status}, self.repo.status()) # Checking the index works as expected self.repo.index.add('.gitignore') self.repo.index.write() - self.assertEqual({'.gitignore': 2}, self.repo.status()) + self.assertEqual({'.gitignore': pygit2.GIT_STATUS_INDEX_MODIFIED}, self.repo.status()) def test_merge_invalid_hex(self): branch_head_hex = '12345678' From 114e300b08f911d4923374e23ba67465c87edc01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 1 Apr 2014 19:56:10 +0200 Subject: [PATCH 0722/2237] Adjust to options changes We can now use a git_buf to extract the search path. --- src/options.c | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/src/options.c b/src/options.c index abfa4656a..1679a93d2 100644 --- a/src/options.c +++ b/src/options.c @@ -37,35 +37,16 @@ extern PyObject *GitError; static PyObject * get_search_path(long level) { - char *buf = NULL; - size_t len = 64; + git_buf buf = {NULL}; PyObject *py_path; - int error; - - do { - len *= 2; - char *tmp = realloc(buf, len); - if (!tmp) { - free(buf); - PyErr_NoMemory(); - return NULL; - } - buf = tmp; - - error = git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, level, buf, len); - } while(error == GIT_EBUFS); + int err; - if (error < 0) { - free(buf); - Error_set(error); - return NULL; - } - - if (!buf) - return NULL; + err = git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, level, &buf); + if (err < 0) + return Error_set(err); - py_path = to_unicode(buf, NULL, NULL); - free(buf); + py_path = to_unicode(buf.ptr, NULL, NULL); + git_buf_free(&buf); if (!py_path) return NULL; From d882af8f52fadfdc35a98daaac8bf0a2fb8e155f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 2 Apr 2014 22:28:18 +0200 Subject: [PATCH 0723/2237] Get ready to release 0.20.3 --- README.rst | 46 ++++++++++++++++++++++++---------------------- docs/conf.py | 2 +- pygit2/version.py | 2 +- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/README.rst b/README.rst index 272b149dd..04c436622 100644 --- a/README.rst +++ b/README.rst @@ -44,33 +44,35 @@ for the topic), send a pull request. Authors ============== -57 developers have contributed at least 1 commit to pygit2:: - - J. David Ibáñez Brodie Rao Adam Spiers - Nico von Geyso David Versmisse Alexander Bayandin - Carlos Martín Nieto Rémi Duraffort Andrew Chin - W. Trevor King Sebastian Thiel András Veres-Szentkirályi - Dave Borowitz Fraser Tweedale Benjamin Kircher - Daniel Rodríguez Troitiño Han-Wen Nienhuys Benjamin Pollack - Richo Healey Petr Viktorin Bryan O'Sullivan - Christian Boos Alex Chamberlain David Fischer - Julien Miotte Amit Bakshi David Sanders - Xu Tao Andrey Devyatkin Eric Davis - Jose Plana Ben Davis Erik van Zijst - Martin Lenders Eric Schrijver Ferengee - Petr Hosek Hervé Cauwelier Gustavo Di Pietro - Victor Garcia Huang Huang Hugh Cole-Baker - Xavier Delannoy Jared Flatow Josh Bleecher Snyder - Yonggang Luo Jiunn Haur Lim Jun Omae - Valentin Haenel Sarath Lakshman Óscar San José - Bernardo Heynemann Vicent Marti Ridge Kennedy - John Szakmeister Zoran Zaric Rui Abreu Ferreira +62 developers have contributed at least 1 commit to pygit2:: + + J. David Ibáñez Rémi Duraffort András Veres-Szentkirályi + Nico von Geyso Sebastian Thiel Benjamin Kircher + Carlos Martín Nieto Fraser Tweedale Benjamin Pollack + W. Trevor King Han-Wen Nienhuys Bryan O'Sullivan + Dave Borowitz Leonardo Rhodes David Fischer + Daniel Rodríguez Troitiño Petr Viktorin David Sanders + Richo Healey Alex Chamberlain Devaev Maxim + Christian Boos Amit Bakshi Eric Davis + Julien Miotte Andrey Devyatkin Erik Meusel + Xu Tao Ben Davis Erik van Zijst + Jose Plana Eric Schrijver Ferengee + Martin Lenders Hervé Cauwelier Gustavo Di Pietro + Petr Hosek Huang Huang Hugh Cole-Baker + Victor Garcia Jared Flatow Josh Bleecher Snyder + Xavier Delannoy Jiunn Haur Lim Jun Omae + Yonggang Luo Sarath Lakshman Óscar San José + Valentin Haenel Vicent Marti Ridge Kennedy + Bernardo Heynemann Zoran Zaric Rui Abreu Ferreira + John Szakmeister Adam Spiers Thomas Kluyver + Brodie Rao Alexander Bayandin earl + David Versmisse Andrew Chin Changelog ============== -0.20.3 (2014-04-XX) +0.20.3 (2014-04-02) ------------------- - A number of memory issues fixed diff --git a/docs/conf.py b/docs/conf.py index d7f2799c3..c432057f7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,7 +50,7 @@ # The short X.Y version. version = '0.20' # The full version, including alpha/beta/rc tags. -release = '0.20.2' +release = '0.20.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/pygit2/version.py b/pygit2/version.py index af8676feb..e68ec8dc5 100644 --- a/pygit2/version.py +++ b/pygit2/version.py @@ -23,4 +23,4 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -__version__ = '0.20.2' +__version__ = '0.20.3' From c76c3f0195aa8c3437db959e0780157931f006a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 11 Apr 2014 23:54:31 +0200 Subject: [PATCH 0724/2237] Start implementing remotes with CFFI This moves enough code into python with CFFI to pass the test_remotes unit tests. There is no credentials support yet. There is a small change in the return value of Remote.fetch() in that we now return a TransferProgress object instead of extracting a few values into a dictionary. --- .gitignore | 1 + pygit2/__init__.py | 1 + pygit2/decl.h | 127 ++++++++++++++++++ pygit2/errors.py | 55 ++++++++ pygit2/ffi.py | 106 +++++++++++++++ pygit2/refspec.py | 110 ++++++++++++++++ pygit2/remote.py | 207 ++++++++++++++++++++++++++++++ pygit2/repository.py | 48 +++++++ src/refspec.c | 297 ------------------------------------------- src/repository.c | 77 ++--------- test/test_remote.py | 12 +- 11 files changed, 675 insertions(+), 366 deletions(-) create mode 100644 pygit2/decl.h create mode 100644 pygit2/errors.py create mode 100644 pygit2/ffi.py create mode 100644 pygit2/refspec.py create mode 100644 pygit2/remote.py delete mode 100644 src/refspec.c diff --git a/.gitignore b/.gitignore index d8022420b..79889fed3 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ pygit2/__pycache__ *.egg-info *.swp docs/_build +__pycache__ diff --git a/pygit2/__init__.py b/pygit2/__init__.py index fc2e69366..396af196a 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -37,6 +37,7 @@ from .version import __version__ from .settings import Settings from .credentials import * +from .remote import Remote def init_repository(path, bare=False): """ diff --git a/pygit2/decl.h b/pygit2/decl.h new file mode 100644 index 000000000..e0ad82622 --- /dev/null +++ b/pygit2/decl.h @@ -0,0 +1,127 @@ +typedef ... git_repository; +typedef ... git_remote; +typedef ... git_refspec; +typedef ... git_push; +typedef ... git_cred; +typedef ... git_oid; + +typedef struct git_strarray { + char **strings; + size_t count; +} git_strarray; + +typedef enum { + GIT_OK = 0, + GIT_ERROR = -1, + GIT_ENOTFOUND = -3, + GIT_EEXISTS = -4, + GIT_EAMBIGUOUS = -5, + GIT_EBUFS = -6, + GIT_EUSER = -7, + GIT_EBAREREPO = -8, + GIT_EUNBORNBRANCH = -9, + GIT_EUNMERGED = -10, + GIT_ENONFASTFORWARD = -11, + GIT_EINVALIDSPEC = -12, + GIT_EMERGECONFLICT = -13, + GIT_ELOCKED = -14, + + GIT_PASSTHROUGH = -30, + GIT_ITEROVER = -31, +} git_error_code; + +typedef struct { + char *message; + int klass; +} git_error; + +const git_error * giterr_last(void); + +void git_strarray_free(git_strarray *array); + +typedef struct git_transfer_progress { + unsigned int total_objects; + unsigned int indexed_objects; + unsigned int received_objects; + unsigned int local_objects; + unsigned int total_deltas; + unsigned int indexed_deltas; + size_t received_bytes; +} git_transfer_progress; + +typedef enum git_remote_completion_type { + GIT_REMOTE_COMPLETION_DOWNLOAD, + GIT_REMOTE_COMPLETION_INDEXING, + GIT_REMOTE_COMPLETION_ERROR, +} git_remote_completion_type; + +typedef enum { + GIT_DIRECTION_FETCH = 0, + GIT_DIRECTION_PUSH = 1 +} git_direction; + +typedef struct git_remote_callbacks { + unsigned int version; + int (*progress)(const char *str, int len, void *data); + int (*completion)(git_remote_completion_type type, void *data); + int (*credentials)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data); + int (*transfer_progress)(const git_transfer_progress *stats, void *data); + int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data); + void *payload; +} git_remote_callbacks ; + +int git_remote_list(git_strarray *out, git_repository *repo); +int git_remote_load(git_remote **out, git_repository *repo, const char *name); +int git_remote_create(git_remote **out, + git_repository *repo, + const char *name, + const char *url); +const char * git_remote_name(const git_remote *remote); +typedef int (*git_remote_rename_problem_cb)(const char *problematic_refspec, void *payload); +int git_remote_rename(git_remote *remote, + const char *new_name, + git_remote_rename_problem_cb callback, + void *payload); +const char * git_remote_url(const git_remote *remote); +int git_remote_set_url(git_remote *remote, const char* url); +const char * git_remote_pushurl(const git_remote *remote); +int git_remote_set_pushurl(git_remote *remote, const char* url); +int git_remote_fetch(git_remote *remote); +const git_transfer_progress * git_remote_stats(git_remote *remote); +int git_remote_add_push(git_remote *remote, const char *refspec); +int git_remote_add_fetch(git_remote *remote, const char *refspec); +int git_remote_save(const git_remote *remote); +int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks); +size_t git_remote_refspec_count(git_remote *remote); +const git_refspec * git_remote_get_refspec(git_remote *remote, size_t n); + +int git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote); +int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array); +int git_remote_get_push_refspecs(git_strarray *array, git_remote *remote); +int git_remote_set_push_refspecs(git_remote *remote, git_strarray *array); + +void git_remote_free(git_remote *remote); + +int git_push_new(git_push **push, git_remote *remote); +int git_push_add_refspec(git_push *push, const char *refspec); +int git_push_finish(git_push *push); +int git_push_unpack_ok(git_push *push); + +int git_push_status_foreach(git_push *push, + int (*cb)(const char *ref, const char *msg, void *data), + void *data); + +int git_push_update_tips(git_push *push); +void git_push_free(git_push *push); + +const char * git_refspec_src(const git_refspec *refspec); +const char * git_refspec_dst(const git_refspec *refspec); +int git_refspec_force(const git_refspec *refspec); +const char * git_refspec_string(const git_refspec *refspec); +git_direction git_refspec_direction(const git_refspec *spec); + +int git_refspec_src_matches(const git_refspec *refspec, const char *refname); +int git_refspec_dst_matches(const git_refspec *refspec, const char *refname); + +int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name); +int git_refspec_rtransform(char *out, size_t outlen, const git_refspec *spec, const char *name); diff --git a/pygit2/errors.py b/pygit2/errors.py new file mode 100644 index 000000000..b9f6c9bfa --- /dev/null +++ b/pygit2/errors.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2010-2014 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. + +# Import from the Standard Library +from string import hexdigits + +# ffi +from .ffi import ffi, C + +from _pygit2 import GitError + +def check_error(err): + if err >= 0: + return + + message = "(no message provided)" + giterr = C.giterr_last() + if giterr != ffi.NULL: + message = ffi.string(giterr.message).decode() + + if err in [C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EEXISTS, C.GIT_EAMBIGUOUS]: + raise ValueError(message) + elif err == C.GIT_ENOTFOUND: + raise KeyError(message) + elif err == C.GIT_EINVALIDSPEC: + raise ValueError(message) + elif err == C.GIT_ITEROVER: + raise StopIteration() + + raise GitError(message) + diff --git a/pygit2/ffi.py b/pygit2/ffi.py new file mode 100644 index 000000000..ce25ff0ea --- /dev/null +++ b/pygit2/ffi.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2010-2014 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. + +# Import from the future +from __future__ import absolute_import + +import inspect +from os import path +from cffi import FFI +import sys + +if sys.version_info.major < 3: + def to_str(s, encoding='utf-8', errors='strict'): + if s == ffi.NULL: + return ffi.NULL + encoding = encoding or 'utf-8' + if isinstance(s, unicode): + return s.encode(encoding, errors) + + return s +else: + def to_str(s, encoding='utf-8', errors='strict'): + if isinstance(s, bytes): + return s + else: + return bytes(s, encoding, errors) + +if sys.version_info.major < 3: + def is_string(s): + return isinstance(s, str) +else: + def is_string(s): + return isinstance(s, basestring) + +ffi = FFI() + +def strarray_to_strings(arr): + l = [None] * arr.count + for i in range(arr.count): + l[i] = ffi.string(arr.strings[i]).decode() + + return l + +def strings_to_strarray(l): + """Convert a list of strings to a git_strarray + + We return first the git_strarray* you can pass to libgit2 and a + list of references to the memory, which we must keep around for as + long as the git_strarray must live. + """ + + if not isinstance(l, list): + raise TypeError("Value must be a list") + + arr = ffi.new('git_strarray *') + strings = ffi.new('char *[]', len(l)) + + # We need refs in order to keep a reference to the value returned + # by the ffi.new(). Otherwise, they will be freed and the memory + # re-used, with less than great consequences. + refs = [None] * len(l) + + for i in range(len(l)): + if not is_string(l[i]): + raise TypeError("Value must be a string") + + s = ffi.new('char []', l[i]) + refs[i] = s + strings[i] = s + + arr.strings = strings + arr.count = len(l) + + return arr, refs + +dir_path = path.dirname(path.abspath(inspect.getfile(inspect.currentframe()))) + +decl_path = path.join(dir_path, 'decl.h') +with open(decl_path, 'rb') as f: + ffi.cdef(f.read()) + +C = ffi.verify("#include ", libraries=["git2"]) diff --git a/pygit2/refspec.py b/pygit2/refspec.py new file mode 100644 index 000000000..00974495d --- /dev/null +++ b/pygit2/refspec.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2010-2014 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. + +# Import from the future +from __future__ import absolute_import + +from .ffi import ffi, C, to_str +from .errors import check_error + +class Refspec(object): + def __init__(self, owner, ptr): + self._owner = owner + self._refspec = ptr + + @property + def src(self): + """Source or lhs of the refspec""" + return ffi.string(C.git_refspec_src(self._refspec)).decode() + + @property + def dst(self): + """Destinaton or rhs of the refspec""" + return ffi.string(C.git_refspec_dst(self._refspec)).decode() + + @property + def force(self): + """Whether this refspeca llows non-fast-forward updates""" + return bool(C.git_refspec_force(self._refspec)) + + @property + def string(self): + """String which was used to create this refspec""" + return ffi.string(C.git_refspec_string(self._refspec)).decode() + + @property + def direction(self): + """Direction of this refspec (fetch or push)""" + return C.git_refspec_direction(self._refspec) + + def src_matches(self, ref): + """src_matches(str) -> Bool + + Returns whether the given string matches the source of this refspec""" + return bool(C.git_refspec_src_matches(self._refspec, to_str(ref))) + + def dst_matches(self, ref): + """dst_matches(str) -> Bool + + Returns whether the given string matches the destination of this refspec""" + return bool(C.git_refspec_dst_matches(self._refspec, to_str(ref))) + + def transform(self, ref): + """transform(str) -> str + + Transform a reference name according to this refspec from the lhs to the rhs.""" + alen = len(ref) + err = C.GIT_EBUFS + ptr = None + ref_str = to_str(ref) + + while err == C.GIT_EBUFS: + alen *= 2 + ptr = ffi.new('char []', alen) + + err = C.git_refspec_transform(ptr, alen, self._refspec, ref_str) + + check_error(err) + return ffi.string(ptr).decode() + + def rtransform(self, ref): + """transform(str) -> str + + Transform a reference name according to this refspec from the lhs to the rhs""" + alen = len(ref) + err = C.GIT_EBUFS + ptr = None + ref_str = to_str(ref) + + while err == C.GIT_EBUFS: + alen *= 2 + ptr = ffi.new('char []', alen) + + err = C.git_refspec_rtransform(ptr, alen, self._refspec, ref_str) + + check_error(err) + return ffi.string(ptr).decode() diff --git a/pygit2/remote.py b/pygit2/remote.py new file mode 100644 index 000000000..28a4860b3 --- /dev/null +++ b/pygit2/remote.py @@ -0,0 +1,207 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2010-2014 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. + +# Import from the future +from __future__ import absolute_import + +from .ffi import ffi, C, to_str, strarray_to_strings, strings_to_strarray +from .errors import check_error, GitError +from .refspec import Refspec + +def maybe_string(ptr): + if not ptr: + return None + + return ffi.string(ptr).decode() + + +class TransferProgress(object): + """Progress downloading and indexing data during a fetch""" + + def __init__(self, tp): + self.total_objects = tp.total_objects + self.indexed_objects = tp.indexed_objects + self.received_objects = tp.received_objects + self.local_objects = tp.local_objects + self.total_deltas = tp.total_deltas + self.indexed_deltas = tp.indexed_deltas + self.received_bytes = tp.received_bytes + +class Remote(object): + def __init__(self, repo, ptr): + """The constructor is for internal use only""" + + self._repo = repo + self._remote = ptr + + # Build the callback structure + callbacks = ffi.new('git_remote_callbacks *') + callbacks.version = 1 + callbacks.transfer_progress = self._transfer_progress_cb + # We need to make sure that this handle stays alive + self._self_handle = ffi.new_handle(self) + callbacks.payload = self._self_handle + + err = C.git_remote_set_callbacks(self._remote, callbacks) + check_error(err) + + def __del__(self): + C.git_remote_free(self._remote) + + @property + def name(self): + return maybe_string(C.git_remote_name(self._remote)) + + @name.setter + def name(self, value): + err = C.git_remote_rename(self._remote, to_str(value), ffi.NULL, ffi.NULL) + check_error(err) + + @property + def url(self): + return maybe_string(C.git_remote_url(self._remote)) + + @url.setter + def url(self, value): + err = C.git_remote_set_url(self._remote, to_str(value)) + + @property + def push_url(self): + return maybe_string(C.git_remote_pushurl(self._remote)) + + @push_url.setter + def push_url(self, value): + err = C.git_remote_set_pushurl(self._remote, to_str(value)) + check_error(err) + + def save(self): + err = C.git_remote_save(self._remote) + check_error(err) + + def fetch(self): + err = C.git_remote_fetch(self._remote) + if err == C.GIT_EUSER: + raise self._stored_exception + + check_error(err) + + return TransferProgress(C.git_remote_stats(self._remote)) + + @property + def refspec_count(self): + return C.git_remote_refspec_count(self._remote) + + def get_refspec(self, n): + spec = C.git_remote_get_refspec(self._remote, n) + return Refspec(self, spec) + + @property + def fetch_refspecs(self): + specs = ffi.new('git_strarray *') + err = C.git_remote_get_fetch_refspecs(specs, self._remote) + check_error(err) + + return strarray_to_strings(specs) + + @fetch_refspecs.setter + def fetch_refspecs(self, l): + arr, refs = strings_to_strarray(l) + err = C.git_remote_set_fetch_refspecs(self._remote, arr) + check_error(err) + + @property + def push_refspecs(self): + specs = ffi.new('git_strarray *') + err = C.git_remote_get_push_refspecs(specs, self._remote) + check_error(err) + + return strarray_to_strings(specs) + + @push_refspecs.setter + def push_refspecs(self, l): + arr, refs = strings_to_strarray(l) + err = C.git_remote_set_push_refspecs(self._remote, arr) + check_error(err) + + def add_fetch(self, spec): + err = C.git_remote_add_fetch(self._remote, to_str(spec)) + + def add_push(self, spec): + err = C.git_remote_add_push(self._remote, to_str(spec)) + + @ffi.callback("int (*cb)(const char *ref, const char *msg, void *data)") + def _push_cb(ref, msg, data): + self = ffi.from_handle(data) + if msg: + self._bad_message = ffi.string(msg).decode() + return 0 + + def push(self, spec): + cpush = ffi.new('git_push **') + err = C.git_push_new(cpush, self._remote) + check_error(err) + + push = cpush[0] + + try: + err = C.git_push_add_refspec(push, to_str(spec)) + check_error(err) + + err = C.git_push_finish(push) + check_error(err) + + if not C.git_push_unpack_ok(push): + raise GitError("remote failed to unpack objects") + + err = C.git_push_status_foreach(push, self._push_cb, ffi.new_handle(self)) + check_error(err) + + if hasattr(self, '_bad_message'): + raise GitError(self._bad_message) + + err = C.git_push_update_tips(push) + check_error(err) + + finally: + C.git_push_free(push) + + # These functions exist to be called by the git_remote as + # callbacks. They proxy the call to whatever the user set + + @ffi.callback('int (*transfer_progress)(const git_transfer_progress *stats, void *data)') + def _transfer_progress_cb(stats_ptr, data): + self = ffi.from_handle(data) + if not hasattr(self, 'transfer_progress'): + return 0 + + try: + self.transfer_progress(TransferProgress(stats_ptr)) + except Exception, e: + self._stored_exception = e + return C.GIT_EUSER + + return 0 diff --git a/pygit2/repository.py b/pygit2/repository.py index e6c750f31..a22c8204c 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -35,6 +35,9 @@ from _pygit2 import GIT_CHECKOUT_SAFE_CREATE, GIT_DIFF_NORMAL from _pygit2 import Reference, Tree, Commit, Blob +from .ffi import ffi, C, to_str +from .errors import check_error +from .remote import Remote class Repository(_Repository): @@ -59,6 +62,51 @@ def __contains__(self, key): def __repr__(self): return "pygit2.Repository(%r)" % self.path + + # + # Remotes + # + def create_remote(self, name, url): + """create_remote(name, url) -> Remote + + Creates a new remote. + """ + + repo_cptr = ffi.new('git_repository **') + repo_cptr[0] = ffi.cast('git_repository *', self._pointer) + cremote = ffi.new('git_remote **') + + repo = repo_cptr[0] + err = C.git_remote_create(cremote, repo, to_str(name), to_str(url)) + check_error(err) + + return Remote(repo, cremote[0]) + + @property + def remotes(self): + """Returns all configured remotes""" + + repo_cptr = ffi.new('git_repository **') + repo_cptr[0] = ffi.cast('git_repository *', self._pointer) + names = ffi.new('git_strarray *') + + repo = repo_cptr[0] + try: + err = C.git_remote_list(names, repo) + check_error(err) + + l = [None] * names.count + cremote = ffi.new('git_remote **') + for i in range(names.count): + err = C.git_remote_load(cremote, repo, names.strings[i]) + check_error(err) + + l[i] = Remote(repo, cremote[0]) + return l + finally: + C.git_strarray_free(names) + + # # References # diff --git a/src/refspec.c b/src/refspec.c deleted file mode 100644 index 3053fe60b..000000000 --- a/src/refspec.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright 2010-2014 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 "error.h" -#include "types.h" -#include "utils.h" -#include "refspec.h" - - -extern PyTypeObject RefspecType; - -Refspec * -wrap_refspec(const Remote *owner, const git_refspec *refspec) -{ - Refspec *spec; - - spec = PyObject_New(Refspec, &RefspecType); - if (!spec) - return NULL; - - Py_INCREF(owner); - spec->owner = owner; - spec->refspec = refspec; - - return spec; -} - -PyDoc_STRVAR(Refspec_direction__doc__, - "The direction of this refspec (fetch or push)"); - -PyObject * -Refspec_direction__get__(Refspec *self) -{ - return Py_BuildValue("i", git_refspec_direction(self->refspec)); -} - -PyDoc_STRVAR(Refspec_src__doc__, "Source or lhs of the refspec"); - -PyObject * -Refspec_src__get__(Refspec *self) -{ - return to_unicode(git_refspec_src(self->refspec), NULL, NULL); -} - -PyDoc_STRVAR(Refspec_dst__doc__, "Destination or rhs of the refspec"); - -PyObject * -Refspec_dst__get__(Refspec *self) -{ - return to_unicode(git_refspec_dst(self->refspec), NULL, NULL); -} - -PyDoc_STRVAR(Refspec_string__doc__, "String used to create this refspec"); - -PyObject * -Refspec_string__get__(Refspec *self) -{ - return to_unicode(git_refspec_string(self->refspec), NULL, NULL); -} - -PyDoc_STRVAR(Refspec_force__doc__, - "Whether this refspec allows non-fast-forward updates"); - -PyObject * -Refspec_force__get__(Refspec *self) -{ - if (git_refspec_force(self->refspec)) - Py_RETURN_TRUE; - - Py_RETURN_FALSE; -} - -PyDoc_STRVAR(Refspec_src_matches__doc__, - "src_matches(str) -> Bool\n" - "\n" - "Returns whether the string matches the source refspec\n"); - -PyObject * -Refspec_src_matches(Refspec *self, PyObject *py_str) -{ - const char *str; - PyObject *tstr; - int res; - - str = py_str_borrow_c_str(&tstr, py_str, NULL); - if (!str) - return NULL; - - res = git_refspec_src_matches(self->refspec, str); - Py_DECREF(tstr); - - if (res) - Py_RETURN_TRUE; - - Py_RETURN_FALSE; -} - -PyDoc_STRVAR(Refspec_dst_matches__doc__, - "dst_matches(str) -> Bool\n" - "\n" - "Returns whether the string matches the destination refspec\n"); - -PyObject * -Refspec_dst_matches(Refspec *self, PyObject *py_str) -{ - const char *str; - PyObject *tstr; - int res; - - str = py_str_borrow_c_str(&tstr, py_str, NULL); - if (!str) - return NULL; - - res = git_refspec_dst_matches(self->refspec, str); - Py_DECREF(tstr); - - if (res) - Py_RETURN_TRUE; - - Py_RETURN_FALSE; -} - -PyDoc_STRVAR(Refspec_transform__doc__, - "transform(str) -> str\n" - "\n" - "Transform a reference according to the refspec\n"); - -PyObject * -Refspec_transform(Refspec *self, PyObject *py_str) -{ - const char *str; - char *trans; - int err, len, alen; - PyObject *py_trans, *tstr; - - str = py_str_borrow_c_str(&tstr, py_str, NULL); - alen = len = strlen(str); - - do { - alen *= alen; - trans = malloc(alen); - if (!trans) { - Py_DECREF(tstr); - return PyErr_NoMemory(); - } - - err = git_refspec_transform(trans, alen, self->refspec, str); - } while(err == GIT_EBUFS); - Py_DECREF(tstr); - - if (err < 0) { - free(trans); - Error_set(err); - return NULL; - } - - py_trans = to_unicode(trans, NULL, NULL); - - free(trans); - - return py_trans; -} - -PyDoc_STRVAR(Refspec_rtransform__doc__, - "rtransform(str) -> str\n" - "\n" - "Transform a reference according to the refspec in reverse\n"); - -PyObject * -Refspec_rtransform(Refspec *self, PyObject *py_str) -{ - const char *str; - char *trans; - int err, len, alen; - PyObject *py_trans, *tstr; - - str = py_str_borrow_c_str(&tstr, py_str, NULL); - alen = len = strlen(str); - - do { - alen *= alen; - trans = malloc(alen); - if (!trans) { - Py_DECREF(tstr); - return PyErr_NoMemory(); - } - - err = git_refspec_rtransform(trans, alen, self->refspec, str); - } while(err == GIT_EBUFS); - Py_DECREF(tstr); - - if (err < 0) { - free(trans); - Error_set(err); - return NULL; - } - - py_trans = to_unicode(trans, NULL, NULL); - - free(trans); - - return py_trans; -} - -PyMethodDef Refspec_methods[] = { - METHOD(Refspec, src_matches, METH_O), - METHOD(Refspec, dst_matches, METH_O), - METHOD(Refspec, transform, METH_O), - METHOD(Refspec, rtransform, METH_O), - {NULL} -}; - -PyGetSetDef Refspec_getseters[] = { - GETTER(Refspec, direction), - GETTER(Refspec, src), - GETTER(Refspec, dst), - GETTER(Refspec, string), - GETTER(Refspec, force), - {NULL} -}; - -static void -Refspec_dealloc(Refspec *self) -{ - Py_CLEAR(self->owner); - PyObject_Del(self); -} - -PyDoc_STRVAR(Refspec__doc__, "Refspec object."); - -PyTypeObject RefspecType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Refspec", /* tp_name */ - sizeof(Refspec), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Refspec_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - Refspec__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Refspec_methods, /* tp_methods */ - 0, /* tp_members */ - Refspec_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 bb2a11b96..14e07a187 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1277,67 +1277,6 @@ Repository_TreeBuilder(Repository *self, PyObject *args) return (PyObject*)builder; } - -PyDoc_STRVAR(Repository_create_remote__doc__, - "create_remote(name, url) -> Remote\n" - "\n" - "Creates a new remote."); - -PyObject * -Repository_create_remote(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 (PyObject*) wrap_remote(remote, self); -} - - -PyDoc_STRVAR(Repository_remotes__doc__, "Returns all configured remotes."); - -PyObject * -Repository_remotes__get__(Repository *self) -{ - git_strarray remotes; - git_remote *remote = NULL; - PyObject *py_list = NULL; - PyObject *py_remote = NULL; - size_t i; - int err; - - git_remote_list(&remotes, self->repo); - - py_list = PyList_New(remotes.count); - for (i=0; i < remotes.count; ++i) { - err = git_remote_load(&remote, self->repo, remotes.strings[i]); - if (err < 0) - goto cleanup; - py_remote = wrap_remote(remote, self); - if (py_remote == NULL) - goto cleanup; - PyList_SetItem(py_list, i, py_remote); - } - - git_strarray_free(&remotes); - return (PyObject*) py_list; - -cleanup: - git_strarray_free(&remotes); - if (py_list) - Py_DECREF(py_list); - if (err < 0) - return Error_set(err); - return NULL; -} - PyDoc_STRVAR(Repository_default_signature__doc__, "Return the signature according to the repository's configuration"); PyObject * @@ -1352,6 +1291,19 @@ Repository_default_signature__get__(Repository *self) return build_signature(NULL, sig, "utf-8"); } +PyDoc_STRVAR(Repository__pointer__doc__, "Get the repo's pointer. For internal use only."); +PyObject * +Repository__pointer__get__(Repository *self) +{ + /* + * This is pretty bad. We shouldn't be casting a pointer into an + * integer, but we can't access the contents of a PyCapsule from + * python code, which we need to do in order to get a type that + * cffi likes. + */ + return PyLong_FromLongLong((long long) self->repo); +} + PyDoc_STRVAR(Repository_checkout_head__doc__, "checkout_head(strategy)\n" "\n" @@ -1633,7 +1585,6 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, revparse_single, METH_O), METHOD(Repository, status, METH_NOARGS), METHOD(Repository, status_file, METH_O), - METHOD(Repository, create_remote, METH_VARARGS), METHOD(Repository, checkout_head, METH_VARARGS), METHOD(Repository, checkout_index, METH_VARARGS), METHOD(Repository, checkout_tree, METH_VARARGS), @@ -1659,8 +1610,8 @@ PyGetSetDef Repository_getseters[] = { GETTER(Repository, is_bare), GETTER(Repository, config), GETTER(Repository, workdir), - GETTER(Repository, remotes), GETTER(Repository, default_signature), + GETTER(Repository, _pointer), {NULL} }; diff --git a/test/test_remote.py b/test/test_remote.py index 797474c62..54ba27c9e 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -189,9 +189,9 @@ class EmptyRepositoryTest(utils.EmptyRepoTestCase): def test_fetch(self): remote = self.repo.remotes[0] stats = remote.fetch() - self.assertEqual(stats['received_bytes'], REMOTE_REPO_BYTES) - self.assertEqual(stats['indexed_objects'], REMOTE_REPO_OBJECTS) - self.assertEqual(stats['received_objects'], REMOTE_REPO_OBJECTS) + self.assertEqual(stats.received_bytes, REMOTE_REPO_BYTES) + self.assertEqual(stats.indexed_objects, REMOTE_REPO_OBJECTS) + self.assertEqual(stats.received_objects, REMOTE_REPO_OBJECTS) def test_transfer_progress(self): self.tp = None @@ -201,9 +201,9 @@ def tp_cb(stats): remote = self.repo.remotes[0] remote.transfer_progress = tp_cb stats = remote.fetch() - self.assertEqual(stats['received_bytes'], self.tp.received_bytes) - self.assertEqual(stats['indexed_objects'], self.tp.indexed_objects) - self.assertEqual(stats['received_objects'], self.tp.received_objects) + self.assertEqual(stats.received_bytes, self.tp.received_bytes) + self.assertEqual(stats.indexed_objects, self.tp.indexed_objects) + self.assertEqual(stats.received_objects, self.tp.received_objects) def test_update_tips(self): remote = self.repo.remotes[0] From 4ef3be18cce6b0645a4bb8d00c4976f00c6f3af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 12 Apr 2014 17:13:15 +0200 Subject: [PATCH 0725/2237] Remote: support credentials via CFFI --- pygit2/decl.h | 18 ++++++++++++++++++ pygit2/remote.py | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/pygit2/decl.h b/pygit2/decl.h index e0ad82622..9c4e05270 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -60,6 +60,13 @@ typedef enum { GIT_DIRECTION_PUSH = 1 } git_direction; +typedef enum { + GIT_CREDTYPE_USERPASS_PLAINTEXT = ..., + GIT_CREDTYPE_SSH_KEY = ..., + GIT_CREDTYPE_SSH_CUSTOM = ..., + GIT_CREDTYPE_DEFAULT = ..., +} git_credtype_t; + typedef struct git_remote_callbacks { unsigned int version; int (*progress)(const char *str, int len, void *data); @@ -125,3 +132,14 @@ int git_refspec_dst_matches(const git_refspec *refspec, const char *refname); int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name); int git_refspec_rtransform(char *out, size_t outlen, const git_refspec *spec, const char *name); + +int git_cred_userpass_plaintext_new( + git_cred **out, + const char *username, + const char *password); +int git_cred_ssh_key_new( + git_cred **out, + const char *username, + const char *publickey, + const char *privatekey, + const char *passphrase); diff --git a/pygit2/remote.py b/pygit2/remote.py index 28a4860b3..eca3dfc1c 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -57,11 +57,13 @@ def __init__(self, repo, ptr): self._repo = repo self._remote = ptr + self._stored_exception = None # Build the callback structure callbacks = ffi.new('git_remote_callbacks *') callbacks.version = 1 callbacks.transfer_progress = self._transfer_progress_cb + callbacks.credentials = self._credentials_cb # We need to make sure that this handle stays alive self._self_handle = ffi.new_handle(self) callbacks.payload = self._self_handle @@ -103,8 +105,9 @@ def save(self): check_error(err) def fetch(self): + self._stored_exception = None err = C.git_remote_fetch(self._remote) - if err == C.GIT_EUSER: + if self._stored_exception: raise self._stored_exception check_error(err) @@ -205,3 +208,46 @@ def _transfer_progress_cb(stats_ptr, data): return C.GIT_EUSER return 0 + + @ffi.callback('int (*credentials)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data)') + def _credentials_cb(cred_out, url, username, allowed, data): + self = ffi.from_handle(data) + + if not hasattr(self, 'credentials'): + return 0 + + try: + url_str = maybe_string(url) + username_str = maybe_string(username) + + creds = self.credentials(url_str, username_str, allowed) + + if not hasattr(creds, 'credential_type') or not hasattr(creds, 'credential_tuple'): + raise TypeError("credential does not implement interface") + + cred_type = creds.credential_type + + if not (allowed & cred_type): + raise TypeError("invalid credential type") + + ccred = ffi.new('git_cred **') + if cred_type == C.GIT_CREDTYPE_USERPASS_PLAINTEXT: + name, passwd = creds.credential_tuple + err = C.git_cred_userpass_plaintext_new(ccred, to_str(name), to_str(passwd)) + + elif cred_type == C.GIT_CREDTYPE_SSH_KEY: + name, pubkey, privkey, passphrase = creds.credential_tuple + err = C.git_cred_ssh_key_new(ccred, to_str(name),to_str(pubkey), + to_str(privkey), to_str(passphrase)) + + else: + raise TypeError("unsupported credential type") + + check_error(err) + cred_out[0] = ccred[0] + + except Exception, e: + self._stored_exception = e + return C.GIT_EUSER + + return 0 From 2d1615dd29f496588fb894418d8a0ea3fd1cadb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 12 Apr 2014 17:33:15 +0200 Subject: [PATCH 0726/2237] Remote: add support for transfer and update_tips though CFFI --- pygit2/decl.h | 7 ++++++- pygit2/remote.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/pygit2/decl.h b/pygit2/decl.h index 9c4e05270..40680ce6c 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -3,7 +3,12 @@ typedef ... git_remote; typedef ... git_refspec; typedef ... git_push; typedef ... git_cred; -typedef ... git_oid; + +#define GIT_OID_RAWSZ ... + +typedef struct git_oid { + unsigned char id[20]; +} git_oid; typedef struct git_strarray { char **strings; diff --git a/pygit2/remote.py b/pygit2/remote.py index eca3dfc1c..d68c884da 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -28,6 +28,8 @@ # Import from the future from __future__ import absolute_import +from _pygit2 import Oid + from .ffi import ffi, C, to_str, strarray_to_strings, strings_to_strarray from .errors import check_error, GitError from .refspec import Refspec @@ -62,7 +64,9 @@ def __init__(self, repo, ptr): # Build the callback structure callbacks = ffi.new('git_remote_callbacks *') callbacks.version = 1 + callbacks.progress = self._progress_cb callbacks.transfer_progress = self._transfer_progress_cb + callbacks.update_tips = self._update_tips_cb callbacks.credentials = self._credentials_cb # We need to make sure that this handle stays alive self._self_handle = ffi.new_handle(self) @@ -209,6 +213,41 @@ def _transfer_progress_cb(stats_ptr, data): return 0 + @ffi.callback('int (*progress)(const char *str, int len, void *data)') + def _progress_cb(string, length, data): + self = ffi.from_handle(data) + + if not hasattr(self, 'progress'): + return 0 + + try: + s = ffi.string(string, length).decode() + self.progress(s) + except Exception, e: + self._stored_exception = e + return C.GIT_EUSER + + return 0 + + @ffi.callback('int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data)') + def _update_tips_cb(refname, a, b, data): + self = ffi.from_handle(data) + + if not hasattr(self, 'update_tips'): + return 0 + + try: + s = maybe_string(refname) + a = Oid(raw=bytes(ffi.buffer(a))) + b = Oid(raw=bytes(ffi.buffer(b))) + + self.update_tips(s, a, b) + except Exception, e: + self._stored_exception = e + return C.GIT_EUSER + + return 0 + @ffi.callback('int (*credentials)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data)') def _credentials_cb(cred_out, url, username, allowed, data): self = ffi.from_handle(data) From cf2703998e42c36c65dc2078377b693a8b41cd01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 12 Apr 2014 18:18:50 +0200 Subject: [PATCH 0727/2237] Remote: add documentation strings Now that it has the features of the old implementation, let's add documentation on how to use it. --- docs/remotes.rst | 6 +-- pygit2/remote.py | 104 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 103 insertions(+), 7 deletions(-) diff --git a/docs/remotes.rst b/docs/remotes.rst index 58ab8ef5d..445bef234 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -16,9 +16,9 @@ The Remote type .. autoattribute:: pygit2.Remote.refspec_count .. autoattribute:: pygit2.Remote.push_refspecs .. autoattribute:: pygit2.Remote.fetch_refspecs -.. autoattribute:: pygit2.Remote.progress -.. autoattribute:: pygit2.Remote.transfer_progress -.. autoattribute:: pygit2.Remote.update_tips +.. automethod:: pygit2.Remote.progress +.. automethod:: pygit2.Remote.transfer_progress +.. automethod:: pygit2.Remote.update_tips .. automethod:: pygit2.Remote.get_refspec .. automethod:: pygit2.Remote.fetch .. automethod:: pygit2.Remote.push diff --git a/pygit2/remote.py b/pygit2/remote.py index d68c884da..c7f122ca1 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -45,15 +45,73 @@ class TransferProgress(object): """Progress downloading and indexing data during a fetch""" def __init__(self, tp): + self.total_objects = tp.total_objects + """Total number objects to download""" + self.indexed_objects = tp.indexed_objects + """Objects which have been indexed""" + self.received_objects = tp.received_objects + """Objects which have been received up to now""" + self.local_objects = tp.local_objects + """Local objects which were used to fix the thin pack""" + self.total_deltas = tp.total_deltas + """Total number of deltas in the pack""" + self.indexed_deltas = tp.indexed_deltas + """Deltas which have been indexed""" + self.received_bytes = tp.received_bytes + """"Number of bytes received up to now""" class Remote(object): + + def progress(self, string): + """Progress output callback + + Override this function with your own progress reporting function + + :param str string: Progress otuput from the remote + """ + pass + + def credentials(self, url, username_from_url, allowed_types): + """Credentials callback + + If the remote server requires authentication, this function will + be called and its return value used for authentication. Override + it if you want to be able to perform authentication. + + :param str url: The url of the remote + :param username_from_url: Username extracted from the url, if any + :type username_from_url: str or None + :param int allowed_types: credential types supported by the remote + :rtype: credential + """ + pass + + def transfer_progress(self, stats): + """Transfer progress callback + + Override with your own function to report transfer progress. + + :param TransferProgress stats: The progress up to now + """ + pass + + def update_tips(self, refname, old, new): + """Update tips callabck + + Override with your own function to report reference updates + + :param str refname: the name of the reference that's being updated + :param Oid old: the reference's old value + :param Oid new: the reference's new value + """ + def __init__(self, repo, ptr): """The constructor is for internal use only""" @@ -80,6 +138,8 @@ def __del__(self): @property def name(self): + """Name of the remote""" + return maybe_string(C.git_remote_name(self._remote)) @name.setter @@ -89,6 +149,8 @@ def name(self, value): @property def url(self): + """Url of the remote""" + return maybe_string(C.git_remote_url(self._remote)) @url.setter @@ -97,6 +159,8 @@ def url(self, value): @property def push_url(self): + """Push url of the remote""" + return maybe_string(C.git_remote_pushurl(self._remote)) @push_url.setter @@ -105,10 +169,19 @@ def push_url(self, value): check_error(err) def save(self): + """save() + + Save a remote to its repository's configuration""" + err = C.git_remote_save(self._remote) check_error(err) def fetch(self): + """fetch() -> TransferProgress + + Perform a fetch against this remote. + """ + self._stored_exception = None err = C.git_remote_fetch(self._remote) if self._stored_exception: @@ -120,14 +193,22 @@ def fetch(self): @property def refspec_count(self): + """Total number of refspecs in this remote""" + return C.git_remote_refspec_count(self._remote) def get_refspec(self, n): + """get_refspec(n) -> Refspec + + Return the refspec at the given position + """ spec = C.git_remote_get_refspec(self._remote, n) return Refspec(self, spec) @property def fetch_refspecs(self): + """Refspecs that will be used for fetching""" + specs = ffi.new('git_strarray *') err = C.git_remote_get_fetch_refspecs(specs, self._remote) check_error(err) @@ -142,6 +223,8 @@ def fetch_refspecs(self, l): @property def push_refspecs(self): + """Refspecs that will be used for pushing""" + specs = ffi.new('git_strarray *') err = C.git_remote_get_push_refspecs(specs, self._remote) check_error(err) @@ -155,9 +238,17 @@ def push_refspecs(self, l): check_error(err) def add_fetch(self, spec): + """add_fetch(refspec) + + Add a fetch refspec to the remote""" + err = C.git_remote_add_fetch(self._remote, to_str(spec)) def add_push(self, spec): + """add_push(refspec) + + Add a push refspec to the remote""" + err = C.git_remote_add_push(self._remote, to_str(spec)) @ffi.callback("int (*cb)(const char *ref, const char *msg, void *data)") @@ -168,6 +259,10 @@ def _push_cb(ref, msg, data): return 0 def push(self, spec): + """push(refspec) + + Push the given refspec to the remote. Raises ``GitError`` on error""" + cpush = ffi.new('git_push **') err = C.git_push_new(cpush, self._remote) check_error(err) @@ -202,7 +297,8 @@ def push(self, spec): @ffi.callback('int (*transfer_progress)(const git_transfer_progress *stats, void *data)') def _transfer_progress_cb(stats_ptr, data): self = ffi.from_handle(data) - if not hasattr(self, 'transfer_progress'): + + if not hasattr(self, 'transfer_progress') or not self.transfer_progress: return 0 try: @@ -217,7 +313,7 @@ def _transfer_progress_cb(stats_ptr, data): def _progress_cb(string, length, data): self = ffi.from_handle(data) - if not hasattr(self, 'progress'): + if not hasattr(self, 'progress') or not self.progress: return 0 try: @@ -233,7 +329,7 @@ def _progress_cb(string, length, data): def _update_tips_cb(refname, a, b, data): self = ffi.from_handle(data) - if not hasattr(self, 'update_tips'): + if not hasattr(self, 'update_tips') or not self.update_tips: return 0 try: @@ -252,7 +348,7 @@ def _update_tips_cb(refname, a, b, data): def _credentials_cb(cred_out, url, username, allowed, data): self = ffi.from_handle(data) - if not hasattr(self, 'credentials'): + if not hasattr(self, 'credentials') or not self.credentials: return 0 try: From db218fae3f262fdb4367434bd7e02ecb30e77239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 12 Apr 2014 18:25:40 +0200 Subject: [PATCH 0728/2237] Remote: get rid of the C code This code has been obsoleted by the CFFI-using code. Some credentials code remains in C due to the clone functionality making use of it. --- pygit2/credentials.py | 6 +- src/pygit2.c | 15 - src/remote.c | 728 ------------------------------------------ src/remote.h | 39 --- src/repository.c | 2 - src/types.h | 32 -- 6 files changed, 4 insertions(+), 818 deletions(-) delete mode 100644 src/remote.c delete mode 100644 src/remote.h diff --git a/pygit2/credentials.py b/pygit2/credentials.py index cad215a57..9f060e2df 100644 --- a/pygit2/credentials.py +++ b/pygit2/credentials.py @@ -25,8 +25,10 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -# Import from pygit2 -from _pygit2 import GIT_CREDTYPE_USERPASS_PLAINTEXT, GIT_CREDTYPE_SSH_KEY +from .ffi import ffi, C + +GIT_CREDTYPE_USERPASS_PLAINTEXT = C.GIT_CREDTYPE_USERPASS_PLAINTEXT +GIT_CREDTYPE_SSH_KEY = C.GIT_CREDTYPE_SSH_KEY class UserPass(object): """Username/Password credentials diff --git a/src/pygit2.c b/src/pygit2.c index bda9545ec..06e4f17e1 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -75,7 +75,6 @@ extern PyTypeObject BranchType; extern PyTypeObject SignatureType; extern PyTypeObject RemoteType; extern PyTypeObject RefspecType; -extern PyTypeObject TransferProgressType; extern PyTypeObject NoteType; extern PyTypeObject NoteIterType; extern PyTypeObject BlameType; @@ -456,20 +455,6 @@ moduleinit(PyObject* m) ADD_TYPE(m, Config) ADD_TYPE(m, ConfigIter) - /* Remotes */ - INIT_TYPE(RemoteType, NULL, NULL) - INIT_TYPE(RefspecType, NULL, NULL) - INIT_TYPE(TransferProgressType, NULL, NULL) - ADD_TYPE(m, Remote) - ADD_TYPE(m, Refspec) - ADD_TYPE(m, TransferProgress) - /* Direction for the refspec */ - ADD_CONSTANT_INT(m, GIT_DIRECTION_FETCH) - ADD_CONSTANT_INT(m, GIT_DIRECTION_PUSH) - /* Credential types */ - ADD_CONSTANT_INT(m, GIT_CREDTYPE_USERPASS_PLAINTEXT) - ADD_CONSTANT_INT(m, GIT_CREDTYPE_SSH_KEY) - /* Blame */ INIT_TYPE(BlameType, NULL, NULL) INIT_TYPE(BlameIterType, NULL, NULL) diff --git a/src/remote.c b/src/remote.c deleted file mode 100644 index dee9b5a76..000000000 --- a/src/remote.c +++ /dev/null @@ -1,728 +0,0 @@ -/* - * Copyright 2010-2014 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 "error.h" -#include "types.h" -#include "utils.h" -#include "oid.h" -#include "refspec.h" -#include "remote.h" - - -extern PyObject *GitError; -extern PyTypeObject RepositoryType; -extern PyTypeObject TransferProgressType; - -PyObject * -wrap_transfer_progress(const git_transfer_progress *stats) -{ - TransferProgress *py_stats; - - py_stats = PyObject_New(TransferProgress, &TransferProgressType); - if (!py_stats) - return NULL; - - py_stats->total_objects = stats->total_objects; - py_stats->indexed_objects = stats->indexed_objects; - py_stats->received_objects = stats->received_objects; - py_stats->local_objects = stats->local_objects; - py_stats->total_deltas = stats->total_deltas; - py_stats->indexed_deltas = stats->indexed_deltas; - py_stats->received_bytes = stats->received_bytes; - - return (PyObject *) py_stats; -} - -void -TransferProgress_dealloc(TransferProgress *self) -{ - PyObject_Del(self); -} - -PyMemberDef TransferProgress_members[] = { - RMEMBER(TransferProgress, total_objects, T_UINT, - "Total number objects to download"), - RMEMBER(TransferProgress, indexed_objects, T_UINT, - "Objects which have been indexed"), - RMEMBER(TransferProgress, received_objects, T_UINT, - "Objects which have been received up to now"), - RMEMBER(TransferProgress, local_objects, T_UINT, - "Local objects which were used to fix the thin pack"), - RMEMBER(TransferProgress, total_deltas, T_UINT, - "Total number of deltas in the pack"), - RMEMBER(TransferProgress, indexed_deltas, T_UINT, - "Deltas which have been indexed"), - /* FIXME: technically this is unsigned, but there's no value for size_t - * here. */ - RMEMBER(TransferProgress, received_bytes, T_PYSSIZET, - "Number of bytes received up to now"), - {NULL}, -}; - -PyDoc_STRVAR(TransferProgress__doc__, - "Progress downloading and indexing data during a fetch"); - -PyTypeObject TransferProgressType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.TransferProgress", /* tp_name */ - sizeof(TransferProgress), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)TransferProgress_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - TransferProgress__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - TransferProgress_members, /* tp_members */ - 0, /* 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 */ -}; - -static int -progress_cb(const char *str, int len, void *data) -{ - Remote *remote = (Remote *) data; - PyObject *arglist, *ret; - - if (remote->progress == NULL) - return 0; - - if (!PyCallable_Check(remote->progress)) { - PyErr_SetString(PyExc_TypeError, "progress callback is not callable"); - return -1; - } - - arglist = Py_BuildValue("(s#)", str, len); - ret = PyObject_CallObject(remote->progress, arglist); - Py_DECREF(arglist); - - if (!ret) - return -1; - - Py_DECREF(ret); - - return 0; -} - -static int -credentials_cb(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *data) -{ - Remote *remote = (Remote *) data; - - return callable_to_credentials(out, url, username_from_url, allowed_types, remote->credentials); -} - -static int -transfer_progress_cb(const git_transfer_progress *stats, void *data) -{ - Remote *remote = (Remote *) data; - PyObject *py_stats, *ret; - - if (remote->transfer_progress == NULL) - return 0; - - if (!PyCallable_Check(remote->transfer_progress)) { - PyErr_SetString(PyExc_TypeError, "transfer progress callback is not callable"); - return -1; - } - - py_stats = wrap_transfer_progress(stats); - if (!py_stats) - return -1; - - ret = PyObject_CallFunctionObjArgs(remote->transfer_progress, py_stats, NULL); - Py_DECREF(py_stats); - if (!ret) - return -1; - - Py_DECREF(ret); - - return 0; -} - -static int -update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data) -{ - Remote *remote = (Remote *) data; - PyObject *ret; - PyObject *old, *new; - - if (remote->update_tips == NULL) - return 0; - - if (!PyCallable_Check(remote->update_tips)) { - PyErr_SetString(PyExc_TypeError, "update tips callback is not callable"); - return -1; - } - - old = git_oid_to_python(a); - new = git_oid_to_python(b); - - ret = PyObject_CallFunction(remote->update_tips, "(s,O,O)", refname, old ,new); - - Py_DECREF(old); - Py_DECREF(new); - - if (!ret) - return -1; - - Py_DECREF(ret); - - return 0; -} - -static void -Remote_dealloc(Remote *self) -{ - Py_CLEAR(self->repo); - Py_CLEAR(self->progress); - git_remote_free(self->remote); - PyObject_Del(self); -} - -PyDoc_STRVAR(Remote_name__doc__, "Name of the remote refspec"); - -PyObject * -Remote_name__get__(Remote *self) -{ - return to_unicode(git_remote_name(self->remote), NULL, NULL); -} - -int -Remote_name__set__(Remote *self, PyObject* py_name) -{ - int err; - const char* name; - PyObject *tname; - - name = py_str_borrow_c_str(&tname, py_name, NULL); - if (name != NULL) { - err = git_remote_rename(self->remote, name, NULL, NULL); - Py_DECREF(tname); - - if (err == GIT_OK) - return 0; - - Error_set(err); - } - - return -1; -} - -PyDoc_STRVAR(Remote_fetch_refspecs__doc__, "Fetch refspecs"); - -PyObject * -Remote_fetch_refspecs__get__(Remote *self) -{ - int err; - git_strarray refspecs; - PyObject *new_list; - - err = git_remote_get_fetch_refspecs(&refspecs, self->remote); - if (err != GIT_OK) - return Error_set(err); - - new_list = get_pylist_from_git_strarray(&refspecs); - - git_strarray_free(&refspecs); - return new_list; -} - -int -Remote_fetch_refspecs__set__(Remote *self, PyObject *py_list) -{ - int err; - git_strarray fetch_refspecs; - - if (get_strarraygit_from_pylist(&fetch_refspecs, py_list) < 0) - return -1; - - err = git_remote_set_fetch_refspecs(self->remote, &fetch_refspecs); - git_strarray_free(&fetch_refspecs); - - if (err < 0) { - Error_set(err); - return -1; - } - - return 0; -} - -PyDoc_STRVAR(Remote_push_refspecs__doc__, "Push refspecs"); - -PyObject * -Remote_push_refspecs__get__(Remote *self) -{ - int err; - git_strarray refspecs; - PyObject *new_list; - - err = git_remote_get_push_refspecs(&refspecs, self->remote); - if (err != GIT_OK) - return Error_set(err); - - new_list = get_pylist_from_git_strarray(&refspecs); - - git_strarray_free(&refspecs); - return new_list; -} - -int -Remote_push_refspecs__set__(Remote *self, PyObject *py_list) -{ - int err; - git_strarray push_refspecs; - - if (get_strarraygit_from_pylist(&push_refspecs, py_list) != 0) - return -1; - - err = git_remote_set_push_refspecs(self->remote, &push_refspecs); - git_strarray_free(&push_refspecs); - - if (err < 0) { - Error_set(err); - return -1; - } - - return 0; -} - - -PyDoc_STRVAR(Remote_url__doc__, "Url of the remote"); - - -PyObject * -Remote_url__get__(Remote *self) -{ - const char *url; - - url = git_remote_url(self->remote); - if (!url) - Py_RETURN_NONE; - - return to_unicode(url, NULL, NULL); -} - - -int -Remote_url__set__(Remote *self, PyObject* py_url) -{ - int err; - const char* url = NULL; - PyObject *turl; - - url = py_str_borrow_c_str(&turl, py_url, NULL); - if (url != NULL) { - err = git_remote_set_url(self->remote, url); - Py_DECREF(turl); - - if (err == GIT_OK) - return 0; - - Error_set(err); - } - - return -1; -} - -PyDoc_STRVAR(Remote_push_url__doc__, "Push url of the remote"); - - -PyObject * -Remote_push_url__get__(Remote *self) -{ - const char *url; - - url = git_remote_pushurl(self->remote); - if (!url) - Py_RETURN_NONE; - - return to_unicode(url, NULL, NULL); -} - - -int -Remote_push_url__set__(Remote *self, PyObject* py_url) -{ - int err; - const char* url = NULL; - PyObject *turl; - - url = py_str_borrow_c_str(&turl, py_url, NULL); - if (url != NULL) { - err = git_remote_set_pushurl(self->remote, url); - Py_DECREF(turl); - - if (err == GIT_OK) - return 0; - - Error_set(err); - } - - return -1; -} - - -PyDoc_STRVAR(Remote_refspec_count__doc__, "Number of refspecs."); - -PyObject * -Remote_refspec_count__get__(Remote *self) -{ - size_t count; - - count = git_remote_refspec_count(self->remote); - return PyLong_FromSize_t(count); -} - - -PyDoc_STRVAR(Remote_get_refspec__doc__, - "get_refspec(n) -> (str, str)\n" - "\n" - "Return the refspec at the given position."); - -PyObject * -Remote_get_refspec(Remote *self, PyObject *value) -{ - size_t n; - const git_refspec *refspec; - - n = PyLong_AsSize_t(value); - if (PyErr_Occurred()) - return NULL; - - refspec = git_remote_get_refspec(self->remote, n); - if (refspec == NULL) { - PyErr_SetObject(PyExc_IndexError, value); - return NULL; - } - - return (PyObject*) wrap_refspec(self, refspec); -} - - -PyDoc_STRVAR(Remote_fetch__doc__, - "fetch() -> {'indexed_objects': int, 'received_objects' : int," - " 'received_bytesa' : int}\n" - "\n" - "Negotiate what objects should be downloaded and download the\n" - "packfile with those objects"); - -PyObject * -Remote_fetch(Remote *self, PyObject *args) -{ - PyObject* py_stats = NULL; - const git_transfer_progress *stats; - int err; - - PyErr_Clear(); - err = git_remote_fetch(self->remote); - /* - * XXX: We should be checking for GIT_EUSER, but on v0.20, this does not - * make it all the way to us for update_tips - */ - if (err < 0 && PyErr_Occurred()) - return NULL; - if (err < 0) - return Error_set(err); - - stats = git_remote_stats(self->remote); - py_stats = Py_BuildValue("{s:I,s:I,s:n}", - "indexed_objects", stats->indexed_objects, - "received_objects", stats->received_objects, - "received_bytes", stats->received_bytes); - - return (PyObject*) py_stats; -} - - -PyDoc_STRVAR(Remote_save__doc__, - "save()\n\n" - "Save a remote to its repository configuration."); - -PyObject * -Remote_save(Remote *self, PyObject *args) -{ - int err; - - err = git_remote_save(self->remote); - if (err == GIT_OK) { - Py_RETURN_NONE; - } - else { - return Error_set(err); - } -} - - -int -push_status_foreach_callback(const char *ref, const char *msg, void *data) -{ - const char **msg_dst = (const char **)data; - if (msg != NULL && *msg_dst == NULL) - *msg_dst = msg; - return 0; -} - -PyDoc_STRVAR(Remote_push__doc__, - "push(refspec)\n" - "\n" - "Push the given refspec to the remote. Raises ``GitError`` on error."); - -PyObject * -Remote_push(Remote *self, PyObject *args) -{ - git_push *push = NULL; - const char *refspec = NULL; - const char *msg = NULL; - int err; - - if (!PyArg_ParseTuple(args, "s", &refspec)) - return NULL; - - err = git_push_new(&push, self->remote); - if (err < 0) - return Error_set(err); - - err = git_push_add_refspec(push, refspec); - if (err < 0) - goto error; - - err = git_push_finish(push); - if (err < 0) - goto error; - - if (!git_push_unpack_ok(push)) { - git_push_free(push); - PyErr_SetString(GitError, "Remote failed to unpack objects"); - return NULL; - } - - err = git_push_status_foreach(push, push_status_foreach_callback, &msg); - if (err < 0) - goto error; - if (msg != NULL) { - git_push_free(push); - PyErr_SetString(GitError, msg); - return NULL; - } - - err = git_push_update_tips(push); - if (err < 0) - goto error; - - git_push_free(push); - Py_RETURN_NONE; - -error: - git_push_free(push); - return Error_set(err); -} - - -PyDoc_STRVAR(Remote_add_push__doc__, - "add_push(refspec)\n" - "\n" - "Add a push refspec to the remote."); - -PyObject * -Remote_add_push(Remote *self, PyObject *args) -{ - git_remote *remote; - char *refspec = NULL; - int err = 0; - - if (!PyArg_ParseTuple(args, "s", &refspec)) - return NULL; - - remote = self->remote; - err = git_remote_add_push(remote, refspec); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; -} - - -PyDoc_STRVAR(Remote_add_fetch__doc__, - "add_fetch(refspec)\n" - "\n" - "Add a fetch refspec to the remote."); - -PyObject * -Remote_add_fetch(Remote *self, PyObject *args) -{ - git_remote *remote; - char *refspec = NULL; - int err = 0; - - if (!PyArg_ParseTuple(args, "s", &refspec)) - return NULL; - - remote = self->remote; - err = git_remote_add_fetch(remote, refspec); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; -} - -PyMethodDef Remote_methods[] = { - METHOD(Remote, fetch, METH_NOARGS), - METHOD(Remote, save, METH_NOARGS), - METHOD(Remote, get_refspec, METH_O), - METHOD(Remote, push, METH_VARARGS), - METHOD(Remote, add_push, METH_VARARGS), - METHOD(Remote, add_fetch, METH_VARARGS), - {NULL} -}; - -PyGetSetDef Remote_getseters[] = { - GETSET(Remote, name), - GETSET(Remote, url), - GETSET(Remote, push_url), - GETTER(Remote, refspec_count), - GETSET(Remote, fetch_refspecs), - GETSET(Remote, push_refspecs), - {NULL} -}; - -PyMemberDef Remote_members[] = { - MEMBER(Remote, progress, T_OBJECT_EX, "Progress output callback"), - MEMBER(Remote, credentials, T_OBJECT_EX, - "credentials(url, username_from_url, allowed_types) -> credential\n" - "\n" - "Credentials callback\n" - "\n" - "If the remote server requires authentication, this function will\n" - "be called and its return value used for authentication.\n" - "\n" - ":param str url: The url of the remote\n" - ":param username_from_url: Username extracted from the url, if any\n" - ":type username_from_url: str or None\n" - ":param int allowed_types: credential types supported by the remote "), - MEMBER(Remote, transfer_progress, T_OBJECT_EX, "Transfer progress callback"), - MEMBER(Remote, update_tips, T_OBJECT_EX, "update tips callback"), - {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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - Remote__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Remote_methods, /* tp_methods */ - Remote_members, /* 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 */ -}; - -PyObject * -wrap_remote(git_remote *c_remote, Repository *repo) -{ - Remote *py_remote = NULL; - git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; - - py_remote = PyObject_New(Remote, &RemoteType); - if (py_remote) { - Py_INCREF(repo); - py_remote->repo = repo; - py_remote->remote = c_remote; - py_remote->progress = NULL; - py_remote->credentials = NULL; - py_remote->transfer_progress = NULL; - py_remote->update_tips = NULL; - - callbacks.progress = progress_cb; - callbacks.credentials = credentials_cb; - callbacks.transfer_progress = transfer_progress_cb; - callbacks.update_tips = update_tips_cb; - callbacks.payload = py_remote; - git_remote_set_callbacks(c_remote, &callbacks); - } - - return (PyObject *)py_remote; -} diff --git a/src/remote.h b/src/remote.h deleted file mode 100644 index ce6ee47d5..000000000 --- a/src/remote.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2010-2014 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. - */ - -#ifndef INCLUDE_pygit2_remote_h -#define INCLUDE_pygit2_remote_h - -#define PY_SSIZE_T_CLEAN -#include -#include -#include - -PyObject* Remote_fetch(Remote *self, PyObject *args); -PyObject* wrap_remote(git_remote *c_remote, Repository *repo); - -#endif diff --git a/src/repository.c b/src/repository.c index 14e07a187..e8e38f96b 100644 --- a/src/repository.c +++ b/src/repository.c @@ -35,7 +35,6 @@ #include "oid.h" #include "note.h" #include "repository.h" -#include "remote.h" #include "branch.h" #include "blame.h" #include "mergeresult.h" @@ -54,7 +53,6 @@ extern PyTypeObject TreeType; extern PyTypeObject TreeBuilderType; extern PyTypeObject ConfigType; extern PyTypeObject DiffType; -extern PyTypeObject RemoteType; extern PyTypeObject ReferenceType; extern PyTypeObject NoteType; extern PyTypeObject NoteIterType; diff --git a/src/types.h b/src/types.h index cfd00092c..e50f8341c 100644 --- a/src/types.h +++ b/src/types.h @@ -194,38 +194,6 @@ typedef struct { char *encoding; } Signature; - -/* git_remote */ -typedef struct { - PyObject_HEAD - Repository *repo; - git_remote *remote; - /* Callbacks for network events */ - PyObject *progress; - PyObject *credentials; - PyObject *transfer_progress; - PyObject *update_tips; -} Remote; - -/* git_refspec */ -typedef struct { - PyObject_HEAD - const Remote *owner; - const git_refspec *refspec; -} Refspec; - -/* git_transfer_progress */ -typedef struct { - PyObject_HEAD - unsigned int total_objects; - unsigned int indexed_objects; - unsigned int received_objects; - unsigned int local_objects; - unsigned int total_deltas; - unsigned int indexed_deltas; - size_t received_bytes; -} TransferProgress; - /* git_blame */ SIMPLE_TYPE(Blame, git_blame, blame) From 072b0382104f432721dbe9e31395d224f0b751b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 12 Apr 2014 19:10:50 +0200 Subject: [PATCH 0729/2237] Implement clone via CFFI as well This lets us get rid of the last piece of C for anything related to remotes and credentials. --- pygit2/__init__.py | 55 ++++++++++++++++++++++++++-- pygit2/decl.h | 64 +++++++++++++++++++++++++++++++++ pygit2/remote.py | 55 +++++++++++++++------------- src/pygit2.c | 65 --------------------------------- src/utils.c | 89 ---------------------------------------------- src/utils.h | 2 -- 6 files changed, 147 insertions(+), 183 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 396af196a..df3a898c7 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -37,7 +37,9 @@ from .version import __version__ from .settings import Settings from .credentials import * -from .remote import Remote +from .remote import Remote, get_credentials +from .errors import check_error +from .ffi import ffi, C, to_str def init_repository(path, bare=False): """ @@ -50,6 +52,19 @@ def init_repository(path, bare=False): return Repository(path) +@ffi.callback('int (*credentials)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data)') +def _credentials_cb(cred_out, url, username_from_url, allowed, data): + d = ffi.from_handle(data) + + try: + ccred = get_credentials(d['callback'], url, username_from_url, allowed) + cred_out[0] = ccred[0] + except Exception, e: + d['exception'] = e + return C.GIT_EUSER + + return 0 + def clone_repository( url, path, bare=False, ignore_cert_errors=False, remote_name="origin", checkout_branch=None, credentials=None): @@ -75,8 +90,42 @@ def clone_repository( """ - _pygit2.clone_repository( - url, path, bare, ignore_cert_errors, remote_name, checkout_branch, credentials) + opts = ffi.new('git_clone_options *') + crepo = ffi.new('git_repository **') + + branch = checkout_branch or None + + # Data, let's use a dict as we don't really want much more + d = {} + d['callback'] = credentials + d_handle = ffi.new_handle(d) + + # We need to keep the ref alive ourselves + checkout_branch_ref = None + if branch: + checkout_branch_ref = ffi.new('char []', branch) + opts.checkout_branch = checkout_branch_ref + + remote_name_ref = ffi.new('char []', to_str(remote_name)) + opts.remote_name = remote_name_ref + + opts.version = 1 + opts.ignore_cert_errors = ignore_cert_errors + opts.bare = bare + opts.remote_callbacks.version = 1 + opts.checkout_opts.version = 1 + if credentials: + opts.remote_callbacks.credentials = _credentials_cb + opts.remote_callbacks.payload = d_handle + + err = C.git_clone(crepo, to_str(url), to_str(path), opts) + C.git_repository_free(crepo[0]) + + if 'exception' in d: + raise d['exception'] + + check_error(err) + return Repository(path) settings = Settings() diff --git a/pygit2/decl.h b/pygit2/decl.h index 40680ce6c..a64c32fc4 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -3,6 +3,8 @@ typedef ... git_remote; typedef ... git_refspec; typedef ... git_push; typedef ... git_cred; +typedef ... git_diff_file; +typedef ... git_tree; #define GIT_OID_RAWSZ ... @@ -43,6 +45,7 @@ typedef struct { const git_error * giterr_last(void); void git_strarray_free(git_strarray *array); +void git_repository_free(git_repository *repo); typedef struct git_transfer_progress { unsigned int total_objects; @@ -148,3 +151,64 @@ int git_cred_ssh_key_new( const char *publickey, const char *privatekey, const char *passphrase); + +typedef enum { ... } git_checkout_notify_t; + +typedef int (*git_checkout_notify_cb)( + git_checkout_notify_t why, + const char *path, + const git_diff_file *baseline, + const git_diff_file *target, + const git_diff_file *workdir, + void *payload); + +typedef void (*git_checkout_progress_cb)( + const char *path, + size_t completed_steps, + size_t total_steps, + void *payload); + +typedef struct git_checkout_opts { + unsigned int version; + + unsigned int checkout_strategy; + + int disable_filters; + unsigned int dir_mode; + unsigned int file_mode; + int file_open_flags; + + unsigned int notify_flags; + git_checkout_notify_cb notify_cb; + void *notify_payload; + + git_checkout_progress_cb progress_cb; + void *progress_payload; + + git_strarray paths; + + git_tree *baseline; + + const char *target_directory; + + const char *our_label; + const char *their_label; +} git_checkout_opts; + + +typedef struct git_clone_options { + unsigned int version; + + git_checkout_opts checkout_opts; + git_remote_callbacks remote_callbacks; + + int bare; + int ignore_cert_errors; + const char *remote_name; + const char* checkout_branch; +} git_clone_options; + +int git_clone(git_repository **out, + const char *url, + const char *local_path, + const git_clone_options *options); diff --git a/pygit2/remote.py b/pygit2/remote.py index c7f122ca1..40e6ddaaa 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -352,37 +352,44 @@ def _credentials_cb(cred_out, url, username, allowed, data): return 0 try: - url_str = maybe_string(url) - username_str = maybe_string(username) + ccred = get_credentials(self.credentials, url, username, allowed) + cred_out[0] = ccred[0] - creds = self.credentials(url_str, username_str, allowed) + except Exception, e: + self._stored_exception = e + return C.GIT_EUSER - if not hasattr(creds, 'credential_type') or not hasattr(creds, 'credential_tuple'): - raise TypeError("credential does not implement interface") + return 0 - cred_type = creds.credential_type +def get_credentials(fn, url, username, allowed): + """Call fn and return the credentials object""" - if not (allowed & cred_type): - raise TypeError("invalid credential type") + url_str = maybe_string(url) + username_str = maybe_string(username) - ccred = ffi.new('git_cred **') - if cred_type == C.GIT_CREDTYPE_USERPASS_PLAINTEXT: - name, passwd = creds.credential_tuple - err = C.git_cred_userpass_plaintext_new(ccred, to_str(name), to_str(passwd)) + creds = fn(url_str, username_str, allowed) - elif cred_type == C.GIT_CREDTYPE_SSH_KEY: - name, pubkey, privkey, passphrase = creds.credential_tuple - err = C.git_cred_ssh_key_new(ccred, to_str(name),to_str(pubkey), - to_str(privkey), to_str(passphrase)) + if not hasattr(creds, 'credential_type') or not hasattr(creds, 'credential_tuple'): + raise TypeError("credential does not implement interface") - else: - raise TypeError("unsupported credential type") + cred_type = creds.credential_type - check_error(err) - cred_out[0] = ccred[0] + if not (allowed & cred_type): + raise TypeError("invalid credential type") - except Exception, e: - self._stored_exception = e - return C.GIT_EUSER + ccred = ffi.new('git_cred **') + if cred_type == C.GIT_CREDTYPE_USERPASS_PLAINTEXT: + name, passwd = creds.credential_tuple + err = C.git_cred_userpass_plaintext_new(ccred, to_str(name), to_str(passwd)) - return 0 + elif cred_type == C.GIT_CREDTYPE_SSH_KEY: + name, pubkey, privkey, passphrase = creds.credential_tuple + err = C.git_cred_ssh_key_new(ccred, to_str(name),to_str(pubkey), + to_str(privkey), to_str(passphrase)) + + else: + raise TypeError("unsupported credential type") + + check_error(err) + + return ccred diff --git a/src/pygit2.c b/src/pygit2.c index 06e4f17e1..d88c0e408 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -115,69 +115,6 @@ init_repository(PyObject *self, PyObject *args) { Py_RETURN_NONE; }; -static int -credentials_cb(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *data) -{ - PyObject *credentials = (PyObject *) data; - - return callable_to_credentials(out, url, username_from_url, allowed_types, credentials); -} - -PyDoc_STRVAR(clone_repository__doc__, - "clone_repository(url, path, bare, remote_name, checkout_branch)\n" - "\n" - "Clones a Git repository in the given url to the given path " - "with the specified options.\n" - "\n" - "Arguments:\n" - "\n" - "url\n" - " Git repository remote url.\n" - "path\n" - " Path where to create the repository.\n" - "bare\n" - " If 'bare' is not 0, then a bare git repository will be created.\n" - "remote_name\n" - " The name given to the 'origin' remote. The default is 'origin'.\n" - "checkout_branch\n" - " The name of the branch to checkout. None means use the remote's " - "HEAD.\n"); - - -PyObject * -clone_repository(PyObject *self, PyObject *args) { - git_repository *repo; - const char *url; - const char *path; - unsigned int bare, ignore_cert_errors; - const char *remote_name, *checkout_branch; - PyObject *credentials = NULL; - int err; - git_clone_options opts = GIT_CLONE_OPTIONS_INIT; - - if (!PyArg_ParseTuple(args, "zzIIzzO", - &url, &path, &bare, &ignore_cert_errors, &remote_name, &checkout_branch, &credentials)) - return NULL; - - opts.bare = bare; - opts.ignore_cert_errors = ignore_cert_errors; - opts.remote_name = remote_name; - opts.checkout_branch = checkout_branch; - - if (credentials != Py_None) { - opts.remote_callbacks.credentials = credentials_cb; - opts.remote_callbacks.payload = credentials; - } - - err = git_clone(&repo, url, path, &opts); - if (err < 0) - return Error_set(err); - - git_repository_free(repo); - Py_RETURN_NONE; -}; - - PyDoc_STRVAR(discover_repository__doc__, "discover_repository(path[, across_fs[, ceiling_dirs]]) -> str\n" "\n" @@ -252,8 +189,6 @@ hash(PyObject *self, PyObject *args) PyMethodDef module_methods[] = { {"init_repository", init_repository, METH_VARARGS, init_repository__doc__}, - {"clone_repository", clone_repository, METH_VARARGS, - clone_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, diff --git a/src/utils.c b/src/utils.c index 24b6bbd47..44acf5c6a 100644 --- a/src/utils.c +++ b/src/utils.c @@ -153,92 +153,3 @@ get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist) return -1; } - -static int -py_cred_to_git_cred(git_cred **out, PyObject *py_cred, unsigned int allowed) -{ - PyObject *py_type, *py_tuple; - long type; - int err = -1; - - py_type = PyObject_GetAttrString(py_cred, "credential_type"); - py_tuple = PyObject_GetAttrString(py_cred, "credential_tuple"); - - if (!py_type || !py_tuple) { - printf("py_type %p, py_tuple %p\n", py_type, py_tuple); - PyErr_SetString(PyExc_TypeError, "credential doesn't implement the interface"); - goto cleanup; - } - - if (!PyLong_Check(py_type)) { - PyErr_SetString(PyExc_TypeError, "credential type is not a long"); - goto cleanup; - } - - type = PyLong_AsLong(py_type); - - /* Sanity check, make sure we're given credentials we can use */ - if (!(allowed & type)) { - PyErr_SetString(PyExc_TypeError, "invalid credential type"); - goto cleanup; - } - - switch (type) { - case GIT_CREDTYPE_USERPASS_PLAINTEXT: - { - const char *username, *password; - - if (!PyArg_ParseTuple(py_tuple, "ss", &username, &password)) - goto cleanup; - - err = git_cred_userpass_plaintext_new(out, username, password); - break; - } - case GIT_CREDTYPE_SSH_KEY: - { - const char *username, *pubkey, *privkey, *passphrase; - - if (!PyArg_ParseTuple(py_tuple, "ssss", &username, &pubkey, &privkey, &passphrase)) - goto cleanup; - - err = git_cred_ssh_key_new(out, username, pubkey, privkey, passphrase); - break; - } - default: - PyErr_SetString(PyExc_TypeError, "unsupported credential type"); - break; - } - -cleanup: - Py_XDECREF(py_type); - Py_XDECREF(py_tuple); - - return err; -} - -int -callable_to_credentials(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, PyObject *credentials) -{ - int err; - PyObject *py_cred = NULL, *arglist = NULL; - - if (credentials == NULL || credentials == Py_None) - return 0; - - if (!PyCallable_Check(credentials)) { - PyErr_SetString(PyExc_TypeError, "credentials callback is not callable"); - return -1; - } - - arglist = Py_BuildValue("(szI)", url, username_from_url, allowed_types); - py_cred = PyObject_CallObject(credentials, arglist); - Py_DECREF(arglist); - - if (!py_cred) - return -1; - - err = py_cred_to_git_cred(out, py_cred, allowed_types); - Py_DECREF(py_cred); - - return err; -} diff --git a/src/utils.h b/src/utils.h index 7f95d73ee..81744f686 100644 --- a/src/utils.h +++ b/src/utils.h @@ -117,8 +117,6 @@ const char *py_str_borrow_c_str(PyObject **tvaue, PyObject *value, const char *e PyObject * get_pylist_from_git_strarray(git_strarray *strarray); int get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist); -int callable_to_credentials(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, PyObject *credentials); - #define py_path_to_c_str(py_path) \ py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding) From 73e9e58fa4941a6b5bcd14537848abbd1ab0000e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 14 Apr 2014 19:37:44 +0200 Subject: [PATCH 0730/2237] Config: make bool and int parsing explicit via functions Passing a tuple to the mapping interface isn't the best of interfaces, as the key is only the string. Instead, expose `Config.get_bool()` and `Config.get_int()` methods to parse the values as per the git-config rules before returning the appropriate type to the user. The mapping interface itself returns a string. --- docs/config.rst | 19 ++----- src/config.c | 128 ++++++++++++++++++++++++++------------------ test/test_config.py | 12 ++--- 3 files changed, 88 insertions(+), 71 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index 4bdaaa1cc..881fc1d70 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -24,18 +24,9 @@ The Config type The :class:`Config` Mapping interface. -Parsing the values -=================== +When using the mapping interface, the value is returned as a +string. In order to apply the git-config parsing rules, you can use +:method:`Config.get_bool` or :method:`Config.get_int`. -Instead of a string, a tuple of `(str,type)` can be used to look up a -key and parse it through the Git rules. E.g. - - config['core.bare',bool] - -will return True if 'core.bare' is truthy. - -Truty values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false', -0, 'off' and 'no'. - -Available types are `bool` and `int`. Not specifying a type returns a -string. +.. automethod:: pygit2.Config.get_bool +.. automethod:: pygit2.Config.get_int diff --git a/src/config.c b/src/config.c index 58fdeaaa8..07dbb408a 100644 --- a/src/config.c +++ b/src/config.c @@ -168,70 +168,94 @@ Config_contains(Config *self, PyObject *py_key) { return 1; } - -PyObject * -Config_getitem(Config *self, PyObject *py_input_key) +/* Get the C string value given a python string as key */ +static int +get_string(const char **key_out, Config *self, PyObject *py_key) { - int err; - const char *value_str; + PyObject *tkey; const char *key; - PyObject *py_key, *py_value, *tkey, *tmp_type = NULL; - PyTypeObject *py_type = NULL; + int err; - if (PyTuple_Check(py_input_key) && PyTuple_Size(py_input_key) == 2) { - py_key = PyTuple_GetItem(py_input_key, 0); - tmp_type = PyTuple_GetItem(py_input_key, 1); - } else { - py_key = py_input_key; + key = py_str_borrow_c_str(&tkey, py_key, NULL); + if (key == NULL) + return -1; + + err = git_config_get_string(key_out, self->config, key); + Py_CLEAR(tkey); + + if (err == GIT_ENOTFOUND) { + PyErr_SetObject(PyExc_KeyError, py_key); + return -1; } - /* If passed a tuple, make sure the second item is a type */ - if (tmp_type) { - if (!PyType_Check(tmp_type)) - return NULL; - else - py_type = (PyTypeObject *) tmp_type; + if (err < 0) { + Error_set(err); + return -1; } - key = py_str_borrow_c_str(&tkey, py_key, NULL); - if (key == NULL) + return 0; +} + +PyObject * +Config_getitem(Config *self, PyObject *py_key) +{ + int err; + const char *value_str; + + err = get_string(&value_str, self, py_key); + if (err < 0) return NULL; - err = git_config_get_string(&value_str, self->config, key); - Py_CLEAR(tkey); + return to_unicode(value_str, NULL, NULL); +} + +PyDoc_STRVAR(Config_get_bool__doc__, + "get_bool(key) -> Bool\n" + "\n" + "Look up *key* and parse its value as a boolean as per the git-config rules\n" + "\n" + "Truthy values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false',\n" + "0, 'off' and 'no'"); + +PyObject * +Config_get_bool(Config *self, PyObject *key) +{ + int err, value; + const char *value_str; + + err = get_string(&value_str, self, key); if (err < 0) - goto cleanup; - - /* If the user specified a type, let's parse it */ - if (py_type) { - if (py_type == &PyBool_Type) { - int value; - if ((err = git_config_parse_bool(&value, value_str)) < 0) - goto cleanup; - - py_value = PyBool_FromLong(value); - } else if (py_type == &PyInteger_Type) { - int64_t value; - if ((err = git_config_parse_int64(&value, value_str)) < 0) - goto cleanup; - - py_value = PyLong_FromLongLong(value); - } - } else { - py_value = to_unicode(value_str, NULL, NULL); - } + return NULL; -cleanup: - if (err < 0) { - if (err == GIT_ENOTFOUND) { - PyErr_SetObject(PyExc_KeyError, py_key); - return NULL; - } + if ((err = git_config_parse_bool(&value, value_str)) < 0) + return NULL; - return Error_set(err); - } + return PyBool_FromLong(value); +} + +PyDoc_STRVAR(Config_get_int__doc__, + "get_int(key) -> int\n" + "\n" + "Look up *key* and parse its value as an integer as per the git-config rules\n" + "\n" + "A value can have a suffix 'k', 'm' or 'g' which stand for 'kilo', 'mega' and\n" + "'giga' respectively"); + +PyObject * +Config_get_int(Config *self, PyObject *key) +{ + int err; + int64_t value; + const char *value_str; + + err = get_string(&value_str, self, key); + if (err < 0) + return NULL; + + if ((err = git_config_parse_int64(&value, value_str)) < 0) + return NULL; - return py_value; + return PyLong_FromLongLong(value); } int @@ -396,6 +420,8 @@ PyMethodDef Config_methods[] = { METHOD(Config, add_file, METH_VARARGS | METH_KEYWORDS), METHOD(Config, get_multivar, METH_VARARGS), METHOD(Config, set_multivar, METH_VARARGS), + METHOD(Config, get_bool, METH_O), + METHOD(Config, get_int, METH_O), {NULL} }; diff --git a/test/test_config.py b/test/test_config.py index f29c703e1..937ed1bf9 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -74,7 +74,7 @@ def test_new(self): config_read = Config(CONFIG_FILENAME) self.assertTrue('core.bare' in config_read) - self.assertFalse(config_read['core.bare',bool]) + self.assertFalse(config_read.get_bool('core.bare')) self.assertTrue('core.editor' in config_read) self.assertEqual(config_read['core.editor'], 'ed') @@ -88,9 +88,9 @@ def test_add(self): config.add_file(CONFIG_FILENAME, 0) self.assertTrue('this.that' in config) - self.assertTrue(config['this.that',bool]) + self.assertTrue(config.get_bool('this.that')) self.assertTrue('something.other.here' in config) - self.assertFalse(config['something.other.here',bool]) + self.assertFalse(config.get_bool('something.other.here')) def test_read(self): config = self.repo.config @@ -103,11 +103,11 @@ def test_read(self): lambda: config['abc.def']) self.assertTrue('core.bare' in config) - self.assertFalse(config['core.bare',bool]) + self.assertFalse(config.get_bool('core.bare')) self.assertTrue('core.editor' in config) self.assertEqual(config['core.editor'], 'ed') self.assertTrue('core.repositoryformatversion' in config) - self.assertEqual(config['core.repositoryformatversion',int], 0) + self.assertEqual(config.get_int('core.repositoryformatversion'), 0) new_file = open(CONFIG_FILENAME, "w") new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n") @@ -129,7 +129,7 @@ def test_write(self): self.assertFalse('core.dummy1' in config) config['core.dummy1'] = 42 self.assertTrue('core.dummy1' in config) - self.assertEqual(config['core.dummy1',int], 42) + self.assertEqual(config.get_int('core.dummy1'), 42) self.assertFalse('core.dummy2' in config) config['core.dummy2'] = 'foobar' From 674546bbb527106273e670b1dd6a4bf84194e4ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 12 Apr 2014 23:30:00 +0200 Subject: [PATCH 0731/2237] Some python3 fixes --- pygit2/__init__.py | 2 +- pygit2/ffi.py | 13 +++++++------ pygit2/remote.py | 8 ++++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index df3a898c7..2dbda72bf 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -59,7 +59,7 @@ def _credentials_cb(cred_out, url, username_from_url, allowed, data): try: ccred = get_credentials(d['callback'], url, username_from_url, allowed) cred_out[0] = ccred[0] - except Exception, e: + except Exception as e: d['exception'] = e return C.GIT_EUSER diff --git a/pygit2/ffi.py b/pygit2/ffi.py index ce25ff0ea..10762b633 100644 --- a/pygit2/ffi.py +++ b/pygit2/ffi.py @@ -29,7 +29,8 @@ from __future__ import absolute_import import inspect -from os import path +import codecs +from os import path, getenv from cffi import FFI import sys @@ -51,10 +52,10 @@ def to_str(s, encoding='utf-8', errors='strict'): if sys.version_info.major < 3: def is_string(s): - return isinstance(s, str) + return isinstance(s, basestring) else: def is_string(s): - return isinstance(s, basestring) + return isinstance(s, str) ffi = FFI() @@ -88,7 +89,7 @@ def strings_to_strarray(l): if not is_string(l[i]): raise TypeError("Value must be a string") - s = ffi.new('char []', l[i]) + s = ffi.new('char []', to_str(l[i])) refs[i] = s strings[i] = s @@ -100,7 +101,7 @@ def strings_to_strarray(l): dir_path = path.dirname(path.abspath(inspect.getfile(inspect.currentframe()))) decl_path = path.join(dir_path, 'decl.h') -with open(decl_path, 'rb') as f: - ffi.cdef(f.read()) +with codecs.open(decl_path, 'r', 'utf-8') as header: + ffi.cdef(header.read()) C = ffi.verify("#include ", libraries=["git2"]) diff --git a/pygit2/remote.py b/pygit2/remote.py index 40e6ddaaa..07614d3b8 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -303,7 +303,7 @@ def _transfer_progress_cb(stats_ptr, data): try: self.transfer_progress(TransferProgress(stats_ptr)) - except Exception, e: + except Exception as e: self._stored_exception = e return C.GIT_EUSER @@ -319,7 +319,7 @@ def _progress_cb(string, length, data): try: s = ffi.string(string, length).decode() self.progress(s) - except Exception, e: + except Exception as e: self._stored_exception = e return C.GIT_EUSER @@ -338,7 +338,7 @@ def _update_tips_cb(refname, a, b, data): b = Oid(raw=bytes(ffi.buffer(b))) self.update_tips(s, a, b) - except Exception, e: + except Exception as e: self._stored_exception = e return C.GIT_EUSER @@ -355,7 +355,7 @@ def _credentials_cb(cred_out, url, username, allowed, data): ccred = get_credentials(self.credentials, url, username, allowed) cred_out[0] = ccred[0] - except Exception, e: + except Exception as e: self._stored_exception = e return C.GIT_EUSER From d51174d34ffd898e2922b952da12451c1f74c4d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 13 Apr 2014 17:07:13 +0200 Subject: [PATCH 0732/2237] Install the "decl.h" file --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index eff54e184..a52fc448d 100644 --- a/setup.py +++ b/setup.py @@ -184,6 +184,7 @@ def get_file_list(self): maintainer_email='jdavid.ibp@gmail.com', long_description=long_description, packages=['pygit2'], + package_data={'pygit2': ['decl.h']}, ext_modules=[ Extension('_pygit2', pygit2_exts, include_dirs=[libgit2_include, 'include'], From e56ab370a7803067b2f230fceb53ebf4d46a580f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 13 Apr 2014 18:57:03 +0200 Subject: [PATCH 0733/2237] Remote: make sure to take the contents of the id Pass the contents of the buffer containing the git_oid to bytes() so build a raw representation of its contents. Using bytes(ffi.buffer(...)) returns the representation of the buffer. --- pygit2/remote.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygit2/remote.py b/pygit2/remote.py index 07614d3b8..6a2a7b99f 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -334,8 +334,8 @@ def _update_tips_cb(refname, a, b, data): try: s = maybe_string(refname) - a = Oid(raw=bytes(ffi.buffer(a))) - b = Oid(raw=bytes(ffi.buffer(b))) + a = Oid(raw=bytes(ffi.buffer(a)[:])) + b = Oid(raw=bytes(ffi.buffer(b)[:])) self.update_tips(s, a, b) except Exception as e: From b4bc2b6295e32fe8a4a46d5007ec1209e6af5bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 13 Apr 2014 19:20:45 +0200 Subject: [PATCH 0734/2237] Depend on the cffi package Let both pip and Travis know what we need. --- .travis.yml | 1 + setup.py | 1 + 2 files changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index f2ffa47a8..4fbf40373 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib before_install: - sudo apt-get install cmake + - pip install cffi - "./.travis.sh" script: diff --git a/setup.py b/setup.py index a52fc448d..914cbed7c 100644 --- a/setup.py +++ b/setup.py @@ -185,6 +185,7 @@ def get_file_list(self): long_description=long_description, packages=['pygit2'], package_data={'pygit2': ['decl.h']}, + install_requires=['cffi'], ext_modules=[ Extension('_pygit2', pygit2_exts, include_dirs=[libgit2_include, 'include'], From f3bb8a45567b5244b231e164266972f89f8f7d72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 13 Apr 2014 20:37:58 +0200 Subject: [PATCH 0735/2237] Fix installation-time cffi compilation The documentation recommends adding the ffi code as an extension so it gets built at the right time. Make use of the LIBGIT2 environment variable to build and link the ffi module the same way the C extension does so the user doesn't have to export CFLAGS. --- pygit2/ffi.py | 10 +++++++++- setup.py | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/pygit2/ffi.py b/pygit2/ffi.py index 10762b633..0f225c899 100644 --- a/pygit2/ffi.py +++ b/pygit2/ffi.py @@ -104,4 +104,12 @@ def strings_to_strarray(l): with codecs.open(decl_path, 'r', 'utf-8') as header: ffi.cdef(header.read()) -C = ffi.verify("#include ", libraries=["git2"]) +# if LIBGIT2 exists, set build and link against that version +libgit2_path = getenv('LIBGIT2') +include_dirs = [] +library_dirs = [] +if libgit2_path: + include_dirs = [path.join(libgit2_path, 'include')] + library_dirs = [path.join(libgit2_path, 'lib')] + +C = ffi.verify("#include ", libraries=["git2"], include_dirs=include_dirs, library_dirs=library_dirs) diff --git a/setup.py b/setup.py index 914cbed7c..27733f40b 100644 --- a/setup.py +++ b/setup.py @@ -173,6 +173,11 @@ def get_file_list(self): with codecs.open('README.rst', 'r', 'utf-8') as readme: long_description = readme.read() +# This ffi is pygit2.ffi due to the path trick used in the beginning +# of the file +from ffi import ffi +ffi_ext = ffi.verifier.get_extension() + setup(name='pygit2', description='Python bindings for libgit2.', keywords='git', @@ -191,5 +196,6 @@ def get_file_list(self): include_dirs=[libgit2_include, 'include'], library_dirs=[libgit2_lib], libraries=['git2']), + ffi_ext, ], cmdclass=cmdclass) From 5a20510f8a73ec1603aac33652b64016b0502748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 14 Apr 2014 02:13:36 +0200 Subject: [PATCH 0736/2237] Safer repository pointer extraction Casting a pointer to a non-pointer type is something which you should never do. Instead, do something a bit more convoluted, but which is guaranteed to give us the right pointer, as well as making sure that the memory we exchange between python/cffi and the C extension is of the right pointer size. While we're touching this code, fix which object we pass to the Remote constructor to keep alive. We need to pass the Repository object to stop it from becoming unreferenced (and thus freeing memory the remote needs) instead of the git_repository pointer. --- pygit2/repository.py | 25 ++++++++++++++----------- src/repository.c | 9 ++------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index a22c8204c..107db9128 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -41,6 +41,15 @@ class Repository(_Repository): + def __init__(self, *args, **kwargs): + super(Repository, self).__init__(*args, **kwargs) + + # Get the pointer as the contents of a buffer and store it for + # later access + repo_cptr = ffi.new('git_repository **') + ffi.buffer(repo_cptr)[:] = self._pointer[:] + self._repo = repo_cptr[0] + # # Mapping interface # @@ -72,36 +81,30 @@ def create_remote(self, name, url): Creates a new remote. """ - repo_cptr = ffi.new('git_repository **') - repo_cptr[0] = ffi.cast('git_repository *', self._pointer) cremote = ffi.new('git_remote **') - repo = repo_cptr[0] - err = C.git_remote_create(cremote, repo, to_str(name), to_str(url)) + err = C.git_remote_create(cremote, self._repo, to_str(name), to_str(url)) check_error(err) - return Remote(repo, cremote[0]) + return Remote(self, cremote[0]) @property def remotes(self): """Returns all configured remotes""" - repo_cptr = ffi.new('git_repository **') - repo_cptr[0] = ffi.cast('git_repository *', self._pointer) names = ffi.new('git_strarray *') - repo = repo_cptr[0] try: - err = C.git_remote_list(names, repo) + err = C.git_remote_list(names, self._repo) check_error(err) l = [None] * names.count cremote = ffi.new('git_remote **') for i in range(names.count): - err = C.git_remote_load(cremote, repo, names.strings[i]) + err = C.git_remote_load(cremote, self._repo, names.strings[i]) check_error(err) - l[i] = Remote(repo, cremote[0]) + l[i] = Remote(self, cremote[0]) return l finally: C.git_strarray_free(names) diff --git a/src/repository.c b/src/repository.c index e8e38f96b..1713136cf 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1293,13 +1293,8 @@ PyDoc_STRVAR(Repository__pointer__doc__, "Get the repo's pointer. For internal u PyObject * Repository__pointer__get__(Repository *self) { - /* - * This is pretty bad. We shouldn't be casting a pointer into an - * integer, but we can't access the contents of a PyCapsule from - * python code, which we need to do in order to get a type that - * cffi likes. - */ - return PyLong_FromLongLong((long long) self->repo); + /* Bytes means a raw buffer */ + return PyBytes_FromStringAndSize((char *) &self->repo, sizeof(git_repository *)); } PyDoc_STRVAR(Repository_checkout_head__doc__, From d7d0eb37c35607d217819d5b2ae5783a875cb023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 14 Apr 2014 16:41:30 +0200 Subject: [PATCH 0737/2237] ffi: style changes Make to_str() accept None as well as ffi.NULL to return as a negative value, and grab the version in a more compatible way. --- pygit2/ffi.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pygit2/ffi.py b/pygit2/ffi.py index 0f225c899..468b9bc2f 100644 --- a/pygit2/ffi.py +++ b/pygit2/ffi.py @@ -34,23 +34,29 @@ from cffi import FFI import sys -if sys.version_info.major < 3: +(major_version, _, _, _, _) = sys.version_info + +if major_version < 3: def to_str(s, encoding='utf-8', errors='strict'): - if s == ffi.NULL: + if s == ffi.NULL or s == None: return ffi.NULL - encoding = encoding or 'utf-8' + if isinstance(s, unicode): + encoding = encoding or 'utf-8' return s.encode(encoding, errors) return s else: def to_str(s, encoding='utf-8', errors='strict'): + if s == ffi.NULL or s == None: + return ffi.NULL + if isinstance(s, bytes): return s - else: - return bytes(s, encoding, errors) -if sys.version_info.major < 3: + return s.encode(encoding, errors) + +if major_version < 3: def is_string(s): return isinstance(s, basestring) else: From 37c01d79c5afc20377328fcbebf45abfffbbf61e Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 17 Apr 2014 10:13:26 -0700 Subject: [PATCH 0738/2237] Update some docstrings which had got out of date. --- pygit2/repository.py | 2 +- src/repository.c | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index e6c750f31..75650708e 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -78,7 +78,7 @@ def create_reference(self, name, target, force=False): Examples:: - repo.create_reference('refs/heads/foo', repo.head.hex) + repo.create_reference('refs/heads/foo', repo.head.get_object().hex) repo.create_reference('refs/tags/foo', 'refs/heads/master') repo.create_reference('refs/tags/foo', 'bbb78a9cec580') """ diff --git a/src/repository.c b/src/repository.c index bb2a11b96..2a606e13b 100644 --- a/src/repository.c +++ b/src/repository.c @@ -916,7 +916,7 @@ PyDoc_STRVAR(Repository_create_branch__doc__, "\n" "Examples::\n" "\n" - " repo.create_branch('foo', repo.head.hex, force=False)"); + " repo.create_branch('foo', repo.head.get_object(), force=False)"); PyObject * Repository_create_branch(Repository *self, PyObject *args) @@ -1065,7 +1065,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) } PyDoc_STRVAR(Repository_create_reference_direct__doc__, - "git_reference_create(name, target, force) -> Reference\n" + "create_reference_direct(name, target, force) -> Reference\n" "\n" "Create a new reference \"name\" which points to an object.\n" "\n" @@ -1077,7 +1077,8 @@ PyDoc_STRVAR(Repository_create_reference_direct__doc__, "\n" "Examples::\n" "\n" - " repo.git_reference_create('refs/heads/foo', repo.head.hex, False)"); + " repo.create_reference_direct('refs/heads/foo',\n" + " repo.head.get_object().hex, False)"); PyObject * Repository_create_reference_direct(Repository *self, PyObject *args, @@ -1104,7 +1105,7 @@ Repository_create_reference_direct(Repository *self, PyObject *args, } PyDoc_STRVAR(Repository_create_reference_symbolic__doc__, - "git_reference_symbolic_create(name, source, force) -> Reference\n" + "create_reference_symbolic(name, source, force) -> Reference\n" "\n" "Create a new reference \"name\" which points to another reference.\n" "\n" @@ -1116,7 +1117,7 @@ PyDoc_STRVAR(Repository_create_reference_symbolic__doc__, "\n" "Examples::\n" "\n" - " repo.git_reference_symbolic_create('refs/tags/foo', 'refs/heads/master', False)"); + " repo.create_reference_symbolic('refs/tags/foo', 'refs/heads/master', False)"); PyObject * Repository_create_reference_symbolic(Repository *self, PyObject *args, From 7ed89b0aabcc2c551807f368faf21310ca30ccec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 14 Apr 2014 17:19:36 +0200 Subject: [PATCH 0739/2237] Remote: protect against invalid input on rename The C API expects a non-NULL new name and raises an assertion if we don't protect against such input, so let's guard against falsy values, which also takes care of the empty string, which is also not valid input. --- pygit2/remote.py | 3 +++ test/test_remote.py | 1 + 2 files changed, 4 insertions(+) diff --git a/pygit2/remote.py b/pygit2/remote.py index 6a2a7b99f..11bc86c60 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -144,6 +144,9 @@ def name(self): @name.setter def name(self, value): + if not value: + raise ValueError("New remote name must be a non-empty string") + err = C.git_remote_rename(self._remote, to_str(value), ffi.NULL, ffi.NULL) check_error(err) diff --git a/test/test_remote.py b/test/test_remote.py index 54ba27c9e..9c95f80cd 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -65,6 +65,7 @@ def test_remote_rename(self): self.assertEqual('new', remote.name) self.assertRaisesAssign(ValueError, remote, 'name', '') + self.assertRaisesAssign(ValueError, remote, 'name', None) def test_remote_set_url(self): From 4c4968a2fb63dd7a147bcbef4830ac9afaad4b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 18 Apr 2014 12:17:54 +0200 Subject: [PATCH 0740/2237] Fix config documentation keyword The keyword for linking to a mehtod is 'meth', not 'method'. Setting the 'currentmodule' allows us to link without the 'pygit2' prefix in the link text. --- docs/config.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/config.rst b/docs/config.rst index 881fc1d70..5c091e752 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -22,11 +22,13 @@ The Config type aware that this may return multiple versions of each entry if they are set multiple times in the configuration files. +.. currentmodule:: pygit2 + The :class:`Config` Mapping interface. When using the mapping interface, the value is returned as a string. In order to apply the git-config parsing rules, you can use -:method:`Config.get_bool` or :method:`Config.get_int`. +:meth:`Config.get_bool` or :meth:`Config.get_int`. .. automethod:: pygit2.Config.get_bool .. automethod:: pygit2.Config.get_int From 569d396f0d5a99215b1fd5b058c7ab3646fac3a5 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Fri, 18 Apr 2014 13:44:25 -0700 Subject: [PATCH 0741/2237] Use repo.head.target instead of repo.head.get_object().hex --- pygit2/repository.py | 2 +- src/repository.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index 75650708e..e184ec2a6 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -78,7 +78,7 @@ def create_reference(self, name, target, force=False): Examples:: - repo.create_reference('refs/heads/foo', repo.head.get_object().hex) + repo.create_reference('refs/heads/foo', repo.head.target) repo.create_reference('refs/tags/foo', 'refs/heads/master') repo.create_reference('refs/tags/foo', 'bbb78a9cec580') """ diff --git a/src/repository.c b/src/repository.c index 2a606e13b..89120761c 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1077,8 +1077,7 @@ PyDoc_STRVAR(Repository_create_reference_direct__doc__, "\n" "Examples::\n" "\n" - " repo.create_reference_direct('refs/heads/foo',\n" - " repo.head.get_object().hex, False)"); + " repo.create_reference_direct('refs/heads/foo', repo.head.target, False)"); PyObject * Repository_create_reference_direct(Repository *self, PyObject *args, From c41c1a1c97f5dae70160e96f77ad10d259fe8ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 20 Apr 2014 18:18:25 +0200 Subject: [PATCH 0742/2237] Config: move to cffi This halves the amount of code we have to take into account for dealing with the config. There is a slight change in the API. Config.get_multivar() returns an iterator instead of a list, which lets us reuse code from the general iterator and is closer to libgit2's API. --- pygit2/__init__.py | 1 + pygit2/config.py | 259 +++++++++++++++++++++ pygit2/decl.h | 49 ++++ pygit2/errors.py | 7 +- pygit2/repository.py | 18 ++ src/config.c | 540 ------------------------------------------- src/config.h | 45 ---- src/pygit2.c | 7 - src/repository.c | 40 ---- src/types.h | 13 -- test/test_config.py | 13 +- 11 files changed, 339 insertions(+), 653 deletions(-) create mode 100644 pygit2/config.py delete mode 100644 src/config.c delete mode 100644 src/config.h diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 2dbda72bf..62954f64b 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -38,6 +38,7 @@ from .settings import Settings from .credentials import * from .remote import Remote, get_credentials +from .config import Config from .errors import check_error from .ffi import ffi, C, to_str diff --git a/pygit2/config.py b/pygit2/config.py new file mode 100644 index 000000000..a6f238835 --- /dev/null +++ b/pygit2/config.py @@ -0,0 +1,259 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2010-2014 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. + +# Import from the future +from __future__ import absolute_import, unicode_literals + +from _pygit2 import Oid + +from .ffi import ffi, C, to_str, is_string +from .errors import check_error, GitError +from .refspec import Refspec + +def assert_string(v, desc): + if not is_string(v): + raise TypeError("%s must be a string" % desc) + +class ConfigIterator(object): + + def __init__(self, config, ptr): + self._iter = ptr + self._config = config + + def __del__(self): + C.git_config_iterator_free(self._iter) + + def __iter__(self): + return self + + def _next_entry(self): + centry = ffi.new('git_config_entry **') + err = C.git_config_next(centry, self._iter) + check_error(err) + + return centry[0] + + def next(self): + return self.__next__() + + def __next__(self): + entry = self._next_entry() + name = ffi.string(entry.name).decode('utf-8') + value = ffi.string(entry.value).decode('utf-8') + + return name, value + +class ConfigMultivarIterator(ConfigIterator): + def __next__(self): + entry = self._next_entry() + + return ffi.string(entry.value).decode('utf-8') + +class Config(object): + """Git configuration management""" + + def __init__(self, path=None): + cconfig = ffi.new('git_config **') + + if not path: + err = C.git_config_new(cconfig) + else: + assert_string(path, "path") + err = C.git_config_open_ondisk(cconfig, to_str(path)) + + check_error(err, True) + self._config = cconfig[0] + + @classmethod + def from_c(cls, repo, ptr): + config = cls.__new__(cls) + config._repo = repo + config._config = ptr + + return config + + def __del__(self): + C.git_config_free(self._config) + + def _get(self, key): + assert_string(key, "key") + + cstr = ffi.new('char **') + err = C.git_config_get_string(cstr, self._config, to_str(key)) + + return err, cstr + + def _get_string(self, key): + err, cstr = self._get(key) + + if err == C.GIT_ENOTFOUND: + raise KeyError(key) + + check_error(err) + return cstr[0] + + def __contains__(self, key): + err, cstr = self._get(key) + + if err == C.GIT_ENOTFOUND: + return False + + check_error(err) + + return True + + def __getitem__(self, key): + val = self._get_string(key) + + return ffi.string(val).decode() + + def __setitem__(self, key, value): + assert_string(key, "key") + + err = 0 + if isinstance(value, bool): + err = C.git_config_set_bool(self._config, to_str(key), value) + elif isinstance(value, int): + err = C.git_config_set_int64(self._config, to_str(key), value) + else: + err = C.git_config_set_string(self._config, to_str(key), to_str(value)) + + check_error(err) + + def __delitem__(self, key): + assert_string(key, "key") + + err = C.git_config_delete_entry(self._config, to_str(key)) + check_error(err) + + def __iter__(self): + citer = ffi.new('git_config_iterator **') + err = C.git_config_iterator_new(citer, self._config) + check_error(err) + + return ConfigIterator(self, citer[0]) + + def get_multivar(self, name, regex=None): + """get_multivar(name[, regex]) -> [str, ...] + + Get each value of a multivar ''name'' as a list. The optional ''regex'' + parameter is expected to be a regular expression to filter the variables + we're interested in.""" + + assert_string(name, "name") + + citer = ffi.new('git_config_iterator **') + err = C.git_config_multivar_iterator_new(citer, self._config, to_str(name), to_str(regex)) + check_error(err) + + return ConfigMultivarIterator(self, citer[0]) + + def set_multivar(self, name, regex, value): + """set_multivar(name, regex, value) + + Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression + to indicate which values to replace""" + + assert_string(name, "name") + assert_string(regex, "regex") + assert_string(value, "value") + + err = C.git_config_set_multivar(self._config, to_str(name), to_str(regex), to_str(value)) + check_error(err) + + def get_bool(self, key): + """get_bool(key) -> Bool + + Look up *key* and parse its value as a boolean as per the git-config rules + + Truthy values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false', + 0, 'off' and 'no'""" + + val = self._get_string(key) + res = ffi.new('int *') + err = C.git_config_parse_bool(res, val) + check_error(err) + + return res[0] != 0 + + def get_int(self, key): + """get_int(key) -> int + + Look up *key* and parse its value as an integer as per the git-config rules. + + A value can have a suffix 'k', 'm' or 'g' which stand for 'kilo', 'mega' and + 'giga' respectively""" + + val = self._get_string(key) + res = ffi.new('int64_t *') + err = C.git_config_parse_int64(res, val) + check_error(err) + + return res[0] + + def add_file(self, path, level=0, force=0): + """add_file(path, level=0, force=0) + + Add a config file instance to an existing config.""" + + err = C.git_config_add_file_ondisk(self._config, to_str(path), level, force) + check_error(err) + + # + # Static methods to get specialized version of the config + # + + @staticmethod + def _from_found_config(fn): + buf = ffi.new('char []', C.GIT_PATH_MAX) + err = fn(buf, C.GIT_PATH_MAX) + check_error(err, True) + return Config(ffi.string(buf).decode()) + + @staticmethod + def get_system_config(): + """get_system_config() -> Config + + Return an object representing the system configuration file.""" + + return Config._from_found_config(C.git_config_find_system) + + @staticmethod + def get_global_config(): + """get_global_config() -> Config + + Return an object representing the global configuration file.""" + + return Config._from_found_config(C.git_config_find_global) + + @staticmethod + def get_xdg_config(): + """get_xdg_config() -> Config + + Return an object representing the global configuration file.""" + + return Config._from_found_config(C.git_config_find_xdg) diff --git a/pygit2/decl.h b/pygit2/decl.h index a64c32fc4..29d4e1c74 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -5,8 +5,11 @@ typedef ... git_push; typedef ... git_cred; typedef ... git_diff_file; typedef ... git_tree; +typedef ... git_config; +typedef ... git_config_iterator; #define GIT_OID_RAWSZ ... +#define GIT_PATH_MAX ... typedef struct git_oid { unsigned char id[20]; @@ -212,3 +215,49 @@ int git_clone(git_repository **out, const char *url, const char *local_path, const git_clone_options *options); + + +typedef enum { + GIT_CONFIG_LEVEL_SYSTEM = 1, + GIT_CONFIG_LEVEL_XDG = 2, + GIT_CONFIG_LEVEL_GLOBAL = 3, + GIT_CONFIG_LEVEL_LOCAL = 4, + GIT_CONFIG_LEVEL_APP = 5, + GIT_CONFIG_HIGHEST_LEVEL = -1, +} git_config_level_t; + +typedef struct { + const char *name; + const char *value; + git_config_level_t level; +} git_config_entry; + +int git_repository_config(git_config **out, git_repository *repo); +void git_config_free(git_config *cfg); + +int git_config_get_string(const char **out, const git_config *cfg, const char *name); +int git_config_set_string(git_config *cfg, const char *name, const char *value); +int git_config_set_bool(git_config *cfg, const char *name, int value); +int git_config_set_int64(git_config *cfg, const char *name, int64_t value); + +int git_config_parse_bool(int *out, const char *value); +int git_config_parse_int64(int64_t *out, const char *value); + +int git_config_delete_entry(git_config *cfg, const char *name); +int git_config_add_file_ondisk(git_config *cfg, + const char *path, + git_config_level_t level, + int force); + +int git_config_iterator_new(git_config_iterator **out, const git_config *cfg); +int git_config_next(git_config_entry **entry, git_config_iterator *iter); +void git_config_iterator_free(git_config_iterator *iter); + +int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp); +int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value); + +int git_config_new(git_config **out); +int git_config_open_ondisk(git_config **out, const char *path); +int git_config_find_system(char *out, size_t length); +int git_config_find_global(char *out, size_t length); +int git_config_find_xdg(char *out, size_t length); diff --git a/pygit2/errors.py b/pygit2/errors.py index b9f6c9bfa..398d08c72 100644 --- a/pygit2/errors.py +++ b/pygit2/errors.py @@ -33,7 +33,7 @@ from _pygit2 import GitError -def check_error(err): +def check_error(err, io=False): if err >= 0: return @@ -45,7 +45,10 @@ def check_error(err): if err in [C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EEXISTS, C.GIT_EAMBIGUOUS]: raise ValueError(message) elif err == C.GIT_ENOTFOUND: - raise KeyError(message) + if io: + raise IOError(message) + else: + raise KeyError(message) elif err == C.GIT_EINVALIDSPEC: raise ValueError(message) elif err == C.GIT_ITEROVER: diff --git a/pygit2/repository.py b/pygit2/repository.py index 107db9128..2827c28bb 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -38,6 +38,7 @@ from .ffi import ffi, C, to_str from .errors import check_error from .remote import Remote +from .config import Config class Repository(_Repository): @@ -110,6 +111,23 @@ def remotes(self): C.git_strarray_free(names) + # + # Configuration + # + @property + def config(self): + """The configuration file for this repository + + If a the configuration hasn't been set yet, the default config for + repository will be returned, including global and system configurations + (if they are available).""" + + cconfig = ffi.new('git_config **') + err = C.git_repository_config(cconfig, self._repo) + check_error(err) + + return Config.from_c(self, cconfig[0]) + # # References # diff --git a/src/config.c b/src/config.c deleted file mode 100644 index 07dbb408a..000000000 --- a/src/config.c +++ /dev/null @@ -1,540 +0,0 @@ -/* - * Copyright 2010-2014 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 "error.h" -#include "types.h" -#include "utils.h" -#include "config.h" - -extern PyTypeObject ConfigType; -extern PyTypeObject ConfigIterType; - - -PyObject * -wrap_config(char *c_path) { - int err; - PyObject *py_path; - Config *py_config; - - py_path = Py_BuildValue("(s)", c_path); - py_config = PyObject_New(Config, &ConfigType); - - err = Config_init(py_config, py_path, NULL); - if (err < 0) - return NULL; - - return (PyObject*) py_config; -} - - -int -Config_init(Config *self, PyObject *args, PyObject *kwds) -{ - char *path = NULL; - int err; - - if (kwds && PyDict_Size(kwds) > 0) { - PyErr_SetString(PyExc_TypeError, - "Config takes no keyword arguments"); - return -1; - } - - if (!PyArg_ParseTuple(args, "|s", &path)) - return -1; - - if (path == NULL) - err = git_config_new(&self->config); - else - err = git_config_open_ondisk(&self->config, path); - - if (err < 0) { - git_config_free(self->config); - - if (err == GIT_ENOTFOUND) - Error_set_exc(PyExc_IOError); - else - Error_set(err); - - return -1; - } - - return 0; -} - - -void -Config_dealloc(Config *self) -{ - git_config_free(self->config); - Py_TYPE(self)->tp_free(self); -} - -PyDoc_STRVAR(Config_get_global_config__doc__, - "get_global_config() -> Config\n" - "\n" - "Return an object representing the global configuration file."); - -PyObject * -Config_get_global_config(void) -{ - char path[GIT_PATH_MAX]; - int err; - - err = git_config_find_global(path, GIT_PATH_MAX); - if (err < 0) { - if (err == GIT_ENOTFOUND) { - PyErr_SetString(PyExc_IOError, "Global config file not found."); - return NULL; - } - - return Error_set(err); - } - - return wrap_config(path); -} - - -PyDoc_STRVAR(Config_get_system_config__doc__, - "get_system_config() -> Config\n" - "\n" - "Return an object representing the system configuration file."); - -PyObject * -Config_get_system_config(void) -{ - char path[GIT_PATH_MAX]; - int err; - - err = git_config_find_system(path, GIT_PATH_MAX); - if (err < 0) { - if (err == GIT_ENOTFOUND) { - PyErr_SetString(PyExc_IOError, "System config file not found."); - return NULL; - } - return Error_set(err); - } - - return wrap_config(path); -} - - -int -Config_contains(Config *self, PyObject *py_key) { - int err; - const char *c_value, *c_key; - PyObject *tkey; - - c_key = py_str_borrow_c_str(&tkey, py_key, NULL); - if (c_key == NULL) - return -1; - - err = git_config_get_string(&c_value, self->config, c_key); - Py_DECREF(tkey); - - if (err < 0) { - if (err == GIT_ENOTFOUND) - return 0; - - Error_set(err); - return -1; - } - - return 1; -} - -/* Get the C string value given a python string as key */ -static int -get_string(const char **key_out, Config *self, PyObject *py_key) -{ - PyObject *tkey; - const char *key; - int err; - - key = py_str_borrow_c_str(&tkey, py_key, NULL); - if (key == NULL) - return -1; - - err = git_config_get_string(key_out, self->config, key); - Py_CLEAR(tkey); - - if (err == GIT_ENOTFOUND) { - PyErr_SetObject(PyExc_KeyError, py_key); - return -1; - } - - if (err < 0) { - Error_set(err); - return -1; - } - - return 0; -} - -PyObject * -Config_getitem(Config *self, PyObject *py_key) -{ - int err; - const char *value_str; - - err = get_string(&value_str, self, py_key); - if (err < 0) - return NULL; - - return to_unicode(value_str, NULL, NULL); -} - -PyDoc_STRVAR(Config_get_bool__doc__, - "get_bool(key) -> Bool\n" - "\n" - "Look up *key* and parse its value as a boolean as per the git-config rules\n" - "\n" - "Truthy values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false',\n" - "0, 'off' and 'no'"); - -PyObject * -Config_get_bool(Config *self, PyObject *key) -{ - int err, value; - const char *value_str; - - err = get_string(&value_str, self, key); - if (err < 0) - return NULL; - - if ((err = git_config_parse_bool(&value, value_str)) < 0) - return NULL; - - return PyBool_FromLong(value); -} - -PyDoc_STRVAR(Config_get_int__doc__, - "get_int(key) -> int\n" - "\n" - "Look up *key* and parse its value as an integer as per the git-config rules\n" - "\n" - "A value can have a suffix 'k', 'm' or 'g' which stand for 'kilo', 'mega' and\n" - "'giga' respectively"); - -PyObject * -Config_get_int(Config *self, PyObject *key) -{ - int err; - int64_t value; - const char *value_str; - - err = get_string(&value_str, self, key); - if (err < 0) - return NULL; - - if ((err = git_config_parse_int64(&value, value_str)) < 0) - return NULL; - - return PyLong_FromLongLong(value); -} - -int -Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) -{ - int err; - const char *key, *value; - PyObject *tkey, *tvalue; - - key = py_str_borrow_c_str(&tkey, py_key, NULL); - if (key == NULL) - return -1; - - if (py_value == NULL) - err = git_config_delete_entry(self->config, key); - else if (PyBool_Check(py_value)) { - err = git_config_set_bool(self->config, key, - (int)PyObject_IsTrue(py_value)); - } else if (PyLong_Check(py_value)) { - err = git_config_set_int64(self->config, key, - (int64_t)PyLong_AsLong(py_value)); - } else { - value = py_str_borrow_c_str(&tvalue, py_value, NULL); - err = git_config_set_string(self->config, key, value); - Py_DECREF(tvalue); - } - - Py_DECREF(tkey); - if (err < 0) { - Error_set(err); - return -1; - } - return 0; -} - -PyDoc_STRVAR(Config_add_file__doc__, - "add_file(path, level=0, force=0)\n" - "\n" - "Add a config file instance to an existing config."); - -PyObject * -Config_add_file(Config *self, PyObject *args, PyObject *kwds) -{ - char *keywords[] = {"path", "level", "force", NULL}; - int err; - char *path; - unsigned int level = 0; - int force = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|Ii", keywords, - &path, &level, &force)) - return NULL; - - err = git_config_add_file_ondisk(self->config, path, level, force); - if (err < 0) - return Error_set_str(err, path); - - Py_RETURN_NONE; -} - - -PyDoc_STRVAR(Config_get_multivar__doc__, - "get_multivar(name[, regex]) -> [str, ...]\n" - "\n" - "Get each value of a multivar ''name'' as a list. The optional ''regex''\n" - "parameter is expected to be a regular expression to filter the variables\n" - "we're interested in."); - -PyObject * -Config_get_multivar(Config *self, PyObject *args) -{ - int err; - PyObject *list; - const char *name = NULL; - const char *regex = NULL; - git_config_iterator *iter; - git_config_entry *entry; - - if (!PyArg_ParseTuple(args, "s|s", &name, ®ex)) - return NULL; - - list = PyList_New(0); - err = git_config_multivar_iterator_new(&iter, self->config, name, regex); - if (err < 0) - return Error_set(err); - - while ((err = git_config_next(&entry, iter)) == 0) { - PyObject *item; - - item = to_unicode(entry->value, NULL, NULL); - if (item == NULL) { - git_config_iterator_free(iter); - return NULL; - } - - PyList_Append(list, item); - Py_CLEAR(item); - } - - git_config_iterator_free(iter); - if (err == GIT_ITEROVER) - err = 0; - - if (err < 0) - return Error_set(err); - - return list; -} - - -PyDoc_STRVAR(Config_set_multivar__doc__, - "set_multivar(name, regex, value)\n" - "\n" - "Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression\n" - "to indicate which values to replace"); - -PyObject * -Config_set_multivar(Config *self, PyObject *args) -{ - int err; - const char *name = NULL; - const char *regex = NULL; - const char *value = NULL; - - if (!PyArg_ParseTuple(args, "sss", &name, ®ex, &value)) - return NULL; - - err = git_config_set_multivar(self->config, name, regex, value); - if (err < 0) { - if (err == GIT_ENOTFOUND) - Error_set(err); - else - PyErr_SetNone(PyExc_TypeError); - return NULL; - } - - Py_RETURN_NONE; -} - -PyObject * -Config_iter(Config *self) -{ - ConfigIter *iter; - int err; - - iter = PyObject_New(ConfigIter, &ConfigIterType); - if (!iter) - return NULL; - - if ((err = git_config_iterator_new(&iter->iter, self->config)) < 0) - return Error_set(err); - - Py_INCREF(self); - iter->owner = self; - - return (PyObject*)iter; -} - -PyMethodDef Config_methods[] = { - METHOD(Config, get_system_config, METH_NOARGS | METH_STATIC), - METHOD(Config, get_global_config, METH_NOARGS | METH_STATIC), - METHOD(Config, add_file, METH_VARARGS | METH_KEYWORDS), - METHOD(Config, get_multivar, METH_VARARGS), - METHOD(Config, set_multivar, METH_VARARGS), - METHOD(Config, get_bool, METH_O), - METHOD(Config, get_int, METH_O), - {NULL} -}; - -PySequenceMethods Config_as_sequence = { - 0, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - 0, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - (objobjproc)Config_contains, /* sq_contains */ -}; - -PyMappingMethods Config_as_mapping = { - 0, /* mp_length */ - (binaryfunc)Config_getitem, /* mp_subscript */ - (objobjargproc)Config_setitem, /* mp_ass_subscript */ -}; - - -PyDoc_STRVAR(Config__doc__, "Configuration management."); - -PyTypeObject ConfigType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.Config", /* tp_name */ - sizeof(Config), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Config_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - &Config_as_sequence, /* tp_as_sequence */ - &Config_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - Config__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)Config_iter, /* tp_iter */ - 0, /* tp_iternext */ - Config_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Config_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - -void -ConfigIter_dealloc(ConfigIter *self) -{ - Py_CLEAR(self->owner); - git_config_iterator_free(self->iter); - PyObject_Del(self); -} - -PyObject * -ConfigIter_iternext(ConfigIter *self) -{ - int err; - git_config_entry *entry; - - if ((err = git_config_next(&entry, self->iter)) < 0) - return Error_set(err); - - return Py_BuildValue("ss", entry->name, entry->value); -} - -PyDoc_STRVAR(ConfigIter__doc__, "Configuration iterator."); - -PyTypeObject ConfigIterType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.ConfigIter", /* tp_name */ - sizeof(ConfigIter), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)ConfigIter_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 */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - ConfigIter__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)ConfigIter_iternext, /* tp_iternext */ - -}; diff --git a/src/config.h b/src/config.h deleted file mode 100644 index ffa0733bb..000000000 --- a/src/config.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2010-2014 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. - */ - -#ifndef INCLUDE_pygit2_config_h -#define INCLUDE_pygit2_config_h - -#define PY_SSIZE_T_CLEAN -#include -#include - -PyObject* wrap_config(char *c_path); -PyObject* Config_get_global_config(void); -PyObject* Config_get_system_config(void); -PyObject* Config_add_file(Config *self, PyObject *args, PyObject *kwds); -PyObject* Config_getitem(Config *self, PyObject *key); -PyObject* Config_foreach(Config *self, PyObject *args); -PyObject* Config_get_multivar(Config *self, PyObject *args); -PyObject* Config_set_multivar(Config *self, PyObject *args); -int Config_init(Config *self, PyObject *args, PyObject *kwds); -int Config_setitem(Config *self, PyObject *key, PyObject *value); -#endif diff --git a/src/pygit2.c b/src/pygit2.c index d88c0e408..2ed080863 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -66,8 +66,6 @@ extern PyTypeObject IndexType; extern PyTypeObject IndexEntryType; extern PyTypeObject IndexIterType; extern PyTypeObject WalkerType; -extern PyTypeObject ConfigType; -extern PyTypeObject ConfigIterType; extern PyTypeObject ReferenceType; extern PyTypeObject RefLogIterType; extern PyTypeObject RefLogEntryType; @@ -385,11 +383,6 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_XDG); ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_SYSTEM); - INIT_TYPE(ConfigType, NULL, PyType_GenericNew) - INIT_TYPE(ConfigIterType, NULL, NULL) - ADD_TYPE(m, Config) - ADD_TYPE(m, ConfigIter) - /* Blame */ INIT_TYPE(BlameType, NULL, NULL) INIT_TYPE(BlameIterType, NULL, NULL) diff --git a/src/repository.c b/src/repository.c index 1713136cf..acbe1d3fa 100644 --- a/src/repository.c +++ b/src/repository.c @@ -513,45 +513,6 @@ Repository_workdir__get__(Repository *self, void *closure) return to_path(c_path); } - -PyDoc_STRVAR(Repository_config__doc__, - "Get the configuration file for this repository.\n" - "\n" - "If a configuration file has not been set, the default config set for the\n" - "repository will be returned, including global and system configurations\n" - "(if they are available)."); - -PyObject * -Repository_config__get__(Repository *self) -{ - int err; - git_config *config; - Config *py_config; - - assert(self->repo); - - if (self->config == NULL) { - err = git_repository_config(&config, self->repo); - if (err < 0) - return Error_set(err); - - py_config = PyObject_New(Config, &ConfigType); - if (py_config == NULL) { - git_config_free(config); - return NULL; - } - - py_config->config = config; - self->config = (PyObject*)py_config; - /* We need 2 refs here. One is returned, one is kept internally. */ - Py_INCREF(self->config); - } else { - Py_INCREF(self->config); - } - - return self->config; -} - PyDoc_STRVAR(Repository_merge_base__doc__, "merge_base(oid, oid) -> Oid\n" "\n" @@ -1601,7 +1562,6 @@ PyGetSetDef Repository_getseters[] = { GETTER(Repository, head_is_unborn), GETTER(Repository, is_empty), GETTER(Repository, is_bare), - GETTER(Repository, config), GETTER(Repository, workdir), GETTER(Repository, default_signature), GETTER(Repository, _pointer), diff --git a/src/types.h b/src/types.h index e50f8341c..c39ade975 100644 --- a/src/types.h +++ b/src/types.h @@ -70,19 +70,6 @@ SIMPLE_TYPE(Tree, git_tree, tree) SIMPLE_TYPE(Blob, git_blob, blob) SIMPLE_TYPE(Tag, git_tag, tag) - -/* git_config */ -typedef struct { - PyObject_HEAD - git_config* config; -} Config; - -typedef struct { - PyObject_HEAD - Config *owner; - git_config_iterator *iter; -} ConfigIter; - /* git_note */ typedef struct { PyObject_HEAD diff --git a/test/test_config.py b/test/test_config.py index 937ed1bf9..5598c0580 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -115,9 +115,10 @@ def test_read(self): config.add_file(CONFIG_FILENAME, 0) self.assertTrue('this.that' in config) - self.assertEqual(len(config.get_multivar('this.that')), 2) - l = config.get_multivar('this.that', 'bar') - self.assertEqual(len(l), 1) + + self.assertEqual(2, len(list(config.get_multivar('this.that')))) + l = list(config.get_multivar('this.that', 'bar')) + self.assertEqual(1, len(l)) self.assertEqual(l[0], 'foobar') def test_write(self): @@ -155,16 +156,16 @@ def test_write(self): config.add_file(CONFIG_FILENAME, 5) self.assertTrue('this.that' in config) l = config.get_multivar('this.that', 'foo.*') - self.assertEqual(len(l), 2) + self.assertEqual(2, len(list(l))) config.set_multivar('this.that', '^.*beer', 'fool') - l = config.get_multivar('this.that', 'fool') + l = list(config.get_multivar('this.that', 'fool')) self.assertEqual(len(l), 1) self.assertEqual(l[0], 'fool') config.set_multivar('this.that', 'foo.*', 'foo-123456') l = config.get_multivar('this.that', 'foo.*') - self.assertEqual(len(l), 2) + self.assertEqual(2, len(list(l))) for i in l: self.assertEqual(i, 'foo-123456') From 1e13a1094915a48ff2a7a2552068986deaa88098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 20 Apr 2014 18:37:08 +0200 Subject: [PATCH 0743/2237] Config: expose config rules parsing Expose Config.parse_bool() and Config.parse_int() to parse text according to git-config rules. --- pygit2/config.py | 19 +++++++++++++++++++ test/test_config.py | 7 +++++++ 2 files changed, 26 insertions(+) diff --git a/pygit2/config.py b/pygit2/config.py index a6f238835..80f38dc43 100644 --- a/pygit2/config.py +++ b/pygit2/config.py @@ -223,6 +223,25 @@ def add_file(self, path, level=0, force=0): err = C.git_config_add_file_ondisk(self._config, to_str(path), level, force) check_error(err) + # + # Methods to parse a string according to the git-config rules + # + + @staticmethod + def parse_bool(text): + res = ffi.new('int *') + err = C.git_config_parse_bool(res, to_str(text)) + + return res[0] != 0 + + @staticmethod + def parse_int(text): + res = ffi.new('int64_t *') + err = C.git_config_parse_int64(res, to_str(text)) + check_error(err) + + return res[0] + # # Static methods to get specialized version of the config # diff --git a/test/test_config.py b/test/test_config.py index 5598c0580..9787c3e13 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -179,5 +179,12 @@ def test_iterator(self): self.assertTrue('core.bare' in lst) self.assertTrue(lst['core.bare']) + def test_parsing(self): + self.assertTrue(Config.parse_bool("on")) + self.assertTrue(Config.parse_bool("1")) + + self.assertEqual(5, Config.parse_int("5")) + self.assertEqual(1024, Config.parse_int("1k")) + if __name__ == '__main__': unittest.main() From 1c76d5667a9a9f77c84805f1cbf41f5396b1b07d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 23 Apr 2014 12:25:23 +0200 Subject: [PATCH 0744/2237] Blob: implement the memory buffer interface This allows us to expose access to the blob's data without the need to copy it into new buffer. --- src/blob.c | 60 +++++++++++++++++++++++++++++++++++++++++++++-- test/test_blob.py | 7 ++++++ 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/blob.c b/src/blob.c index 133d49423..de0141bf8 100644 --- a/src/blob.c +++ b/src/blob.c @@ -158,8 +158,58 @@ PyGetSetDef Blob_getseters[] = { {NULL} }; +static int +Blob_getbuffer(Blob *self, Py_buffer *view, int flags) +{ + return PyBuffer_FillInfo(view, (PyObject *) self, + (void *) git_blob_rawcontent(self->blob), + git_blob_rawsize(self->blob), 1, flags); +} + +#if PY_MAJOR_VERSION == 2 -PyDoc_STRVAR(Blob__doc__, "Blob objects."); +static Py_ssize_t +Blob_getreadbuffer(Blob *self, Py_ssize_t index, const void **ptr) +{ + if (index != 0) { + PyErr_SetString(PyExc_SystemError, + "accessing non-existent blob segment"); + return -1; + } + *ptr = (void *) git_blob_rawcontent(self->blob); + return git_blob_rawsize(self->blob); +} + +static Py_ssize_t +Blob_getsegcount(Blob *self, Py_ssize_t *lenp) +{ + if (lenp) + *lenp = git_blob_rawsize(self->blob); + + return 1; +} + +static PyBufferProcs Blob_as_buffer = { + (readbufferproc)Blob_getreadbuffer, + NULL, /* bf_getwritebuffer */ + (segcountproc)Blob_getsegcount, + NULL, /* charbufferproc */ + (getbufferproc)Blob_getbuffer, +}; + +#else + +static PyBufferProcs Blob_as_buffer = { + (getbufferproc)Blob_getbuffer, +}; + +#endif /* python 2 vs python 3 buffers */ + +PyDoc_STRVAR(Blob__doc__, "Blob object.\n" + "\n" + "Blobs implement the buffer interface, which means you can get access\n" + "to its data via `memoryview(blob)` without the need to create a copy." +); PyTypeObject BlobType = { PyVarObject_HEAD_INIT(NULL, 0) @@ -180,8 +230,14 @@ PyTypeObject BlobType = { 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ - 0, /* tp_as_buffer */ + &Blob_as_buffer, /* tp_as_buffer */ +#if PY_MAJOR_VERSION == 2 + Py_TPFLAGS_DEFAULT | /* tp_flags */ + Py_TPFLAGS_HAVE_GETCHARBUFFER | + Py_TPFLAGS_HAVE_NEWBUFFER, +#else Py_TPFLAGS_DEFAULT, /* tp_flags */ +#endif Blob__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ diff --git a/test/test_blob.py b/test/test_blob.py index da3db3e8f..9fd1d4f9e 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -74,6 +74,13 @@ def test_create_blob(self): self.assertEqual(BLOB_NEW_CONTENT, blob.data) self.assertEqual(len(BLOB_NEW_CONTENT), blob.size) self.assertEqual(BLOB_NEW_CONTENT, blob.read_raw()) + blob_buffer = memoryview(blob) + self.assertEqual(len(BLOB_NEW_CONTENT), len(blob_buffer)) + self.assertEqual(BLOB_NEW_CONTENT, blob_buffer) + def set_content(): + blob_buffer[:2] = b'hi' + + self.assertRaises(TypeError, set_content) def test_create_blob_fromworkdir(self): From 17ba85831bb9ab6ed28e0565d64db097b873968f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Wed, 23 Apr 2014 14:01:09 +0200 Subject: [PATCH 0745/2237] Drop official support for Python 2.6 --- .travis.yml | 1 - README.rst | 8 ++++++-- docs/index.rst | 7 +++++-- docs/install.rst | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4fbf40373..8440098d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: python python: - - "2.6" - "2.7" - "3.2" - "3.3" diff --git a/README.rst b/README.rst index 04c436622..48ffc0686 100644 --- a/README.rst +++ b/README.rst @@ -6,8 +6,12 @@ pygit2 - libgit2 bindings in Python :target: http://travis-ci.org/libgit2/pygit2 Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2 -implements the core of Git. Pygit2 works with Python 2.6, 2.7, 3.1, 3.2 and -3.3 +implements the core of Git. Pygit2 works with Python 2.7, 3.2, 3.3, 3.4 and +pypy. + +It is likely to work with Python 2.6 and 3.1, but these versions are not +officially supported. + Pygit2 links: diff --git a/docs/index.rst b/docs/index.rst index 6eccbd564..62f112fc3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,8 +10,11 @@ Welcome to pygit2's documentation! :target: http://travis-ci.org/libgit2/pygit2 Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2 -implements the core of Git. Pygit2 works with Python 2.6, 2.7, 3.1, 3.2 and -3.3 +implements the core of Git. Pygit2 works with Python 2.7, 3.2, 3.3, 3.4 and +pypy. + +It is likely to work with Python 2.6 and 3.1, but these versions are not +officially supported. Pygit2 links: diff --git a/docs/install.rst b/docs/install.rst index f74e2d7ac..5f058f22d 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -13,7 +13,7 @@ website: http://libgit2.github.com -Also, make sure you have Python 2.6+ installed together with the Python +Also, make sure you have Python 2.7 or 3.2+ installed together with the Python development headers. When those are installed, you can install pygit2: From c68de8e2b8a0c95b90f033dd7883460e39d1f112 Mon Sep 17 00:00:00 2001 From: Jun Omae Date: Mon, 28 Apr 2014 16:24:49 +0900 Subject: [PATCH 0746/2237] make build options.c with VS2008 compiler --- src/options.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/options.c b/src/options.c index abfa4656a..072a5ae24 100644 --- a/src/options.c +++ b/src/options.c @@ -41,10 +41,11 @@ get_search_path(long level) size_t len = 64; PyObject *py_path; int error; + char *tmp; do { len *= 2; - char *tmp = realloc(buf, len); + tmp = realloc(buf, len); if (!tmp) { free(buf); PyErr_NoMemory(); From 1fbe52c0f753d36ad5f71b9dd1be75c21532b223 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Tue, 29 Apr 2014 12:38:30 +0200 Subject: [PATCH 0747/2237] Add version check to C code --- src/types.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types.h b/src/types.h index c39ade975..15f7bfedb 100644 --- a/src/types.h +++ b/src/types.h @@ -32,6 +32,10 @@ #include #include +#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 20) +#error You need a compatible libgit2 version (v0.20.x) +#endif + /* * Python objects * From 0c62c83135a799535cda5ce0e33a3f72afc634ca Mon Sep 17 00:00:00 2001 From: "Ian P. McCullough" Date: Thu, 8 May 2014 08:50:21 -0400 Subject: [PATCH 0748/2237] Fix format string for Blob.diff(); Format string items out of order relative to docstring and outargs. --- src/blob.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blob.c b/src/blob.c index 133d49423..d1f5f3b08 100644 --- a/src/blob.c +++ b/src/blob.c @@ -63,7 +63,7 @@ Blob_diff(Blob *self, PyObject *args, PyObject *kwds) int err; char *keywords[] = {"blob", "flag", "old_as_path", "new_as_path", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!ssI", keywords, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!Iss", keywords, &BlobType, &py_blob, &opts.flags, &old_as_path, &new_as_path)) return NULL; From 6b3f9e92f7a67cdb14f86ef972bd41311361afec Mon Sep 17 00:00:00 2001 From: "Ian P. McCullough" Date: Thu, 8 May 2014 09:01:06 -0400 Subject: [PATCH 0749/2237] And on diff_to_buffer too. --- src/blob.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blob.c b/src/blob.c index d1f5f3b08..076827bfd 100644 --- a/src/blob.c +++ b/src/blob.c @@ -106,7 +106,7 @@ Blob_diff_to_buffer(Blob *self, PyObject *args, PyObject *kwds) char *keywords[] = {"buffer", "flag", "old_as_path", "buffer_as_path", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s#ssI", keywords, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s#Iss", keywords, &buffer, &buffer_len, &opts.flags, &old_as_path, &buffer_as_path)) return NULL; From b1bacdd8d58923859eb5b3f015a8f142c9420436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 18 May 2014 11:03:30 +0200 Subject: [PATCH 0750/2237] Wrap config snapshot functions These allow complex reads to come from the same version of the config. --- pygit2/config.py | 13 +++++++++++++ pygit2/decl.h | 2 ++ pygit2/repository.py | 13 +++++++++++++ 3 files changed, 28 insertions(+) diff --git a/pygit2/config.py b/pygit2/config.py index 6bbe6d20c..a757b9713 100644 --- a/pygit2/config.py +++ b/pygit2/config.py @@ -223,6 +223,19 @@ def add_file(self, path, level=0, force=0): err = C.git_config_add_file_ondisk(self._config, to_str(path), level, force) check_error(err) + def snapshot(self): + """Create a snapshot from this Config object + + This means that looking up multiple values will use the same version + of the configuration files + """ + + ccfg = ffi.new('git_config **') + err = C.git_config_snapshot(cfg, self._config) + check_error(err) + + return Config.from_c(self._repo, ccfg[0]) + # # Methods to parse a string according to the git-config rules # diff --git a/pygit2/decl.h b/pygit2/decl.h index a628f3b57..b26147292 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -255,6 +255,7 @@ typedef struct { } git_config_entry; int git_repository_config(git_config **out, git_repository *repo); +int git_repository_config_snapshot(git_config **out, git_repository *repo); void git_config_free(git_config *cfg); int git_config_get_string(const char **out, const git_config *cfg, const char *name); @@ -279,6 +280,7 @@ int git_config_multivar_iterator_new(git_config_iterator **out, const git_config int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value); int git_config_new(git_config **out); +int git_config_snapshot(git_config **out, git_config *config); int git_config_open_ondisk(git_config **out, const char *path); int git_config_find_system(git_buf *out); int git_config_find_global(git_buf *out); diff --git a/pygit2/repository.py b/pygit2/repository.py index 3f18c2c40..db047f5ee 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -128,6 +128,19 @@ def config(self): return Config.from_c(self, cconfig[0]) + @property + def config_snapshot(self): + """A snapshot for this repositiory's configuration + + This allows reads over multiple values to use the same version + of the configuration files""" + + cconfig = ffi.new('git_config **') + err = C.git_repository_config_snapshot(cconfig, self._repo) + check_error(err) + + return Config.from_c(self, cconfig[0]) + # # References # From 19be4b6aa4a8e0b06f9f7bbe0cfe071ee40e35f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 16 May 2014 03:18:14 +0200 Subject: [PATCH 0751/2237] clone: wrap clone_into() This allows the user to prepare the repository and remote with whichever custom settings they want before performing the "clone" proper. --- pygit2/__init__.py | 23 +++++++++++++++++++++++ pygit2/decl.h | 1 + test/test_repository.py | 9 ++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 62954f64b..9aa0c428e 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -129,4 +129,27 @@ def clone_repository( return Repository(path) +def clone_into(repo, remote, branch=None): + """Clone into an empty repository from the specified remote + + :param Repository repo: The empty repository into which to clone + + :param Remote remote: The remote from which to clone + + :param str branch: Branch to checkout after the clone. Pass None + to use the remotes's default branch. + + This allows you specify arbitrary repository and remote configurations + before performing the clone step itself. E.g. you can replicate git-clone's + '--mirror' option by setting a refspec of '+refs/*:refs/*', 'core.mirror' to true + and calling this function. + """ + + err = C.git_clone_into(repo._repo, remote._remote, ffi.NULL, to_str(branch)) + + if remote._stored_exception: + raise remote._stored_exception + + check_error(err) + settings = Settings() diff --git a/pygit2/decl.h b/pygit2/decl.h index 29d4e1c74..9f82b1cbd 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -216,6 +216,7 @@ int git_clone(git_repository **out, const char *local_path, const git_clone_options *options); +int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch); typedef enum { GIT_CONFIG_LEVEL_SYSTEM = 1, diff --git a/test/test_repository.py b/test/test_repository.py index 71214ccef..e70fe9779 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -40,7 +40,7 @@ # Import from pygit2 from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT -from pygit2 import init_repository, clone_repository, discover_repository +from pygit2 import init_repository, clone_repository, clone_into, discover_repository from pygit2 import Oid, Reference, hashfile import pygit2 from . import utils @@ -461,6 +461,13 @@ def test_clone_remote_name(self): self.assertFalse(repo.is_empty) self.assertEqual(repo.remotes[0].name, "custom_remote") + def test_clone_into(self): + repo_path = "./test/data/testrepo.git/" + repo = init_repository(os.path.join(self._temp_dir, "clone-into")) + remote = repo.create_remote("origin", 'file://' + os.path.realpath(repo_path)) + clone_into(repo, remote) + self.assertTrue('refs/remotes/origin/master' in repo.listall_references()) + def test_clone_with_credentials(self): credentials = pygit2.UserPass("libgit2", "libgit2") repo = clone_repository( From 97c0e476a31ccc7fc0d9a8c238892f99f1299582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 27 May 2014 18:06:28 +0200 Subject: [PATCH 0752/2237] Index: add failing tests for a standalone Index The index can exist purely as a data structure. Let's test that so we make sure we support that. --- test/test_index.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/test/test_index.py b/test/test_index.py index 7bed86bb3..eb7fa05d7 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -34,7 +34,7 @@ import tempfile import pygit2 -from pygit2 import Repository +from pygit2 import Repository, Index from . import utils @@ -206,5 +206,19 @@ def test_create_entry(self): tree_id = index.write_tree() self.assertEqual('60e769e57ae1d6a2ab75d8d253139e6260e1f912', str(tree_id)) +class StandaloneIndexTest(utils.RepoTestCase): + + def test_create_empty(self): + index = Index() + + def test_create_empty_read_tree_as_string(self): + index = Index() + # no repo associated, so we don't know where to read from + self.assertRaises(TypeError, index, 'read_tree', 'fd937514cb799514d4b81bb24c5fcfeb6472b245') + + def test_create_empty_read_tree(self): + index = Index() + index.read_tree(self.repo['fd937514cb799514d4b81bb24c5fcfeb6472b245']) + if __name__ == '__main__': unittest.main() From f69a57a82a05c161511cb4f651aa231dd6e05ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 27 May 2014 18:10:06 +0200 Subject: [PATCH 0753/2237] Index: make the file optional There is no obligation for an index to exist as a file at all. --- src/index.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/index.c b/src/index.c index 726974986..0cb8207b7 100644 --- a/src/index.c +++ b/src/index.c @@ -45,7 +45,7 @@ extern PyTypeObject RepositoryType; int Index_init(Index *self, PyObject *args, PyObject *kwds) { - char *path; + char *path = NULL; int err; if (kwds && PyDict_Size(kwds) > 0) { @@ -53,9 +53,10 @@ Index_init(Index *self, PyObject *args, PyObject *kwds) return -1; } - if (!PyArg_ParseTuple(args, "s", &path)) + if (!PyArg_ParseTuple(args, "|s", &path)) return -1; + self->repo = NULL; err = git_index_open(&self->index, path); if (err < 0) { Error_set_str(err, path); From 9e91a390cc39625ebc72d609e1be6d04c71fab25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 27 May 2014 18:24:53 +0200 Subject: [PATCH 0754/2237] Index: accept a tree for read_tree() An index may not have an associated repository, so giving it an id in that case is useless. Raise an error in that case and accept a Tree object to make the function useful then. --- docs/working-copy.rst | 8 ++++++++ src/index.c | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/docs/working-copy.rst b/docs/working-copy.rst index 76bcada87..88762b706 100644 --- a/docs/working-copy.rst +++ b/docs/working-copy.rst @@ -26,6 +26,14 @@ Custom entries:: >>> entry = pygit2.IndexEntry('README.md', blob_id, blob_filemode) >>> repo.index.add(entry) +The index fulfills a dual role as the in-memory representation of the +index file and data structure which represents a flat list of a +tree. You can use it independently of the index file, e.g. + + >>> index = pygit2.Index() + >>> entry = pygit2.IndexEntry('README.md', blob_id, blob_filemode) + >>> index.add(entry) + The Index type ==================== diff --git a/src/index.c b/src/index.c index 0cb8207b7..6a855f41a 100644 --- a/src/index.c +++ b/src/index.c @@ -426,26 +426,46 @@ Index_remove(Index *self, PyObject *args) PyDoc_STRVAR(Index_read_tree__doc__, "read_tree(tree)\n" "\n" - "Update the index file from the tree identified by the given oid."); + "Update the index file from the specified tree. The tree can be a Tree object or an Oid.\n" + "Using an Oid is only possible if this index is associated with a repository"); PyObject * Index_read_tree(Index *self, PyObject *value) { git_oid oid; - git_tree *tree; - int err; + git_tree *tree = NULL; + int err, need_free = 0; size_t len; len = py_oid_to_git_oid(value, &oid); - if (len == 0) - return NULL; + if (len == 0) { + Tree *py_tree; + if (!PyObject_TypeCheck(value, &TreeType)) { + return NULL; + } - err = git_tree_lookup_prefix(&tree, self->repo->repo, &oid, len); - if (err < 0) - return Error_set(err); + PyErr_Clear(); + py_tree = (Tree *) value; + tree = py_tree->tree; + } + + /* + * if the user passed in an id but we're not associated with a + * repo, we can't do anything + */ + if (tree == NULL && self->repo == NULL) { + PyErr_SetString(PyExc_TypeError, "id given but no associated repository"); + return NULL; + } else if (tree == NULL) { + need_free = 1; + err = git_tree_lookup_prefix(&tree, self->repo->repo, &oid, len); + if (err < 0) + return Error_set(err); + } err = git_index_read_tree(self->index, tree); - git_tree_free(tree); + if (need_free) + git_tree_free(tree); if (err < 0) return Error_set(err); From 6cf06ba9fe89e0673c0d8c96d1fc8cbae738e852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 2 Jun 2014 18:50:55 +0200 Subject: [PATCH 0755/2237] Rewrite init_repository using cffi --- pygit2/__init__.py | 6 ++++-- pygit2/decl.h | 9 ++++++++- src/pygit2.c | 32 -------------------------------- 3 files changed, 12 insertions(+), 35 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 9aa0c428e..a067a3bfe 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -29,7 +29,6 @@ from __future__ import absolute_import # Low level API -import _pygit2 from _pygit2 import * # High level API @@ -49,7 +48,10 @@ def init_repository(path, bare=False): If *bare* is True the repository will be bare, i.e. it will not have a working copy. """ - _pygit2.init_repository(path, bare) + crepository = ffi.new('git_repository **') + err = C.git_repository_init(crepository, to_str(path), bare) + check_error(err) + return Repository(path) diff --git a/pygit2/decl.h b/pygit2/decl.h index 9f82b1cbd..1fb28e7d2 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -254,7 +254,10 @@ int git_config_iterator_new(git_config_iterator **out, const git_config *cfg); int git_config_next(git_config_entry **entry, git_config_iterator *iter); void git_config_iterator_free(git_config_iterator *iter); -int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp); +int git_config_multivar_iterator_new(git_config_iterator **out, + const git_config *cfg, const char *name, + const char *regexp); + int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value); int git_config_new(git_config **out); @@ -262,3 +265,7 @@ int git_config_open_ondisk(git_config **out, const char *path); int git_config_find_system(char *out, size_t length); int git_config_find_global(char *out, size_t length); int git_config_find_xdg(char *out, size_t length); + + +int git_repository_init(git_repository **out, const char *path, + unsigned is_bare); diff --git a/src/pygit2.c b/src/pygit2.c index 2ed080863..4808f7437 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -82,37 +82,6 @@ extern PyTypeObject MergeResultType; -PyDoc_STRVAR(init_repository__doc__, - "init_repository(path, bare)\n" - "\n" - "Creates a new Git repository in the given path.\n" - "\n" - "Arguments:\n" - "\n" - "path\n" - " Path where to create the repository.\n" - "\n" - "bare\n" - " Whether the repository will be bare or not.\n"); - -PyObject * -init_repository(PyObject *self, PyObject *args) { - git_repository *repo; - const char *path; - unsigned int bare; - int err; - - if (!PyArg_ParseTuple(args, "sI", &path, &bare)) - return NULL; - - err = git_repository_init(&repo, path, bare); - if (err < 0) - return Error_set_str(err, path); - - git_repository_free(repo); - Py_RETURN_NONE; -}; - PyDoc_STRVAR(discover_repository__doc__, "discover_repository(path[, across_fs[, ceiling_dirs]]) -> str\n" "\n" @@ -186,7 +155,6 @@ hash(PyObject *self, PyObject *args) PyMethodDef module_methods[] = { - {"init_repository", init_repository, METH_VARARGS, init_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, From 95e6593625f2a870c8729bc65537b19f0b5df8aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 3 Jun 2014 12:52:46 +0200 Subject: [PATCH 0756/2237] init_repository now wraps git_repository_init_ext Fixes #347 --- pygit2/__init__.py | 46 +++++++++++++++++- pygit2/decl.h | 113 ++++++++++++++++++++++++++++++++++++--------- pygit2/ffi.py | 9 ++-- 3 files changed, 139 insertions(+), 29 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index a067a3bfe..eed50c81a 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -41,17 +41,59 @@ from .errors import check_error from .ffi import ffi, C, to_str -def init_repository(path, bare=False): +def init_repository(path, bare=False, + flags=C.GIT_REPOSITORY_INIT_MKPATH, + mode=0, + workdir_path=None, + description=None, + template_path=None, + initial_head=None, + origin_url=None): """ Creates a new Git repository in the given *path*. If *bare* is True the repository will be bare, i.e. it will not have a working copy. + + The *flags* may be a combination of: + + - GIT_REPOSITORY_INIT_BARE (overriden by the *bare* parameter) + - GIT_REPOSITORY_INIT_NO_REINIT + - GIT_REPOSITORY_INIT_NO_DOTGIT_DIR + - GIT_REPOSITORY_INIT_MKDIR + - GIT_REPOSITORY_INIT_MKPATH (set by default) + - GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE + + The *mode* parameter may be any of GIT_REPOSITORY_SHARED_UMASK (default), + GIT_REPOSITORY_SHARED_GROUP or GIT_REPOSITORY_INIT_SHARED_ALL, or a custom + value. + + The *workdir_path*, *description*, *template_path*, *initial_head* and + *origin_url* are all strings. + + See libgit2's documentation on git_repository_init_ext for further details. """ + # Pre-process input parameters + if bare: + flags |= C.GIT_REPOSITORY_INIT_BARE + + # Options + options = ffi.new('git_repository_init_options *') + options.version = 1 + options.flags = flags + options.mode = mode + options.workdir_path = to_str(workdir_path) + options.description = to_str(description) + options.template_path = to_str(template_path) + options.initial_head = to_str(initial_head) + options.origin_url = to_str(origin_url) + + # Call crepository = ffi.new('git_repository **') - err = C.git_repository_init(crepository, to_str(path), bare) + err = C.git_repository_init_ext(crepository, to_str(path), options) check_error(err) + # Ok return Repository(path) diff --git a/pygit2/decl.h b/pygit2/decl.h index 1fb28e7d2..fd1c22ee5 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -5,8 +5,6 @@ typedef ... git_push; typedef ... git_cred; typedef ... git_diff_file; typedef ... git_tree; -typedef ... git_config; -typedef ... git_config_iterator; #define GIT_OID_RAWSZ ... #define GIT_PATH_MAX ... @@ -20,6 +18,7 @@ typedef struct git_strarray { size_t count; } git_strarray; + typedef enum { GIT_OK = 0, GIT_ERROR = -1, @@ -71,11 +70,13 @@ typedef enum { GIT_DIRECTION_PUSH = 1 } git_direction; + typedef enum { - GIT_CREDTYPE_USERPASS_PLAINTEXT = ..., - GIT_CREDTYPE_SSH_KEY = ..., - GIT_CREDTYPE_SSH_CUSTOM = ..., - GIT_CREDTYPE_DEFAULT = ..., + GIT_CREDTYPE_USERPASS_PLAINTEXT, + GIT_CREDTYPE_SSH_KEY, + GIT_CREDTYPE_SSH_CUSTOM, + GIT_CREDTYPE_DEFAULT, + ... } git_credtype_t; typedef struct git_remote_callbacks { @@ -90,10 +91,12 @@ typedef struct git_remote_callbacks { int git_remote_list(git_strarray *out, git_repository *repo); int git_remote_load(git_remote **out, git_repository *repo, const char *name); -int git_remote_create(git_remote **out, - git_repository *repo, - const char *name, - const char *url); +int git_remote_create( + git_remote **out, + git_repository *repo, + const char *name, + const char *url); + const char * git_remote_name(const git_remote *remote); typedef int (*git_remote_rename_problem_cb)(const char *problematic_refspec, void *payload); int git_remote_rename(git_remote *remote, @@ -125,9 +128,10 @@ int git_push_add_refspec(git_push *push, const char *refspec); int git_push_finish(git_push *push); int git_push_unpack_ok(git_push *push); -int git_push_status_foreach(git_push *push, - int (*cb)(const char *ref, const char *msg, void *data), - void *data); +int git_push_status_foreach( + git_push *push, + int (*cb)(const char *ref, const char *msg, void *data), + void *data); int git_push_update_tips(git_push *push); void git_push_free(git_push *push); @@ -155,6 +159,10 @@ int git_cred_ssh_key_new( const char *privatekey, const char *passphrase); +/* + * git_checkout + */ + typedef enum { ... } git_checkout_notify_t; typedef int (*git_checkout_notify_cb)( @@ -199,6 +207,10 @@ typedef struct git_checkout_opts { } git_checkout_opts; +/* + * git_clone + */ + typedef struct git_clone_options { unsigned int version; @@ -212,11 +224,22 @@ typedef struct git_clone_options { } git_clone_options; int git_clone(git_repository **out, - const char *url, - const char *local_path, - const git_clone_options *options); + const char *url, + const char *local_path, + const git_clone_options *options); + +int git_clone_into( + git_repository *repo, + git_remote *remote, + const git_checkout_opts *co_opts, + const char *branch); -int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch); +/* + * git_config + */ + +typedef ... git_config; +typedef ... git_config_iterator; typedef enum { GIT_CONFIG_LEVEL_SYSTEM = 1, @@ -254,11 +277,17 @@ int git_config_iterator_new(git_config_iterator **out, const git_config *cfg); int git_config_next(git_config_entry **entry, git_config_iterator *iter); void git_config_iterator_free(git_config_iterator *iter); -int git_config_multivar_iterator_new(git_config_iterator **out, - const git_config *cfg, const char *name, - const char *regexp); +int git_config_multivar_iterator_new( + git_config_iterator **out, + const git_config *cfg, + const char *name, + const char *regexp); -int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value); +int git_config_set_multivar( + git_config *cfg, + const char *name, + const char *regexp, + const char *value); int git_config_new(git_config **out); int git_config_open_ondisk(git_config **out, const char *path); @@ -267,5 +296,43 @@ int git_config_find_global(char *out, size_t length); int git_config_find_xdg(char *out, size_t length); -int git_repository_init(git_repository **out, const char *path, - unsigned is_bare); +/* + * git_repository_init + */ +typedef enum { + GIT_REPOSITORY_INIT_BARE, + GIT_REPOSITORY_INIT_NO_REINIT, + GIT_REPOSITORY_INIT_NO_DOTGIT_DIR, + GIT_REPOSITORY_INIT_MKDIR, + GIT_REPOSITORY_INIT_MKPATH, + GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE, + ... +} git_repository_init_flag_t; + +typedef enum { + GIT_REPOSITORY_INIT_SHARED_UMASK, + GIT_REPOSITORY_INIT_SHARED_GROUP, + GIT_REPOSITORY_INIT_SHARED_ALL, + ... +} git_repository_init_mode_t; + +typedef struct { + unsigned int version; + uint32_t flags; + uint32_t mode; + const char *workdir_path; + const char *description; + const char *template_path; + const char *initial_head; + const char *origin_url; +} git_repository_init_options; + +int git_repository_init( + git_repository **out, + const char *path, + unsigned is_bare); + +int git_repository_init_ext( + git_repository **out, + const char *repo_path, + git_repository_init_options *opts); diff --git a/pygit2/ffi.py b/pygit2/ffi.py index 468b9bc2f..0dba2f34b 100644 --- a/pygit2/ffi.py +++ b/pygit2/ffi.py @@ -38,7 +38,7 @@ if major_version < 3: def to_str(s, encoding='utf-8', errors='strict'): - if s == ffi.NULL or s == None: + if s == ffi.NULL or s is None: return ffi.NULL if isinstance(s, unicode): @@ -48,7 +48,7 @@ def to_str(s, encoding='utf-8', errors='strict'): return s else: def to_str(s, encoding='utf-8', errors='strict'): - if s == ffi.NULL or s == None: + if s == ffi.NULL or s is None: return ffi.NULL if isinstance(s, bytes): @@ -108,7 +108,7 @@ def strings_to_strarray(l): decl_path = path.join(dir_path, 'decl.h') with codecs.open(decl_path, 'r', 'utf-8') as header: - ffi.cdef(header.read()) + ffi.cdef(header.read()) # if LIBGIT2 exists, set build and link against that version libgit2_path = getenv('LIBGIT2') @@ -118,4 +118,5 @@ def strings_to_strarray(l): include_dirs = [path.join(libgit2_path, 'include')] library_dirs = [path.join(libgit2_path, 'lib')] -C = ffi.verify("#include ", libraries=["git2"], include_dirs=include_dirs, library_dirs=library_dirs) +C = ffi.verify("#include ", libraries=["git2"], + include_dirs=include_dirs, library_dirs=library_dirs) From 491e352e4162542107e450f0223f87938b98f26c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 7 Jun 2014 21:31:47 +0200 Subject: [PATCH 0757/2237] Update to latest libgit2 --- pygit2/decl.h | 8 ++++++++ src/repository.c | 12 +++++++----- test/test_repository.py | 8 ++++---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/pygit2/decl.h b/pygit2/decl.h index b26147292..1d65df2c5 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -220,6 +220,13 @@ typedef struct git_checkout_options { const char *their_label; } git_checkout_options; +typedef enum { + GIT_CLONE_LOCAL_AUTO, + GIT_CLONE_LOCAL, + GIT_CLONE_NO_LOCAL, + GIT_CLONE_LOCAL_NO_LINKS, +} git_clone_local_t; + typedef struct git_clone_options { unsigned int version; @@ -228,6 +235,7 @@ typedef struct git_clone_options { int bare; int ignore_cert_errors; + git_clone_local_t local; const char *remote_name; const char* checkout_branch; git_signature *signature; diff --git a/src/repository.c b/src/repository.c index b2409ec27..0b12053a4 100644 --- a/src/repository.c +++ b/src/repository.c @@ -546,13 +546,14 @@ Repository_merge_base(Repository *self, PyObject *args) } PyDoc_STRVAR(Repository_merge_analysis__doc__, - "merge_analysis(id) -> Integer\n" + "merge_analysis(id) -> (Integer, Integer)\n" "\n" "Analyzes the given branch and determines the opportunities for merging\n" "them into the HEAD of the repository\n" "\n" - "The returned value is a mixture of the GIT_MERGE_ANALYSIS_NONE, _NORMAL,\n" - " _UP_TO_DATE, _FASTFORWARD and _UNBORN flags"); + "The first returned value is a mixture of the GIT_MERGE_ANALYSIS_NONE, _NORMAL,\n" + " _UP_TO_DATE, _FASTFORWARD and _UNBORN flags.\n" + "The second value is the user's preference from 'merge.ff'"); PyObject * Repository_merge_analysis(Repository *self, PyObject *py_id) @@ -562,6 +563,7 @@ Repository_merge_analysis(Repository *self, PyObject *py_id) git_oid id; git_merge_head *merge_head; git_merge_analysis_t analysis; + git_merge_preference_t preference; len = py_oid_to_git_oid(py_id, &id); if (len == 0) @@ -571,13 +573,13 @@ Repository_merge_analysis(Repository *self, PyObject *py_id) if (err < 0) return Error_set(err); - err = git_merge_analysis(&analysis, self->repo, (const git_merge_head **) &merge_head, 1); + err = git_merge_analysis(&analysis, &preference, self->repo, (const git_merge_head **) &merge_head, 1); git_merge_head_free(merge_head); if (err < 0) return Error_set(err); - return PyLong_FromLong(analysis); + return Py_BuildValue("(ii)", analysis, preference); } PyDoc_STRVAR(Repository_merge__doc__, diff --git a/test/test_repository.py b/test/test_repository.py index c35b0fd99..f40409287 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -313,7 +313,7 @@ def test_merge_none(self): def test_merge_analysis_uptodate(self): branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533' branch_id = self.repo.get(branch_head_hex).id - analysis = self.repo.merge_analysis(branch_id) + analysis, preference = self.repo.merge_analysis(branch_id) self.assertTrue(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) self.assertFalse(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) @@ -322,7 +322,7 @@ def test_merge_analysis_uptodate(self): def test_merge_analysis_fastforward(self): branch_head_hex = 'e97b4cfd5db0fb4ebabf4f203979ca4e5d1c7c87' branch_id = self.repo.get(branch_head_hex).id - analysis = self.repo.merge_analysis(branch_id) + analysis, preference = self.repo.merge_analysis(branch_id) self.assertFalse(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) self.assertTrue(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) self.assertEqual({}, self.repo.status()) @@ -330,7 +330,7 @@ def test_merge_analysis_fastforward(self): def test_merge_no_fastforward_no_conflicts(self): branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' branch_id = self.repo.get(branch_head_hex).id - analysis= self.repo.merge_analysis(branch_id) + analysis, preference = self.repo.merge_analysis(branch_id) self.assertFalse(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) self.assertFalse(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) # Asking twice to assure the reference counting is correct @@ -341,7 +341,7 @@ def test_merge_no_fastforward_conflicts(self): branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' branch_id = self.repo.get(branch_head_hex).id - analysis = self.repo.merge_analysis(branch_id) + analysis, preference = self.repo.merge_analysis(branch_id) self.assertFalse(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) self.assertFalse(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) From d3af09e86deba467daa8f6e058697c0f082561cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 7 Jun 2014 21:45:10 +0200 Subject: [PATCH 0758/2237] Adjust to clone_into signature change --- pygit2/__init__.py | 2 +- pygit2/decl.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index eed50c81a..b4faa031b 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -189,7 +189,7 @@ def clone_into(repo, remote, branch=None): and calling this function. """ - err = C.git_clone_into(repo._repo, remote._remote, ffi.NULL, to_str(branch)) + err = C.git_clone_into(repo._repo, remote._remote, ffi.NULL, to_str(branch), ffi.NULL) if remote._stored_exception: raise remote._stored_exception diff --git a/pygit2/decl.h b/pygit2/decl.h index 603509b92..862f867bc 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -262,7 +262,8 @@ int git_clone_into( git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, - const char *branch); + const char *branch, + const git_signature *signature); /* * git_config From 130fff6f2c062235dade5b7ff6b7b74e517f9a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 8 Jun 2014 20:35:15 +0200 Subject: [PATCH 0759/2237] Bump required libgit2 version to 0.21 --- src/types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types.h b/src/types.h index 806e58978..e69b489eb 100644 --- a/src/types.h +++ b/src/types.h @@ -32,8 +32,8 @@ #include #include -#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 20) -#error You need a compatible libgit2 version (v0.20.x) +#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 21) +#error You need a compatible libgit2 version (v0.21.x) #endif /* From bde58d972742707094eeb16ac7b1d9dfdf75f342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 8 Jun 2014 20:35:21 +0200 Subject: [PATCH 0760/2237] Remote: make renaming take a method call Renaming a remote in pygit2 has been done via Remote.name= up to now, but this is inherently unsafe, as it provides no way to pass up the refspecs that libgit2 was unable to remap. In fact, if there ever was such problem, we would have segfaulted. libgit2 now provides a much more direct way of getting back the results, so expose it as the return value of Remote.rename(). This also removes the hint that a rename might be something that happens only to the in-memory structure. --- pygit2/decl.h | 10 +++++----- pygit2/remote.py | 19 +++++++++++++++---- test/test_remote.py | 9 +++++---- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/pygit2/decl.h b/pygit2/decl.h index 862f867bc..d3b5b022d 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -116,11 +116,11 @@ int git_remote_create( const char *url); const char * git_remote_name(const git_remote *remote); -typedef int (*git_remote_rename_problem_cb)(const char *problematic_refspec, void *payload); -int git_remote_rename(git_remote *remote, - const char *new_name, - git_remote_rename_problem_cb callback, - void *payload); + +int git_remote_rename( + git_strarray *problems, + git_remote *remote, + const char *new_name); const char * git_remote_url(const git_remote *remote); int git_remote_set_url(git_remote *remote, const char* url); const char * git_remote_pushurl(const git_remote *remote); diff --git a/pygit2/remote.py b/pygit2/remote.py index 890e074e6..2b7f32d79 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -142,14 +142,25 @@ def name(self): return maybe_string(C.git_remote_name(self._remote)) - @name.setter - def name(self, value): - if not value: + def rename(self, new_name): + """Rename this remote + + Returns a list of fetch refspecs which were not in the standard format + and thus could not be remapped + """ + + if not new_name: raise ValueError("New remote name must be a non-empty string") - err = C.git_remote_rename(self._remote, to_str(value), ffi.NULL, ffi.NULL) + problems = ffi.new('git_strarray *') + err = C.git_remote_rename(problems, self._remote, to_str(new_name)) check_error(err) + ret = strarray_to_strings(problems) + C.git_strarray_free(problems) + + return ret + @property def url(self): """Url of the remote""" diff --git a/test/test_remote.py b/test/test_remote.py index 9c95f80cd..57814a386 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -61,11 +61,12 @@ def test_remote_rename(self): remote = self.repo.remotes[0] self.assertEqual(REMOTE_NAME, remote.name) - remote.name = 'new' + problems = remote.rename('new') + self.assertEqual([], problems) self.assertEqual('new', remote.name) - self.assertRaisesAssign(ValueError, remote, 'name', '') - self.assertRaisesAssign(ValueError, remote, 'name', None) + self.assertRaises(ValueError, remote.rename, '') + self.assertRaises(ValueError, remote.rename, None) def test_remote_set_url(self): @@ -153,7 +154,7 @@ def test_remote_list(self): def test_remote_save(self): remote = self.repo.remotes[0] - remote.name = 'new-name' + remote.rename('new-name') remote.url = 'http://example.com/test.git' remote.save() From 981fc32af6818faf92a7e3b3a9e688db05dc7bea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 9 Jun 2014 09:19:09 +0200 Subject: [PATCH 0761/2237] travis: test 3.4 too --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 8440098d0..9f31e6fc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ python: - "2.7" - "3.2" - "3.3" + - "3.4" - "pypy" env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib From bd322fa1320d83d027e085a71b2b7ae57aefdfb5 Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Sun, 8 Jun 2014 19:23:13 +0100 Subject: [PATCH 0762/2237] Correct LIBGIT2_VERSION name and add documentation LIBGIT2_VERSION was previously recorded as LIBGIT2_VER_VERSION which is incorrect. We also add basic explanations to all the constants so that the page is a little less bare. Perhaps this should be done as autodoc style comments in the code but I guess not. --- docs/general.rst | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/general.rst b/docs/general.rst index bf0b3c164..b71e6e7bb 100644 --- a/docs/general.rst +++ b/docs/general.rst @@ -6,14 +6,45 @@ General :local: +Top level constants and exceptions from the library. + Constants ========= +The following constants provide information about the version of the libgit2 +library that has been built against. The version number has a +``MAJOR.MINOR.REVISION`` format. + .. py:data:: LIBGIT2_VER_MAJOR + + Integer value of the major version number. For example, for the version + ``0.20.0``:: + + >>> print LIBGIT2_VER_MAJOR + 0 + .. py:data:: LIBGIT2_VER_MINOR + + Integer value of the minor version number. For example, for the version + ``0.20.0``:: + + >>> print LIBGIT2_VER_MINOR + 20 + .. py:data:: LIBGIT2_VER_REVISION -.. py:data:: LIBGIT2_VER_VERSION + Integer value of the revision version number. For example, for the version + ``0.20.0``:: + + >>> print LIBGIT2_VER_REVISION + 0 + +.. py:data:: LIBGIT2_VERSION + + The libgit2 version number as a string:: + + >>> print LIBGIT2_VERSION + '0.20.0' Errors ====== @@ -22,3 +53,4 @@ Errors :members: :show-inheritance: :undoc-members: + From 28ae47f42bcfee8ce5b33660798f6483a3e0f45d Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Mon, 9 Jun 2014 22:28:04 +0100 Subject: [PATCH 0763/2237] Provide a doc example for discover_repository To clarify the behaviour and usage. --- docs/repository.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/repository.rst b/docs/repository.rst index c2bd0c20c..1ce6191de 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -33,6 +33,11 @@ Functions .. autofunction:: pygit2.discover_repository + Example:: + + >>> current_working_directory = os.getcwd() + >>> repository_path = discover_repository(current_working_directory) + >>> repo = Repository(repository_path) The Repository class From 83ccdd9c1fda5337e7b61324f25760f8a4850479 Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Sun, 15 Jun 2014 11:28:26 +0100 Subject: [PATCH 0764/2237] Explain that reference targets are writable It might seem like a really obvious point to make but without emphasizing it, it isn't completely clear. I would like to mention this in the Branch section as well for how to point a branch at another commit but I can't see how to smoothly slide it in. --- src/reference.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/reference.c b/src/reference.c index e597e353a..dbf68105a 100644 --- a/src/reference.c +++ b/src/reference.c @@ -202,7 +202,10 @@ Reference_resolve(Reference *self, PyObject *args) PyDoc_STRVAR(Reference_target__doc__, "The reference target: If direct the value will be an Oid object, if it\n" "is symbolic it will be an string with the full name of the target\n" - "reference."); + "reference.\n" + "\n" + "The target is writable. Setting the Reference's target to another Oid\n" + "object will direct the reference to that Oid instead."); PyObject * Reference_target__get__(Reference *self) From 7296b921cceef795f55674479db6a8b32bfce09c Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Sun, 15 Jun 2014 11:30:33 +0100 Subject: [PATCH 0765/2237] Fix spelling typo --- docs/references.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/references.rst b/docs/references.rst index 363e3eebe..6ede2a712 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -45,7 +45,7 @@ Example. These two lines are equivalent:: Branches ==================== -Branches inherit from References, and additionally provide spetialized +Branches inherit from References, and additionally provide specialized accessors for some unique features. .. automethod:: pygit2.Repository.listall_branches From 1f111c08b631b38985f67bbc36d3fadfc4db152c Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Sun, 15 Jun 2014 11:39:24 +0100 Subject: [PATCH 0766/2237] Provide example for Reference.log_append I would have found this useful when trying to do reflog additions. It might not be massively complex but examples always help. --- docs/references.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/references.rst b/docs/references.rst index 6ede2a712..cccd68aa1 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -27,6 +27,23 @@ The Reference type .. automethod:: pygit2.Reference.resolve .. automethod:: pygit2.Reference.log .. automethod:: pygit2.Reference.log_append + + Example:: + + >>> branch = repository.lookup_reference("refs/heads/master") + >>> branch.target = another_commit.id + >>> committer = Signature('Cecil Committer', 'cecil@committers.tld') + >>> branch.log_append(another_commit.id, committer, + "changed branch target using pygit2") + + This creates a reflog entry in ``git reflog master`` which looks like:: + + 7296b92 master@{10}: changed branch target using pygit2 + + In order to make an entry in ``git reflog``, ie. the reflog for ``HEAD``, you + have to get the Reference object for ``HEAD`` and call ``log_append`` on + that. + .. automethod:: pygit2.Reference.get_object From 791b39433c8c8d7356d47ac030386256c57620d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 21 Jun 2014 10:23:52 +0200 Subject: [PATCH 0767/2237] C coding style: remove tabs --- src/blame.c | 14 +++--- src/options.c | 131 ++++++++++++++++++++++++------------------------ src/reference.c | 2 +- 3 files changed, 74 insertions(+), 73 deletions(-) diff --git a/src/blame.c b/src/blame.c index b238f050a..4fe1770de 100644 --- a/src/blame.c +++ b/src/blame.c @@ -70,18 +70,18 @@ wrap_blame_hunk(const git_blame_hunk *hunk, Blame *blame) py_hunk->final_commit_id = git_oid_allocfmt(&hunk->final_commit_id); py_hunk->final_start_line_number = hunk->final_start_line_number; - py_hunk->final_signature = NULL; - if (hunk->final_signature) - git_signature_dup(&py_hunk->final_signature, hunk->final_signature); + py_hunk->final_signature = NULL; + if (hunk->final_signature) + git_signature_dup(&py_hunk->final_signature, hunk->final_signature); py_hunk->orig_commit_id = git_oid_allocfmt(&hunk->orig_commit_id); py_hunk->orig_path = hunk->orig_path != NULL ? - strdup(hunk->orig_path) : NULL; + strdup(hunk->orig_path) : NULL; py_hunk->orig_start_line_number = hunk->orig_start_line_number; - py_hunk->orig_signature = NULL; - if (hunk->orig_signature) - git_signature_dup(&py_hunk->orig_signature, hunk->orig_signature); + py_hunk->orig_signature = NULL; + if (hunk->orig_signature) + git_signature_dup(&py_hunk->orig_signature, hunk->orig_signature); py_hunk->boundary = hunk->boundary; } diff --git a/src/options.c b/src/options.c index 1679a93d2..f6eaa5ec4 100644 --- a/src/options.c +++ b/src/options.c @@ -70,94 +70,95 @@ option(PyObject *self, PyObject *args) option = PyLong_AsLong(py_option); - switch (option) { - case GIT_OPT_GET_SEARCH_PATH: - { - PyObject *py_level; + switch (option) { + case GIT_OPT_GET_SEARCH_PATH: + { + PyObject *py_level; - py_level = PyTuple_GetItem(args, 1); - if (!py_level) - return NULL; + py_level = PyTuple_GetItem(args, 1); + if (!py_level) + return NULL; - if (!PyLong_Check(py_level)) - goto on_non_integer; + if (!PyLong_Check(py_level)) + goto on_non_integer; - return get_search_path(PyLong_AsLong(py_level)); - break; - } + return get_search_path(PyLong_AsLong(py_level)); + break; + } + + case GIT_OPT_SET_SEARCH_PATH: + { + PyObject *py_level, *py_path, *tpath; + const char *path; + int err; - case GIT_OPT_SET_SEARCH_PATH: - { - PyObject *py_level, *py_path, *tpath; - const char *path; - int err; + py_level = PyTuple_GetItem(args, 1); + if (!py_level) + return NULL; - py_level = PyTuple_GetItem(args, 1); - if (!py_level) - return NULL; + py_path = PyTuple_GetItem(args, 2); + if (!py_path) + return NULL; - py_path = PyTuple_GetItem(args, 2); - if (!py_path) - return NULL; + if (!PyLong_Check(py_level)) + goto on_non_integer; - if (!PyLong_Check(py_level)) - goto on_non_integer; + path = py_str_borrow_c_str(&tpath, py_path, NULL); + if (!path) + return NULL; - path = py_str_borrow_c_str(&tpath, py_path, NULL); - if (!path) - return NULL; + err = git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, PyLong_AsLong(py_level), path); + Py_DECREF(tpath); - err = git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, PyLong_AsLong(py_level), path); - Py_DECREF(tpath); + if (err < 0) { + Error_set(err); + return NULL; + } - if (err < 0) { - Error_set(err); - return NULL; + Py_RETURN_NONE; + break; } - Py_RETURN_NONE; - break; - } + case GIT_OPT_GET_MWINDOW_SIZE: + { + size_t size; - case GIT_OPT_GET_MWINDOW_SIZE: - { - size_t size; + error = git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &size); + if (error < 0) { + Error_set(error); + return NULL; + } - if ((error = git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &size)) < 0) { - Error_set(error); - return NULL; - } + return PyLong_FromSize_t(size); - return PyLong_FromSize_t(size); - - break; - } + break; + } - case GIT_OPT_SET_MWINDOW_SIZE: - { - size_t size; - PyObject *py_size; + case GIT_OPT_SET_MWINDOW_SIZE: + { + size_t size; + PyObject *py_size; - py_size = PyTuple_GetItem(args, 1); - if (!py_size) - return NULL; + py_size = PyTuple_GetItem(args, 1); + if (!py_size) + return NULL; - if (!PyLong_Check(py_size)) - goto on_non_integer; + if (!PyLong_Check(py_size)) + goto on_non_integer; + size = PyLong_AsSize_t(py_size); + error = git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, size); + if (error < 0) { + Error_set(error); + return NULL; + } - size = PyLong_AsSize_t(py_size); - if ((error = git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, size)) < 0) { - Error_set(error); - return NULL; + Py_RETURN_NONE; + break; } - - Py_RETURN_NONE; - break; } - } - PyErr_SetString(PyExc_ValueError, "unknown/unsupported option value"); + PyErr_SetString(PyExc_ValueError, "unknown/unsupported option value"); return NULL; on_non_integer: diff --git a/src/reference.c b/src/reference.c index b2b5bd233..d86d8a8e9 100644 --- a/src/reference.c +++ b/src/reference.c @@ -62,7 +62,7 @@ RefLogIter_iternext(RefLogIter *self) py_entry->oid_old = git_oid_allocfmt(git_reflog_entry_id_old(entry)); py_entry->oid_new = git_oid_allocfmt(git_reflog_entry_id_new(entry)); py_entry->message = strdup(git_reflog_entry_message(entry)); - git_signature_dup(&py_entry->signature, git_reflog_entry_committer(entry)); + git_signature_dup(&py_entry->signature, git_reflog_entry_committer(entry)); ++(self->i); From a9fcbb33d17ba8b2a80c43854dbcb97583bf1e17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 21 Jun 2014 21:14:33 +0200 Subject: [PATCH 0768/2237] Check errors returned by git_signature_dup --- src/blame.c | 41 +++++++++++++++++++++++++---------------- src/reference.c | 6 +++++- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/blame.c b/src/blame.c index 4fe1770de..f1c2f6292 100644 --- a/src/blame.c +++ b/src/blame.c @@ -60,32 +60,41 @@ PyObject* wrap_blame_hunk(const git_blame_hunk *hunk, Blame *blame) { BlameHunk *py_hunk = NULL; + int err; if (!hunk) Py_RETURN_NONE; py_hunk = PyObject_New(BlameHunk, &BlameHunkType); - if (py_hunk != NULL) { - py_hunk->lines_in_hunk = hunk->lines_in_hunk; - py_hunk->final_commit_id = git_oid_allocfmt(&hunk->final_commit_id); - py_hunk->final_start_line_number = hunk->final_start_line_number; - - py_hunk->final_signature = NULL; - if (hunk->final_signature) - git_signature_dup(&py_hunk->final_signature, hunk->final_signature); + if (py_hunk == NULL) + return NULL; - py_hunk->orig_commit_id = git_oid_allocfmt(&hunk->orig_commit_id); - py_hunk->orig_path = hunk->orig_path != NULL ? - strdup(hunk->orig_path) : NULL; - py_hunk->orig_start_line_number = hunk->orig_start_line_number; + py_hunk->lines_in_hunk = hunk->lines_in_hunk; + py_hunk->final_commit_id = git_oid_allocfmt(&hunk->final_commit_id); + py_hunk->final_start_line_number = hunk->final_start_line_number; - py_hunk->orig_signature = NULL; - if (hunk->orig_signature) - git_signature_dup(&py_hunk->orig_signature, hunk->orig_signature); + py_hunk->final_signature = NULL; + if (hunk->final_signature) { + err = git_signature_dup(&py_hunk->final_signature, + hunk->final_signature); + if (err < 0) + return Error_set(err); + } - py_hunk->boundary = hunk->boundary; + py_hunk->orig_commit_id = git_oid_allocfmt(&hunk->orig_commit_id); + py_hunk->orig_path = hunk->orig_path != NULL ? + strdup(hunk->orig_path) : NULL; + py_hunk->orig_start_line_number = hunk->orig_start_line_number; + + py_hunk->orig_signature = NULL; + if (hunk->orig_signature) { + err = git_signature_dup(&py_hunk->orig_signature, + hunk->orig_signature); + if (err < 0) + return Error_set(err); } + py_hunk->boundary = hunk->boundary; return (PyObject*) py_hunk; } diff --git a/src/reference.c b/src/reference.c index d86d8a8e9..cbd96d75d 100644 --- a/src/reference.c +++ b/src/reference.c @@ -54,6 +54,7 @@ RefLogIter_iternext(RefLogIter *self) { const git_reflog_entry *entry; RefLogEntry *py_entry; + int err; if (self->i < self->size) { entry = git_reflog_entry_byindex(self->reflog, self->i); @@ -62,7 +63,10 @@ RefLogIter_iternext(RefLogIter *self) py_entry->oid_old = git_oid_allocfmt(git_reflog_entry_id_old(entry)); py_entry->oid_new = git_oid_allocfmt(git_reflog_entry_id_new(entry)); py_entry->message = strdup(git_reflog_entry_message(entry)); - git_signature_dup(&py_entry->signature, git_reflog_entry_committer(entry)); + err = git_signature_dup(&py_entry->signature, + git_reflog_entry_committer(entry)); + if (err < 0) + return Error_set(err); ++(self->i); From a063867fe0e4506e29f22c45dd403d805e3fb1b7 Mon Sep 17 00:00:00 2001 From: Jack O'Connor Date: Fri, 30 May 2014 11:09:29 +0900 Subject: [PATCH 0769/2237] Index: add a setter for workdir --- src/repository.c | 23 ++++++++++++++++++++++- test/test_repository.py | 5 +++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/repository.c b/src/repository.c index 0b12053a4..ad0b3d92f 100644 --- a/src/repository.c +++ b/src/repository.c @@ -512,6 +512,27 @@ Repository_workdir__get__(Repository *self, void *closure) return to_path(c_path); } +int +Repository_workdir__set__(Repository *self, PyObject *py_workdir) +{ + int err; + const char *workdir; + PyObject *tworkdir; + + workdir = py_str_borrow_c_str(&tworkdir, py_workdir, NULL); + if (workdir == NULL) + return -1; + + err = git_repository_set_workdir(self->repo, workdir, 0 /* update_gitlink */); + Py_DECREF(tworkdir); + if (err < 0) { + Error_set_str(err, workdir); + return -1; + } + + return 0; +} + PyDoc_STRVAR(Repository_merge_base__doc__, "merge_base(oid, oid) -> Oid\n" "\n" @@ -1597,7 +1618,7 @@ PyGetSetDef Repository_getseters[] = { GETTER(Repository, head_is_unborn), GETTER(Repository, is_empty), GETTER(Repository, is_bare), - GETTER(Repository, workdir), + GETSET(Repository, workdir), GETTER(Repository, default_signature), GETTER(Repository, _pointer), {NULL} diff --git a/test/test_repository.py b/test/test_repository.py index 4581f4774..37f575c0a 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -190,6 +190,11 @@ def test_get_workdir(self): expected = realpath(self.repo_path) self.assertEqual(directory, expected) + def test_set_workdir(self): + directory = tempfile.mkdtemp() + self.repo.workdir = directory + self.assertEqual(realpath(self.repo.workdir), realpath(directory)) + def test_checkout_ref(self): ref_i18n = self.repo.lookup_reference('refs/heads/i18n') From b190169f5e83cbdb2346acd52cea30e14a205eb5 Mon Sep 17 00:00:00 2001 From: Jack O'Connor Date: Fri, 30 May 2014 23:50:41 +0900 Subject: [PATCH 0770/2237] support setting a detatched HEAD --- src/repository.c | 32 +++++++++++++++++++++----------- test/test_repository.py | 9 +++++++++ 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/repository.c b/src/repository.c index ad0b3d92f..bd048c73d 100644 --- a/src/repository.c +++ b/src/repository.c @@ -179,21 +179,31 @@ Repository_head__get__(Repository *self) } int -Repository_head__set__(Repository *self, PyObject *py_refname) +Repository_head__set__(Repository *self, PyObject *py_val) { int err; - const char *refname; - PyObject *trefname; + if (PyObject_TypeCheck(py_val, &OidType)) { + git_oid oid; + py_oid_to_git_oid(py_val, &oid); + err = git_repository_set_head_detached(self->repo, &oid, NULL, NULL); + if (err < 0) { + Error_set(err); + return -1; + } + } else { + const char *refname; + PyObject *trefname; - refname = py_str_borrow_c_str(&trefname, py_refname, NULL); - if (refname == NULL) - return -1; + refname = py_str_borrow_c_str(&trefname, py_val, NULL); + if (refname == NULL) + return -1; - err = git_repository_set_head(self->repo, refname, NULL, NULL); - Py_DECREF(trefname); - if (err < 0) { - Error_set_str(err, refname); - return -1; + err = git_repository_set_head(self->repo, refname, NULL, NULL); + Py_DECREF(trefname); + if (err < 0) { + Error_set_str(err, refname); + return -1; + } } return 0; diff --git a/test/test_repository.py b/test/test_repository.py index 37f575c0a..4d6472ec1 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -70,6 +70,15 @@ def test_head(self): self.assertFalse(self.repo.head_is_unborn) self.assertFalse(self.repo.head_is_detached) + def test_set_head(self): + # Test setting a detatched HEAD. + self.repo.head = Oid(hex=PARENT_SHA) + self.assertEqual(self.repo.head.target.hex, PARENT_SHA) + # And test setting a normal HEAD. + self.repo.head = "refs/heads/master" + self.assertEqual(self.repo.head.name, "refs/heads/master") + self.assertEqual(self.repo.head.target.hex, HEAD_SHA) + def test_read(self): self.assertRaises(TypeError, self.repo.read, 123) self.assertRaisesWithArg(KeyError, '1' * 40, self.repo.read, '1' * 40) From 9811123922764f86f128cca8c637283333ba31eb Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Thu, 26 Jun 2014 23:27:23 +0100 Subject: [PATCH 0771/2237] Fix docstrings for Blob.diff & diff_to_buffer They were both missing a closing parenthesis which stop Sphinx from interpreting and formatting them properly. --- src/blob.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blob.c b/src/blob.c index 11ec3cd15..6ec9fd5cc 100644 --- a/src/blob.c +++ b/src/blob.c @@ -38,7 +38,7 @@ extern PyObject *GitError; extern PyTypeObject BlobType; PyDoc_STRVAR(Blob_diff__doc__, - "diff([blob, flag, old_as_path, new_as_path] -> Patch\n" + "diff([blob, flag, old_as_path, new_as_path]) -> Patch\n" "\n" "Directly generate a :py:class:`pygit2.Patch` from the difference\n" " between two blobs.\n" @@ -79,7 +79,7 @@ Blob_diff(Blob *self, PyObject *args, PyObject *kwds) PyDoc_STRVAR(Blob_diff_to_buffer__doc__, - "diff_to_buffer([buffer, flag, old_as_path, buffer_as_path] -> Patch\n" + "diff_to_buffer([buffer, flag, old_as_path, buffer_as_path]) -> Patch\n" "\n" "Directly generate a :py:class:`~pygit2.Patch` from the difference\n" " between a blob and a buffer.\n" From b96b285cea0177bee1039f0957daf30d411ea833 Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Thu, 26 Jun 2014 23:29:24 +0100 Subject: [PATCH 0772/2237] Improve diff & diff_to_buffer doc formatting We switch to a parameter list for both functions and add a return type. We also remove the indentation from the second line of the explanation which was causing Sphinx to return it as two lines instead of one continuous one. We also added return types. I am not sure of the type of the GIT_DIFF* flags so I have not included that. --- src/blob.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/blob.c b/src/blob.c index 6ec9fd5cc..294c884cf 100644 --- a/src/blob.c +++ b/src/blob.c @@ -41,17 +41,17 @@ PyDoc_STRVAR(Blob_diff__doc__, "diff([blob, flag, old_as_path, new_as_path]) -> Patch\n" "\n" "Directly generate a :py:class:`pygit2.Patch` from the difference\n" - " between two blobs.\n" + "between two blobs.\n" "\n" - "Arguments:\n" + ":param Blob blob: the :py:class:`~pygit2.Blob` to diff.\n" "\n" - "blob: the :py:class:`~pygit2.Blob` to diff.\n" + ":param flag: a GIT_DIFF_* constant.\n" "\n" - "flag: a GIT_DIFF_* constant.\n" + ":param str old_as_path: treat old blob as if it had this filename.\n" "\n" - "old_as_path: treat old blob as if it had this filename.\n" + ":param str new_as_path: treat new blob as if it had this filename.\n" "\n" - "new_as_path: treat new blob as if it had this filename.\n"); + ":rtype: Patch\n"); PyObject * Blob_diff(Blob *self, PyObject *args, PyObject *kwds) @@ -82,17 +82,17 @@ PyDoc_STRVAR(Blob_diff_to_buffer__doc__, "diff_to_buffer([buffer, flag, old_as_path, buffer_as_path]) -> Patch\n" "\n" "Directly generate a :py:class:`~pygit2.Patch` from the difference\n" - " between a blob and a buffer.\n" + "between a blob and a buffer.\n" "\n" - "Arguments:\n" + ":param Blob buffer: Raw data for new side of diff.\n" "\n" - "buffer: Raw data for new side of diff.\n" + ":param flag: a GIT_DIFF_* constant.\n" "\n" - "flag: a GIT_DIFF_* constant.\n" + ":param str old_as_path: treat old blob as if it had this filename.\n" "\n" - "old_as_path: treat old blob as if it had this filename.\n" + ":param str buffer_as_path: treat buffer as if it had this filename.\n" "\n" - "buffer_as_path: treat buffer as if it had this filename.\n"); + ":rtype: Patch\n"); PyObject * Blob_diff_to_buffer(Blob *self, PyObject *args, PyObject *kwds) From 7b3201d868578e8e6936cb868c7acc38a4b04b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 27 Jun 2014 17:28:01 +0200 Subject: [PATCH 0773/2237] Get ready to release 0.21.0 --- README.rst | 112 ++++++++++++++++++++++++++++++++++++---------- docs/conf.py | 4 +- docs/general.rst | 8 ++-- docs/install.rst | 4 +- pygit2/version.py | 2 +- 5 files changed, 98 insertions(+), 32 deletions(-) diff --git a/README.rst b/README.rst index 48ffc0686..776451c77 100644 --- a/README.rst +++ b/README.rst @@ -48,34 +48,100 @@ for the topic), send a pull request. Authors ============== -62 developers have contributed at least 1 commit to pygit2:: - - J. David Ibáñez Rémi Duraffort András Veres-Szentkirályi - Nico von Geyso Sebastian Thiel Benjamin Kircher - Carlos Martín Nieto Fraser Tweedale Benjamin Pollack - W. Trevor King Han-Wen Nienhuys Bryan O'Sullivan - Dave Borowitz Leonardo Rhodes David Fischer - Daniel Rodríguez Troitiño Petr Viktorin David Sanders - Richo Healey Alex Chamberlain Devaev Maxim - Christian Boos Amit Bakshi Eric Davis - Julien Miotte Andrey Devyatkin Erik Meusel - Xu Tao Ben Davis Erik van Zijst - Jose Plana Eric Schrijver Ferengee - Martin Lenders Hervé Cauwelier Gustavo Di Pietro - Petr Hosek Huang Huang Hugh Cole-Baker - Victor Garcia Jared Flatow Josh Bleecher Snyder - Xavier Delannoy Jiunn Haur Lim Jun Omae - Yonggang Luo Sarath Lakshman Óscar San José - Valentin Haenel Vicent Marti Ridge Kennedy - Bernardo Heynemann Zoran Zaric Rui Abreu Ferreira - John Szakmeister Adam Spiers Thomas Kluyver - Brodie Rao Alexander Bayandin earl - David Versmisse Andrew Chin +66 developers have contributed at least 1 commit to pygit2:: + + J. David Ibáñez Rémi Duraffort Adam Spiers + Nico von Geyso Sebastian Thiel Alexander Bayandin + Carlos Martín Nieto Fraser Tweedale Andrew Chin + W. Trevor King Han-Wen Nienhuys András Veres-Szentkirályi + Dave Borowitz Leonardo Rhodes Benjamin Kircher + Daniel Rodríguez Troitiño Petr Viktorin Benjamin Pollack + Richo Healey Thomas Kluyver Bryan O'Sullivan + Christian Boos Alex Chamberlain Daniel Bruce + Julien Miotte Amit Bakshi David Fischer + Xu Tao Andrey Devyatkin David Sanders + Jose Plana Ben Davis Devaev Maxim + Martin Lenders Eric Schrijver Eric Davis + Petr Hosek Hervé Cauwelier Erik Meusel + Victor Garcia Huang Huang Erik van Zijst + Xavier Delannoy Ian P. McCullough Ferengee + Yonggang Luo Jack O'Connor Gustavo Di Pietro + Valentin Haenel Jared Flatow Hugh Cole-Baker + Michael Jones Jiunn Haur Lim Josh Bleecher Snyder + Bernardo Heynemann Jun Omae Óscar San José + John Szakmeister Sarath Lakshman Ridge Kennedy + Brodie Rao Vicent Marti Rui Abreu Ferreira + David Versmisse Zoran Zaric earl Changelog ============== +0.21.0 (2014-06-27) +------------------- + +Highlights: + +- Drop official support for Python 2.6, and add support for Python 3.4 + `#376 `_ + +- Upgrade to libgit2 v0.21.0 + `#374 `_ + +- Start using cffi + `#360 `_ + `#361 `_ + +Backward incompatible changes: + +- Replace ``oid`` by ``id`` through the API to follow libgit2 conventions. +- Merge API overhaul following changes in libgit2. +- New ``Remote.rename(...)`` replaces ``Remote.name = ...`` +- Now ``Remote.fetch()`` returns a ``TransferProgress`` object. +- Now ``Config.get_multivar(...)`` returns an iterator instead of a list. + +New features: + +- New ``Config.snapshot()`` and ``Repository.config_snapshot()`` + +- New ``Config`` methods: ``get_bool(...)``, ``get_int(...)``, + ``parse_bool(...)`` and ``parse_int(...)`` + `#357 `_ + +- Blob: implement the memory buffer interface + `#362 `_ + +- New ``clone_into(...)`` function + `#368 `_ + +- Now ``Index`` can be used alone, without a repository + `#372 `_ + +- Add more options to ``init_repository`` + `#347 `_ + +- Support ``Repository.workdir = ...`` and + support setting detached heads ``Repository.head = `` + `#377 `_ + +Other: + +- Fix again build with VS2008 + `#364 `_ + +- Fix ``Blob.diff(...)`` and ``Blob.diff_to_buffer(...)`` arguments passing + `#366 `_ + +- Fail gracefully when compiling against the wrong version of libgit2 + `#365 `_ + +- Several documentation improvements and updates + `#359 `_ + `#375 `_ + `#378 `_ + + + 0.20.3 (2014-04-02) ------------------- diff --git a/docs/conf.py b/docs/conf.py index c432057f7..c51e1f922 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,9 +48,9 @@ # built documents. # # The short X.Y version. -version = '0.20' +version = '0.21' # The full version, including alpha/beta/rc tags. -release = '0.20.3' +release = '0.21.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/general.rst b/docs/general.rst index b71e6e7bb..4ed9d3b21 100644 --- a/docs/general.rst +++ b/docs/general.rst @@ -18,7 +18,7 @@ library that has been built against. The version number has a .. py:data:: LIBGIT2_VER_MAJOR Integer value of the major version number. For example, for the version - ``0.20.0``:: + ``0.21.0``:: >>> print LIBGIT2_VER_MAJOR 0 @@ -26,10 +26,10 @@ library that has been built against. The version number has a .. py:data:: LIBGIT2_VER_MINOR Integer value of the minor version number. For example, for the version - ``0.20.0``:: + ``0.21.0``:: >>> print LIBGIT2_VER_MINOR - 20 + 21 .. py:data:: LIBGIT2_VER_REVISION @@ -44,7 +44,7 @@ library that has been built against. The version number has a The libgit2 version number as a string:: >>> print LIBGIT2_VERSION - '0.20.0' + '0.21.0' Errors ====== diff --git a/docs/install.rst b/docs/install.rst index 5f058f22d..fcc6877db 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -26,8 +26,8 @@ When those are installed, you can install pygit2: $ python setup.py test .. note:: A minor version of pygit2 must be used with the corresponding minor - version of libgit2. For example, pygit2 v0.20.x must be used with libgit2 - v0.20.0 + version of libgit2. For example, pygit2 v0.21.x must be used with libgit2 + v0.21.0 Building on \*nix (including OS X) =================================== diff --git a/pygit2/version.py b/pygit2/version.py index e68ec8dc5..cf9fe6be8 100644 --- a/pygit2/version.py +++ b/pygit2/version.py @@ -23,4 +23,4 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -__version__ = '0.20.3' +__version__ = '0.21.0' From 3cbc9b6c33f28927b6cfc0bcb54e191e943444f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 1 Jul 2014 16:36:05 +0200 Subject: [PATCH 0774/2237] README, tell to install cffi first --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 776451c77..284ac77ee 100644 --- a/README.rst +++ b/README.rst @@ -32,6 +32,7 @@ Quick install guide 3. Install pygit2 with *pip*:: + $ pip install cffi $ pip install pygit2 For detailed instructions check the documentation, From 1d509c110933e4fd978d8f748388eece1aa77cff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Jul 2014 08:57:47 +0200 Subject: [PATCH 0775/2237] travis: download a tag instead of master libgit2's development now happens on the master branch, which means we can't use it to refer to the latest release. Download v0.21.0 explicitly instead. --- .travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.sh b/.travis.sh index 26985cf4c..1e7c4353f 100755 --- a/.travis.sh +++ b/.travis.sh @@ -2,7 +2,7 @@ cd ~ -git clone --depth=1 -b master https://github.com/libgit2/libgit2.git +git clone --depth=1 -b v0.21.0 https://github.com/libgit2/libgit2.git cd libgit2/ mkdir build && cd build From b0bf223276ac3a0a522aca4d1c2e8c08636f9081 Mon Sep 17 00:00:00 2001 From: Jasper Lievisse Adriaanse Date: Thu, 3 Jul 2014 10:10:38 +0200 Subject: [PATCH 0776/2237] Tweak include/lib dir detection in ffi.py Joint work with @carlosmn --- pygit2/ffi.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pygit2/ffi.py b/pygit2/ffi.py index 0dba2f34b..98de11c2a 100644 --- a/pygit2/ffi.py +++ b/pygit2/ffi.py @@ -112,11 +112,11 @@ def strings_to_strarray(l): # if LIBGIT2 exists, set build and link against that version libgit2_path = getenv('LIBGIT2') -include_dirs = [] -library_dirs = [] -if libgit2_path: - include_dirs = [path.join(libgit2_path, 'include')] - library_dirs = [path.join(libgit2_path, 'lib')] +if not libgit2_path: + libgit2_path = '/usr/local' + +include_dirs = [path.join(libgit2_path, 'include')] +library_dirs = [path.join(libgit2_path, 'lib')] C = ffi.verify("#include ", libraries=["git2"], include_dirs=include_dirs, library_dirs=library_dirs) From 0bfead36b23b884f8f3f3bb53c18963b7f2b5896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 6 Jul 2014 16:11:27 +0200 Subject: [PATCH 0777/2237] branch: correct notion of remote-tracking branch and upstream The current documentation text seems to be very confused about what a remote-tracking branch is and what the relationship to an upstream is. Correct the text to talk about the upstream branch when dealing with the upstream configuration, instead of implying that it's related to the setup of remote-tracking branches. --- src/branch.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/branch.c b/src/branch.c index 39ddd675d..1793ceaf8 100644 --- a/src/branch.c +++ b/src/branch.c @@ -129,7 +129,7 @@ Branch_branch_name__get__(Branch *self) PyDoc_STRVAR(Branch_remote_name__doc__, - "The name of the remote that the remote tracking branch belongs to."); + "The name of the remote set to be the upstream of this branch."); PyObject * Branch_remote_name__get__(Branch *self) @@ -154,8 +154,8 @@ Branch_remote_name__get__(Branch *self) PyDoc_STRVAR(Branch_upstream__doc__, - "The branch supporting the remote tracking branch or None if this is not a " - "remote tracking branch. Set to None to unset."); + "The branch's upstream branch or None if this branch does not have an upstream set. " + "Set to None to unset the upstream configuration."); PyObject * Branch_upstream__get__(Branch *self) @@ -206,7 +206,7 @@ int Branch_upstream__set__(Branch *self, Reference *py_ref) PyDoc_STRVAR(Branch_upstream_name__doc__, - "The name of the reference supporting the remote tracking branch."); + "The name of the reference set to be the upstream of this one"); PyObject * Branch_upstream_name__get__(Branch *self) From 9a7348a9d0af13e2d4bcf2ab19e167e64f173cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 8 Jul 2014 13:53:34 +0200 Subject: [PATCH 0778/2237] Update docs for merging Remove references to MergeResult and put merge_analysis in the docs. --- docs/merge.rst | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/docs/merge.rst b/docs/merge.rst index 47984a868..413f03480 100644 --- a/docs/merge.rst +++ b/docs/merge.rst @@ -6,12 +6,13 @@ Merge .. automethod:: pygit2.Repository.merge_base .. automethod:: pygit2.Repository.merge +.. automethod:: pygit2.Repository.merge_analysis The merge method ================= The method does a merge over the current working copy. -It gets an Oid object as a parameter and returns a MergeResult object. +It gets an Oid object as a parameter. As its name says, it only does the merge, does not commit nor update the branch reference in the case of a fastforward. @@ -21,17 +22,14 @@ merge with the default ones defined in GIT_MERGE_OPTS_INIT libgit2 constant. Example:: - >>> branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533' - >>> branch_id = self.repo.get(branch_head_hex).id - >>> merge_result = self.repo.merge(branch_id) + >>> other_branch_tip = '5ebeeebb320790caf276b9fc8b24546d63316533' + >>> repo.merge(other_branch_tip) -The MergeResult object -====================== +You can now inspect the index file for conflicts and get back to the +user to resolve if there are. Once there are no conflicts left, you +can create a commit with these two parents. -Represents the result of a merge and contains these fields: - -- is_uptodate: bool, if there wasn't any merge because the repo was already - up to date -- is_fastforward: bool, whether the merge was fastforward or not -- fastforward_id: Oid, in the case it was a fastforward, this is the - forwarded id. + >>> user = repo.default_signature() + >>> tree = repo.index.write_tree() + >>> new_commit = repo.create_commit('HEAD', user, user, tree, + [repo.head.target, other_branch_tip]) From 02fd05baae541e38ef10cc2058095672ba616703 Mon Sep 17 00:00:00 2001 From: vtemian Date: Wed, 9 Jul 2014 16:39:34 +0300 Subject: [PATCH 0779/2237] Added clean_state_files --- pygit2/decl.h | 1 + pygit2/repository.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/pygit2/decl.h b/pygit2/decl.h index d3b5b022d..7068e94a6 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -114,6 +114,7 @@ int git_remote_create( git_repository *repo, const char *name, const char *url); +int git_repository_state_cleanup(git_repository *repo); const char * git_remote_name(const git_remote *remote); diff --git a/pygit2/repository.py b/pygit2/repository.py index db047f5ee..c4e46c85d 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -299,3 +299,6 @@ def treeish_to_tree(obj): raise NotImplementedError('git_diff_blob_to_blob()') raise ValueError("Only blobs and treeish can be diffed") + + def clean_state_files(self): + C.git_repository_state_cleanup(self._repo) From 19dd7629d3c1266b12b6fb75913ba3aed9953be5 Mon Sep 17 00:00:00 2001 From: vtemian Date: Wed, 9 Jul 2014 16:46:52 +0300 Subject: [PATCH 0780/2237] Proper naming --- pygit2/repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index c4e46c85d..61f5143da 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -300,5 +300,5 @@ def treeish_to_tree(obj): raise ValueError("Only blobs and treeish can be diffed") - def clean_state_files(self): + def state_cleanup(self): C.git_repository_state_cleanup(self._repo) From d8864bdf02a828478e849cdc1600ea48ec66fdc1 Mon Sep 17 00:00:00 2001 From: vtemian Date: Wed, 9 Jul 2014 16:50:17 +0300 Subject: [PATCH 0781/2237] Added docs --- pygit2/repository.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pygit2/repository.py b/pygit2/repository.py index 61f5143da..1dff8785c 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -301,4 +301,9 @@ def treeish_to_tree(obj): raise ValueError("Only blobs and treeish can be diffed") def state_cleanup(self): + """ + Remove all the metadata associated with an ongoing command like + merge, revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, + etc. + """ C.git_repository_state_cleanup(self._repo) From 6f438ad1732d6547c35da7df9a0627dc3534b125 Mon Sep 17 00:00:00 2001 From: vtemian Date: Wed, 9 Jul 2014 18:08:26 +0300 Subject: [PATCH 0782/2237] Added sphinx rtd theme --- docs/_themes/sphinx_rtd_theme/__init__.py | 17 + .../_themes/sphinx_rtd_theme/breadcrumbs.html | 19 + docs/_themes/sphinx_rtd_theme/footer.html | 32 ++ docs/_themes/sphinx_rtd_theme/layout.html | 160 +++++++ docs/_themes/sphinx_rtd_theme/layout_old.html | 205 +++++++++ docs/_themes/sphinx_rtd_theme/search.html | 50 +++ docs/_themes/sphinx_rtd_theme/searchbox.html | 7 + .../static/css/badge_only.css | 1 + .../sphinx_rtd_theme/static/css/theme.css | 4 + .../static/fonts/FontAwesome.otf | Bin 0 -> 62856 bytes .../static/fonts/fontawesome-webfont.eot | Bin 0 -> 38205 bytes .../static/fonts/fontawesome-webfont.svg | 414 ++++++++++++++++++ .../static/fonts/fontawesome-webfont.ttf | Bin 0 -> 80652 bytes .../static/fonts/fontawesome-webfont.woff | Bin 0 -> 44432 bytes .../sphinx_rtd_theme/static/js/theme.js | 47 ++ docs/_themes/sphinx_rtd_theme/theme.conf | 8 + docs/_themes/sphinx_rtd_theme/versions.html | 37 ++ docs/conf.py | 4 +- 18 files changed, 1003 insertions(+), 2 deletions(-) create mode 100644 docs/_themes/sphinx_rtd_theme/__init__.py create mode 100644 docs/_themes/sphinx_rtd_theme/breadcrumbs.html create mode 100644 docs/_themes/sphinx_rtd_theme/footer.html create mode 100644 docs/_themes/sphinx_rtd_theme/layout.html create mode 100644 docs/_themes/sphinx_rtd_theme/layout_old.html create mode 100644 docs/_themes/sphinx_rtd_theme/search.html create mode 100644 docs/_themes/sphinx_rtd_theme/searchbox.html create mode 100644 docs/_themes/sphinx_rtd_theme/static/css/badge_only.css create mode 100644 docs/_themes/sphinx_rtd_theme/static/css/theme.css create mode 100644 docs/_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf create mode 100644 docs/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot create mode 100644 docs/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg create mode 100644 docs/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf create mode 100644 docs/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff create mode 100644 docs/_themes/sphinx_rtd_theme/static/js/theme.js create mode 100644 docs/_themes/sphinx_rtd_theme/theme.conf create mode 100644 docs/_themes/sphinx_rtd_theme/versions.html diff --git a/docs/_themes/sphinx_rtd_theme/__init__.py b/docs/_themes/sphinx_rtd_theme/__init__.py new file mode 100644 index 000000000..1440863d6 --- /dev/null +++ b/docs/_themes/sphinx_rtd_theme/__init__.py @@ -0,0 +1,17 @@ +"""Sphinx ReadTheDocs theme. + +From https://github.com/ryan-roemer/sphinx-bootstrap-theme. + +""" +import os + +VERSION = (0, 1, 5) + +__version__ = ".".join(str(v) for v in VERSION) +__version_full__ = __version__ + + +def get_html_theme_path(): + """Return list of HTML theme paths.""" + cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + return cur_dir diff --git a/docs/_themes/sphinx_rtd_theme/breadcrumbs.html b/docs/_themes/sphinx_rtd_theme/breadcrumbs.html new file mode 100644 index 000000000..ff0938e5c --- /dev/null +++ b/docs/_themes/sphinx_rtd_theme/breadcrumbs.html @@ -0,0 +1,19 @@ +
+ +
+
diff --git a/docs/_themes/sphinx_rtd_theme/footer.html b/docs/_themes/sphinx_rtd_theme/footer.html new file mode 100644 index 000000000..3c7afed33 --- /dev/null +++ b/docs/_themes/sphinx_rtd_theme/footer.html @@ -0,0 +1,32 @@ +
+ {% if next or prev %} + + {% endif %} + +
+ +
+

+ {%- if show_copyright %} + {%- if hasdoc('copyright') %} + {% trans path=pathto('copyright'), copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} + {%- else %} + {% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} + {%- endif %} + {%- endif %} + + {%- if last_updated %} + {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %} + {%- endif %} +

+
+ + {% trans %}Sphinx theme provided by Read the Docs{% endtrans %} +
diff --git a/docs/_themes/sphinx_rtd_theme/layout.html b/docs/_themes/sphinx_rtd_theme/layout.html new file mode 100644 index 000000000..8585a6b31 --- /dev/null +++ b/docs/_themes/sphinx_rtd_theme/layout.html @@ -0,0 +1,160 @@ +{# TEMPLATE VAR SETTINGS #} +{%- set url_root = pathto('', 1) %} +{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} +{%- if not embedded and docstitle %} + {%- set titlesuffix = " — "|safe + docstitle|e %} +{%- else %} + {%- set titlesuffix = "" %} +{%- endif %} + + + + + + + + {% block htmltitle %} + {{ title|striptags|e }}{{ titlesuffix }} + {% endblock %} + + {# FAVICON #} + {% if favicon %} + + {% endif %} + + {# CSS #} + + + {# OPENSEARCH #} + {% if not embedded %} + {% if use_opensearch %} + + {% endif %} + + {% endif %} + + {# RTD hosts this file, so just load on non RTD builds #} + {% if not READTHEDOCS %} + + {% endif %} + + {% for cssfile in css_files %} + + {% endfor %} + + {%- block linktags %} + {%- if hasdoc('about') %} + + {%- endif %} + {%- if hasdoc('genindex') %} + + {%- endif %} + {%- if hasdoc('search') %} + + {%- endif %} + {%- if hasdoc('copyright') %} + + {%- endif %} + + {%- if parents %} + + {%- endif %} + {%- if next %} + + {%- endif %} + {%- if prev %} + + {%- endif %} + {%- endblock %} + {%- block extrahead %} {% endblock %} + + {# Keep modernizr in head - http://modernizr.com/docs/#installing #} + + + + + + +
+ + {# SIDE NAV, TOGGLES ON MOBILE #} + + +
+ + {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #} + + + + {# PAGE CONTENT #} +
+
+ {% include "breadcrumbs.html" %} +
+ {% block body %}{% endblock %} +
+ {% include "footer.html" %} +
+
+ +
+ +
+ {% include "versions.html" %} + + {% if not embedded %} + + + {%- for scriptfile in script_files %} + + {%- endfor %} + + {% endif %} + + {# RTD hosts this file, so just load on non RTD builds #} + {% if not READTHEDOCS %} + + {% endif %} + + {# STICKY NAVIGATION #} + {% if theme_sticky_navigation %} + + {% endif %} + + {%- block footer %} {% endblock %} + + + diff --git a/docs/_themes/sphinx_rtd_theme/layout_old.html b/docs/_themes/sphinx_rtd_theme/layout_old.html new file mode 100644 index 000000000..deb8df2a1 --- /dev/null +++ b/docs/_themes/sphinx_rtd_theme/layout_old.html @@ -0,0 +1,205 @@ +{# + basic/layout.html + ~~~~~~~~~~~~~~~~~ + + Master layout template for Sphinx themes. + + :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- block doctype -%} + +{%- endblock %} +{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} +{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} +{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and + (sidebars != []) %} +{%- set url_root = pathto('', 1) %} +{# XXX necessary? #} +{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} +{%- if not embedded and docstitle %} + {%- set titlesuffix = " — "|safe + docstitle|e %} +{%- else %} + {%- set titlesuffix = "" %} +{%- endif %} + +{%- macro relbar() %} + +{%- endmacro %} + +{%- macro sidebar() %} + {%- if render_sidebar %} +
+
+ {%- block sidebarlogo %} + {%- if logo %} + + {%- endif %} + {%- endblock %} + {%- if sidebars != None %} + {#- new style sidebar: explicitly include/exclude templates #} + {%- for sidebartemplate in sidebars %} + {%- include sidebartemplate %} + {%- endfor %} + {%- else %} + {#- old style sidebars: using blocks -- should be deprecated #} + {%- block sidebartoc %} + {%- include "localtoc.html" %} + {%- endblock %} + {%- block sidebarrel %} + {%- include "relations.html" %} + {%- endblock %} + {%- block sidebarsourcelink %} + {%- include "sourcelink.html" %} + {%- endblock %} + {%- if customsidebar %} + {%- include customsidebar %} + {%- endif %} + {%- block sidebarsearch %} + {%- include "searchbox.html" %} + {%- endblock %} + {%- endif %} +
+
+ {%- endif %} +{%- endmacro %} + +{%- macro script() %} + + {%- for scriptfile in script_files %} + + {%- endfor %} +{%- endmacro %} + +{%- macro css() %} + + + {%- for cssfile in css_files %} + + {%- endfor %} +{%- endmacro %} + + + + + {{ metatags }} + {%- block htmltitle %} + {{ title|striptags|e }}{{ titlesuffix }} + {%- endblock %} + {{ css() }} + {%- if not embedded %} + {{ script() }} + {%- if use_opensearch %} + + {%- endif %} + {%- if favicon %} + + {%- endif %} + {%- endif %} +{%- block linktags %} + {%- if hasdoc('about') %} + + {%- endif %} + {%- if hasdoc('genindex') %} + + {%- endif %} + {%- if hasdoc('search') %} + + {%- endif %} + {%- if hasdoc('copyright') %} + + {%- endif %} + + {%- if parents %} + + {%- endif %} + {%- if next %} + + {%- endif %} + {%- if prev %} + + {%- endif %} +{%- endblock %} +{%- block extrahead %} {% endblock %} + + +{%- block header %}{% endblock %} + +{%- block relbar1 %}{{ relbar() }}{% endblock %} + +{%- block content %} + {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %} + +
+ {%- block document %} +
+ {%- if render_sidebar %} +
+ {%- endif %} +
+ {% block body %} {% endblock %} +
+ {%- if render_sidebar %} +
+ {%- endif %} +
+ {%- endblock %} + + {%- block sidebar2 %}{{ sidebar() }}{% endblock %} +
+
+{%- endblock %} + +{%- block relbar2 %}{{ relbar() }}{% endblock %} + +{%- block footer %} + +

asdf asdf asdf asdf 22

+{%- endblock %} + + + diff --git a/docs/_themes/sphinx_rtd_theme/search.html b/docs/_themes/sphinx_rtd_theme/search.html new file mode 100644 index 000000000..e3aa9b5c6 --- /dev/null +++ b/docs/_themes/sphinx_rtd_theme/search.html @@ -0,0 +1,50 @@ +{# + basic/search.html + ~~~~~~~~~~~~~~~~~ + + Template for the search page. + + :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- extends "layout.html" %} +{% set title = _('Search') %} +{% set script_files = script_files + ['_static/searchtools.js'] %} +{% block footer %} + + {# this is used when loading the search index using $.ajax fails, + such as on Chrome for documents on localhost #} + + {{ super() }} +{% endblock %} +{% block body %} + + + {% if search_performed %} +

{{ _('Search Results') }}

+ {% if not search_results %} +

{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}

+ {% endif %} + {% endif %} +
+ {% if search_results %} +
    + {% for href, caption, context in search_results %} +
  • + {{ caption }} +

    {{ context|e }}

    +
  • + {% endfor %} +
+ {% endif %} +
+{% endblock %} diff --git a/docs/_themes/sphinx_rtd_theme/searchbox.html b/docs/_themes/sphinx_rtd_theme/searchbox.html new file mode 100644 index 000000000..24418d32b --- /dev/null +++ b/docs/_themes/sphinx_rtd_theme/searchbox.html @@ -0,0 +1,7 @@ +
+
+ + + +
+
diff --git a/docs/_themes/sphinx_rtd_theme/static/css/badge_only.css b/docs/_themes/sphinx_rtd_theme/static/css/badge_only.css new file mode 100644 index 000000000..4868a0027 --- /dev/null +++ b/docs/_themes/sphinx_rtd_theme/static/css/badge_only.css @@ -0,0 +1 @@ +.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:"\f02d"}.icon-book:before{content:"\f02d"}.fa-caret-down:before{content:"\f0d7"}.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}} diff --git a/docs/_themes/sphinx_rtd_theme/static/css/theme.css b/docs/_themes/sphinx_rtd_theme/static/css/theme.css new file mode 100644 index 000000000..eb3f865fe --- /dev/null +++ b/docs/_themes/sphinx_rtd_theme/static/css/theme.css @@ -0,0 +1,4 @@ +*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.1.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.1.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff?v=4.1.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.1.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.pull-left.icon{margin-right:.3em}.fa.pull-right,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before,.icon-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before,.icon-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:"\f057"}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before,.icon-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before,.icon-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:"\f0a8"}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before,.icon-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before,.icon-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-square:before,.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .icon,.nav .fa,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .icon{display:inline}.btn .fa.fa-large,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.admonition-todo{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso,.rst-content .admonition-todo{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .admonition-todo .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .admonition-todo .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso,.rst-content .wy-alert-info.admonition-todo{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title,.rst-content .wy-alert-info.admonition-todo .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.admonition-todo{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.admonition-todo .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.admonition-todo{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.admonition-todo a{color:#2980b9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child,.rst-content .admonition-todo p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width: 768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27ae60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27ae60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#e74c3c !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#e67e22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980b9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#999;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{display:block;float:left;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{display:block;float:left;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{display:block;float:left;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:6px 0 0 0;font-size:90%}.wy-control-no-input{display:inline-block;margin:6px 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:0.3125em;font-style:italic}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#f3f6f6;color:#cad2d3}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e74c3c}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fff;color:#cad2d3;border-color:transparent}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{padding:6px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #e74c3c}.wy-control-group.wy-control-group-error textarea{border:solid 1px #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px}.wy-table td p:last-child,.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child{margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980b9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27ae60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#e74c3c !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}code,.rst-content tt{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;color:#e74c3c;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li p:last-child,.rst-content .section ul li p:last-child,.rst-content .toctree-wrapper ul li p:last-child,article ul li p:last-child{margin-bottom:0}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-disc li ol li,.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,article ul li ol li{list-style:decimal}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.wy-plain-list-decimal li p:last-child,.rst-content .section ol li p:last-child,.rst-content ol.arabic li p:last-child,article ol li p:last-child{margin-bottom:0}.wy-plain-list-decimal li ul,.rst-content .section ol li ul,.rst-content ol.arabic li ul,article ol li ul{margin-bottom:0}.wy-plain-list-decimal li ul li,.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,article ol li ul li{list-style:disc}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9b59b6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#eaf2f5}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#2980b9;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:0.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-side-nav-search{z-index:200;background-color:#2980b9;text-align:center;padding:0.809em;display:block;color:#fcfcfc;margin-bottom:0.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto 0.809em auto;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:0.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:absolute;top:0;left:0;width:300px;overflow:hidden;min-height:100%;background:#343131;z-index:200}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:0.4045em 0.809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,footer,.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}nav.stickynav{position:fixed;top:0}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last,.rst-content .admonition-todo .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after{visibility:visible;content:"\f0c1";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt{color:#000}.rst-content tt big,.rst-content tt em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt{font-weight:bold}.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:bold}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}.math{text-align:center} diff --git a/docs/_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf b/docs/_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..8b0f54e47e1d356dcf1496942a50e228e0f1ee14 GIT binary patch literal 62856 zcmcfp2Y3_5)&LBzEbU6(wGF`%u_do$I-wUs=poc3^xzP>t859|l91%ydy%{4ZewH9 zLNU#OK%5)jlp7M#adH#VlN(Y~MSVYG)7F`Dsts8mQIv>+ztD)dFw+9OVG%`1 zdML`ns?&x=Qnp|IfM+dm&(}ePcdqmf37+Ghm#p%f+FVKQ2*chjkzF#ZB~9w-bef!xGBr6D7h{6UGOP@t%*!8rhr zqTX&D_txFJckW8F88SgJDOYWQiq1}9HpST zU`<34PZ)C!_3}_&M2)6kC53tq%16Wv<;B!kk^fL$a$g&o8ZTNrRL|U3FQqy}Aw%^t z%FjbIl=r0M9>Z`rYKq77t>{++@-k0@oM~*1+}p2(7`Q4V*n=HYq=vsI?g5v}-nP z3|{}}ibb1(*R0;YdDD}@+q7nj-e?F6nlWp}oWMD=X3yOms||yGW^I(#9B4HL0`>*2 zG{Pq6qjlCmi#Eba+D94TAv}p9V_D5%k=nR0b4*~E)oRv<#|upiMk~z0GGmR=Yz-V5 ze^pq5HgIj2Au?HKwVD>qoJsnJx#u=RZ=|+Tk5lVmJ2z1#N=q3aw}vu8YK7c-N>4=y zwHEjdq-Iky;2wVdD3u7c7HAy@>636rQ}I+R6-Jq%%_eFi6$}s_rB+ajpcD*stEugP zo136*FtrWZo1wQ}7%h+r0@$R$MYWppE&yKBVk^ODoieQIXI-PMCWPv3^jr9p7*cDDu9q6%xx{?3;;b@n3omixrmwx*YNmZf9p3xm@i;8 zp?TpJjUB@J0D^@;Vq@WEgcj}}s2gf=U*-SLs=qz||El20$!O-RlsfnS_J9)6lK^rf z@F|+|fem;DctSVzuQ6lCs>g=*`}C{(m-TP#-`gM6ukSbXXY`l%AL#GuKiB_u|L6U` z^xwJVb4z_|(yht2X53nKYvZlGw+y#3Zk69U@CS95u-8E9*x%q${UiIw^e^w<+#lK> z-M_Ej)SuN~+27uOroXrU-Tp88`)^UVM&1epcn{s0b!+*p&9_2tnQmp>swD94ennAt zcir7`_tDR9d~W}I%Sf-0+(^%nvXRn}u#+RjBRxinMp7g0j<_@8_K4p{{5Im&i2f13 zj`+pr(-A+9_-Vw=5kHRjVZ`?%z8i6aJ1^|@`u}w?=l`!y{JYkcahKF7zYy(4XAHaLAh7>kswf;WDJ8 zodnW*&mk}LA4ATyzs;HS z&jMIk)X1SUY8WQ8mk8qz!5gX{ac?|#KNXah-`{R{t;jx;+arrw4mTM?C=b`)g9B|K zKbe$=Z!xqbc>xxr!#G3cIJ_43-sk>0XiMsaXE3e+56S@N-W&nebhy1GS=0t{!`!CB zeXl$`20SDCO)=z#yl@A)%foXM<_FJ&aY(!S?qN9ajLc&>wDpF%>BD`=97%ujZX|^{ zkUJb;(Bvllh3Ak$Tkm1o9O@S+z@h#=rtsbrEayd0}DguL&kx00m+ja=Bpt$)C)Jj(+GE#@N5{qN_YooPx`~Xe7HP3 z{%{$_+eqqQIN>I3Ngv^P)=&zdhx-v8M)G7X!|w&{r;s|*7v>g7Gy(!cXqP3lRov@8 zR1fWh=MwT9Zqok0{>Y@@?`{gwSN{7?L`gvE7m2*?lX6LUm1893w2Pdz9?n{^!(W2e zdWpaFl9b@u0BLprBcj#q)KgjW@7iqlGG5Yvz*k2E1b+8G7f(?i1&vA9XxDLyUk5nmBs6~80?xA;He-^DJ8RN^C1NybWMO6ExxOV&s>OP-SKlxQUu zNxCEtRJdwMgQQb(MDmQ}tmIiqujCEMHOY0!HkBMipnS7>{u``WKCv$?i#JtM9$^4u7g87d5nYqQ>kup*r>4Q>U zI$1hRI!8KRx>mYFs*@&5bEW0dI%&J~sPvTdy!1usRp|%PFQwl}f0q6xb;-PBD%k|t zY}tI-V%aj;YS{+aQ?dwIjLaxYk`>BoWsR~9*)iEk*+tn)va7OpWS_{smHjSrdP+V0 zJk_4#J?D9@_1xwe?HTK7@=Wl|@+|Uf_B`o%#`BWri=J_T=4`v|*&UBhl-L)Zv5p0%+J>@(~s_AL7X`wDx7eUJT&{SSMK z9pETV%t<)~r{X4Z^SBk<7A}m7;^H_fm&|2x`CJ88%QbUt++pq*cal5LUErSMUf^El zUgJLCKIVSme)FQdBwi!E`Us0Q z%p9T98WOazMw1pS4`!>y8fGSUh&Ik-O^&x{%~AT;IIAusHq0EYwdzPtZ?PI<%-T3( zf;Poyj0@2lgv1zcHAY2Q^wEZ}*a%}ZXpR=04ir-WpbZI&wOaLYTC*`MGSZl6h=r8Y z4d>%cq(*NDHzt{4!;(WH^yY|Ityyc*hFL*fHES(8GA!v5YmA7AiVce8e_;!6kC&7Z?Hyy8O0n%G}drq zY^2^A7ORi2YLl!XIxW$Sg>0fe(yD_8(T0#%Z4_w&Inczd&{N0@YP37MFWzF+MkX06M(8q>71~9GMQF*2ge2%AwMG*R7f)W-5CO{_W(pxQ1Gtd{5P-01VNw=dm{|+^ z6%j+0-eT37Lc+r$ViLp5kx^l=IKzeEl&qvF4E7NA%LH2ey@o@10m4vTyAQN~fSq7A zx?gWNFHF`H8*d3AI~%7r4CUPWFH{<1gk*m_30u(tfF`iWB#nqQTC}hv2E8F#m?SuDFTQn3UEkkc8@TWC!-F{GC^ww z>q*$~q;*EKK82V{VgW}(B4CfL)4q56 z4)D)xH0hF~^)O1fFcUYy3iJruY7hufKutIFVd8R^gr`Ecp*I_TDL24)U$r5ORbRg-pCjNXR?8@hRjlg!)^B z(D!dOu%iM74)q`)qGOHW+C($Zqs|&;iLn3^gGC89>$Oo4U_&EF=f-R>g=zQ41JxU% z^ai~(IaX`22o=$0BPn|0z*CK8 zK%DqkW2^;?Z85-a0Z6ni9$1JOKmq#-j|FR7G;j-Zd_)ZF6-)}K?p{V%Lg*B4TBUeba0p4h(`{lkhnUa;!S@mlEwb3uRAAna%X|R34lqnNUbFX_%$pF{0bXxjWdRmGt^CFZcG*MWq&*% zpD-JDPJjsSWiSA$4WFQ~!(L z(g@%$q;&`!M=`(;0H;FcJiPEeUTy)bGXu%#O;$^MxH}UvXTe-kd`b#g8@(3xP*30x znc%M+5eqCjy*4&-n6xnX2oC%!5s^Uj?t@SuO@S=#uW(bx z{WX6b2|^FDjXG;w?7RqzWiB8Wa4|QJBTGftngtFZz*C@qy(Q$Y1K?iO@DUL*ch+1% z9wK1j&>$1McLEb&Zk8+5#cF{jf&aTxfx3yPAYib-S%s<1oju2WfRYkWB~Tuak9)I+ z(-1(skh!xT*2bHo!{JN-dNJ<8yjM5m zG60rH7zk-~uZGNixK`kLe=CruA#>*j!96b-j;Z)?t?(j4`6Spia^GJE{4Ojx680Zt zNWe8%t069;H$XAk92OS^LR}2VREDV856=$Q!%mO|6<}C_6UCa{zd}W<5upDiblg`Y z4Cvl7f*bc0-6U;-JxByu&zNWdaxxqBk$}(fNs-__0UlzBNj3priZ@%}*dQl4?7A@u zxFO-}z(C>X2fTOs4u7+;J0*%HiJsMQxqoBiu59bC{I)* zIwpEv)GK;ZbY1kl=qJ%1q5%)ugY$R_l;6D`VIDej?~k_t(Uq#ab(*CcOB-jjSFxlRYtLG(g8nl{qO zbOHT5{ZCLqIVOM^&rD@zGV_^TOav3dn3%)Nr_5K(_smbsZ;XR+Nxh{3(y`L%(je&q z=^E)esaBdKO_%0LE2WLn1JX|EJJNqkKa+kfy&=6R{Z;m$EI>A1Hd!`RHd8iFwn+Af zOe@pN;$&u7o$Qe8lVqKiD_fkJ-=Jui1W386V`Pb1S)E zZZ{Xs={O@7&!utMTpf3Udy%`wead~q-Q@bYKfGjKDz6z{L0&7o9`}0EYlm03m(I)J zmEe`?mG4#O)#laVb=0fN>w?#dUN3vS=Jl4>2VS3feeLyw*Uw(Rc{#l9deh#V_egJz z_ayH*-iy4Kd2jIE?ESR2*4ylzxhxHlZ~0u+4bSNe2Avwqk&^$DHRv=KS#CD3;S~8SQm|;x zN%uXOg<%H!6sOWpT07MECb~&~iaal%Kr~kA@W=0ly z{t+$Uxdi~XHN7!e%}J9R(_7UXGlAu{@LgPTdU`T9mC4D=%h61g=2Yj|)i)V?b+ui? zE#uW(1@DS-MfI`{o?I@T&abi;)~M_?7x@=n*uipt?Z;r>c-GlBp66Pcnp(J_b~W~k zJU4;W8IE;z9Xr-_5FpZ3`8gH2s@$By{Co|!66RIRN3*C1^>ST?V>+@U!LTF2up`?- zL$|?lw4^nqr~{nKnUu7&6b%lRrZlCsr~{Z@h76@~^htykcl!R`V4$yrCB3Hbq$wn746_@NOa-3Klzp2l^gn2VQjbAuo0?#JQLL z$Mz}bSE*b<%<3&$R%={A(pBfD{9}jO88R43TRRf@j!umu(~;H5a&uR%M853YmDj$} zIQyjET)Xy-no~>!4446Ue9XYDW$(ym^9NXsBiI!j&bBmH*VjYd5uCtsQXS7>`8HO> zDbN}`0?ouLy46Rz8=vn%p8Uqm@ezB}D0m6pght^=)w6thX?kgz2G3qG5zoOZl-P#$ z;62Eu9_V9|U>i5{jy^LBsJUYYou6NrldH_F$f?R#6Z}L^@PMpQjwrgSs={8Q zoOChE&E(fDVqJZ+_^S(9K%?|z4Qv@&$Gd6owP0l%>_y%&IxVx)7#jOLcGPC4#d!g42=Yrv!#JYwQRKph}ax;`_tIz`20);H(1 zsJH++i<8d1wvyoE7px2R-tQK>V~5{WU|KHT4=~~?>;J-zTfD!37u?D8Q>s%Z8#$yy z%h5wD_x>xdywB+ughWP$WMyPzRwT*3=TpiXGn-0FZKbMbDvnhisqR1g!-dcPCCh&K zU-?&5z+T@$$>=nPF5$IkC4LdF#0#)`=@RwFOYj1u#w%4&w-#zI;XGu*dusADPKoOm z8YZ0Itm0}4+W;2`1!=edNfwuq23(9Y^AiBwidZ$*g5O$1LZ$6+E(!Uc|#A>nDKry|{>zcC#+K%kF13+aeB` z9VD9p6UpVd$^V7B9CH{zE9`mIIchS3J(9JvNG|5m;2dy7E#^4~49g)Y8pA2@Lg!dK zg2BOf!)Nnef3=~Zrna)izq+0-OJ%Z4GBT8|Rd_LG9C|4SxZ~=3jfW$p9$pYw$y_dg z$>JhlV>uJMiW^X%#R@E9a470Q>roqx9zaWQErSDbk~yp(uQ0DT&%cNvuP5iE^LQ+u z26PNWna=x2;dpDwYtF2PX<;eXb5R_ zZZpZ*jjdH0&h{xRQ82^3_v)+fai0dznTkb#fpNA>TZj!$wMBp(y(a5G+OcF=O-IX7 zI1yn7^P5|gEmh6+^=fi-zRxzcYPfTi=c-TFqDL>HS)ZW?kxW)_xu>W{<;ZnRKUuRK|0& z{yIfL1XJ`OLv>qeQ+d6Ac^h59pu}O!d{)1 zv*gVuu9H;FWrMuddxQ0v#UA3Pz#$I+SM%g3Mhc$GgAw6?7&+-zJQ9zbG>QEFIth(L zBY*uBja2)zlewX3ESktVZS|5(mkM&oHz$Xv$b>E&ZkH^c3ZkKeyP{@`J>81Zl|K725KKL~og7cTUw&+r2C zUk9>oB)d(Z#5JNP*mUmDq4TywX6_8%+DKj@yYsN}P;F;x zs~Sy06X}*#uDQ7i4t1y4@e^&gBNN(#@|4_eym;lN^{dj7Q_?EUGMmj-qU3N8NR(vr zL5@U0AW!DyaDfW~n7L>qoU7ycb%~=uC}_($bO;~RAg|+gl_}Tm%SPM9pFM`C+p(U`f$Ogj39`p#D49F9Oe2B)Y(1=eW zw)bneg>cL|gV(T-@p*5{tE=Jcu_#{Qxp*GXIvt3kkYHpQ3rMZzl>31_u>s6-4t1k$ z+%4rq9}T342VUdi$!t^dQ!_JRmu7%?geCz#$k7y78#|!3og3_v;<;Rny}YW5!%{qk zYr=}g#4>emYj$g9vy8LVs?h8`L_|TiBLNz~6T}mIn`7Q#x%%eXmYM^ywlbt>Y*KQW ztPgGNM5|#@Lho##(bo(L9oRr~qe#cANDc%f=kjIw`MHHTDlBJG(mA{ekB4g&=UR+@ z#y>k2b08anAWukZCeRZa(ch0ofCOX(Es0wN+K`%qt+#QuZ7_-y0m}#2?n`dsD*wD% zU9TxGD=jNm!ZzETgs?z(%&2dH6S29assTs?*$2o*DW}7G$(=zkCn=n0K=g91j%PTP zO^O&KdH%vD8V)3XPz7L>;2B8w07~qv;%G|;IoyGV`0yOvTG|Z!pBsQ#a448*<@V{7 zdf2gEhBIedl9SbV5}wF0Z(rH8R)gfF3J%|GPxzE<#INuQA;=Fuj>54gr^1)E;a_nA zo)4mW8(@oc8NVA2@UCNk;D%})%w{#z2H@ok=K_g?v+@cKVge`%egi3pAfR$7s)V8% zDeAC@I!=iS?|Kv_iSmi9WFEB;;){P5Rf%dKM4(>OC~6j+5}g+P=`qz~g~xw9Zi~l? z6U67mcO<+dT5?YEC%uhsrC(z|gAE zO*vJ0Soy8esY(oZgqQLER6n4etX{4*s1K;GsNYi~jhAMuW{;*_b1QI4;QGKH$2>CT zA7i<(=f?Sr+dQskyn1}e_?r{PPpF*GHsRt#zlr~zR50n=$@LGNnX+igA5%|F+cqs@ z+S}6~n7(}aZ!^p@%4hsObLz||W*(ijYF6oN$QX$5KDr7zAHmywn^DlpJ_O|_m=Lh-A{Et-MyoGSNERokiok) zBnhB3NFqWKByj{Ii5OXtL=iv-I)VcRzH|jku>?yL&Y*4VU{JsS#rOmaeBcup%p(vg z?BW3W4M&OsA3!q@+*i8Vuj{V(uR|WXD@)op>iqEmJe@|bq0uaUO$x21Z|quaWJ_xUXAmZ_~hhx4bGFsw0wse^@d)0B zL-DjAP%gua%Yc&7*ptG~HMb>n%yYV^Ir+quNu8Y~X zOsAO}fxX6IZ{=QTe4}1~-O+ORpvERWcIMrGol^hUixhq6Nu^Kwy$j!Uz@hXT4-9Ss z-^eat$rCh}7lHN*%g%HL&}$Su8|+c)fPpL~YD3OWLx-U)QRDO)^r8pth-2Z11unc6 zgng%-ae6tu=(e_wW5-~S1W_f(E39}MY+<0HH}t}`?3|LK9Q9xyw$l+A#;7pmon0@m z&K*)1ESq+ndV%!`g!5xSUcduLyEub)22bZfY4K@?Qx%R1r~Nu#$Db%*0|u7If<;f- zZs~|Wl!(S*4>TT2kOs?S>p%Q{+3%`Sh&B5C`;XrEP=ho`23o%ajYA%X+By!lcghCs z(t*>G`3tf5iS25v9E+7>u>TlY=(eddSF1{x5@z+(?=Ec9VE;d`68_zm&3^yMUl5~Q z0Git}{%n4T8P1e5L>?Gep2ptkLk#cJzMcm|(|{by6<_nIywA5V(E)G8Gcom+3bm`G z563%p(Fbx;4q8>~c*j#Xi_WWWENE06tM5GgA^R;KAldIYrnu%>=<-IpTt0YLpJO5Z z7ka_5=ykNkF$!&QjdCo4<9+{Y{}-4YM?Pfn-Sr?2iLE?(P=OM*pd0w2DX66fl@N?-1iD^%I(}!F>Y{#DE3uA#DGd2hEe5<#MzbG*8eJ9rAVS*a7>X z{S`8p!61R*K0CV=3?EN|rl+Y>-AblM$u#nWsCFL|0B zfQG|)pZ4~I6JVA_-Cz?4mQ3W`hJitlTLhF*gLObK6@qDS+lA0x(4E2J0agpr&cu^; zCO{MD_+OBcSu~yntMX9y*I=$xBgAa|S3PuJ@wbLP?TrDFLn7oI!1w?W6b|fFfXJWR zs>T5*;3zvdesBW5jGjNr;s6}*4v+5OI|y>`@(7+gbxs`u84}+uPY@vw00iu76xufo z;xcky3)%Z&;>+Yhm+!$8%J?!scS9CB;mhtZ2z){+m9XdqJo!a-xeFw$i9EJ~O~`HB z##U^V3ifpbIY!5;!OjkR*D9R>68VYgd@_*MUtkE$$-fkUxcc07c}E{~7;XvDpX)Cb|1|XFuvZq>JsB#)PveQe{;jxBiN^8{5K0jUrRqVzDg~18#Ciz@>FQUv zymy! z&*Od810Fl&u{>a&NYRqnoKmjF>yBohOh1`&!vECeGZ#-?l2ulhSKE~}#We+0>ac&U zetlbytST=DEOI$HMPT2?V*?FMarLpa{zkN(ZYfS}NLFDp%px@Hdbg?*+HWKXULd8 zkEK16c|6zUdZ=x9l%!V#N--vs)1Y?7`7@ zUn0ko6}wEv0^s#bf$8Y;nt{g#G6c;O9Rxkp~37xp$cQT7Cj!TNVhT`^& zI&4Hw_&KKS_Q{rzgsVT3nbUxjS!=s=ByFFeTQM)>Kqhz5aopk1G=ntHm(bZMG8dQ$BhNn1}_Fh1}7Nti)0c zsT@ogRyZ#PtP12$h;{@IwrJG15JZTZim@zu2-s#H3a(^DF9b*f!~-`SXB4TWX_;v% zT*RcM)i;-FDx{sz1Pp>3(E_#;_tAw?r_B|uIG=Ss?X=o8Z{QexDBE<7`o%{7?Ua9oUL)qyK{_Ai_VIOP#S7N&Z?ckpe>SiZNU9u zm_q=i4bJZ5(sVGj!PB!f7mo=XL{82L5inMgk&7V{T*SK~8Nwgw=%`(Z+g00lwVjUA zU=<3WUD{k?Dq6tekKu^y$hJ1`S7AGt=)v}92iHh2woB0rmiQX{&w_)RM|6e?WpRxG1qwgX1Z!msyPF7Ub7d7P6Vlc}3fyKQX z{8za}`FR?A4PT@4^9plwl!99goGkcu9*=ILU}-~rO?{;X|K@0ah;2_8fQ@>SAE*Hu zm0Ehb1*Q3A1^#G9oZ@s=Z~7@U&T;h6C(|Pi z>r_B2x`_Sz(lt28)kCN2v$jPmT?xPQJ9rqtDh3Y{nDII?+Y{^5u5Q$qRByH=X89*( zW+qsbz#re{>&mNY!JH4q<+i%|_71QcjvmY20Be`s_Y9ba=Ca)^9*q@#$RFGQTd(6C zD%WBR767mVjOD@V9ovsqp^2K>2HSzmI?N+AtVd2c@Vk*_I(IXT8ZbX?y>VB zUjx`hNA3vvLF4-_R%7+suyd>U8$5c5_dOFpf9J3&TGE@)C^juSC%r(E5|OF3M9T2A z8F=ALyha5M-v?g!X1a!$w-VTSu>AxDq`vRwfu|HHXh4~0-SQeQgF!}1ZYz~VPn9c zflBaRv=`n3Qn*Usc#Ek45eF0^LSR7lb6Mh?HnDpSg`cyk1F(JR%Ob?7Vgyf{qpy_(zgvuS>Vj=cLo{pa z>7>`QufDBBFQFGv3;F@B7jX-I>9Oo}NgLE_GwF{*7W7V4osfp`C!~n`D{ zw)N2Ge`)&ziIhHfGEX#uH_&MpKf(LB?vesIuAl_mzgzL^#-FF3QCH;Vl;)~*24l45 z5hQEJ5XpdL?T;vL1Qt`RP}9%>a6BA^|X!|NjdB_-jxI_CZ_l=Idxa zYiv&H$kZH3Ka|;-Ec<2Ut6=@}QDUDhSUP#7+LCO}G^NX|nW;%eh5%56KxP0ZU4iv*KA7w1xTwa7;q_g#*D8$PI$hF$~8E;@fbZi2er?M%mste&UVe zXw>l^U;pv=3AlcEd7Zho235`~JX|gRb zKMD8VG5SSkg(gI)?#yI@*VMn7sL4H8YOkr6)!UoP8&pmwgM1I4LNhLF(2)Uk4S`SY@Fxs`Oc(;0h69>rvKnWwBS-<;xgEr(x6DibxmxA2GpmIW%yoQloTB&TirQB-&)3iy;JKCM^{C2fZQ!-8vmGcos@_>` zs?06jUahZ9ZjxoybQv>rMOIl>wlW*yIdawc z1=gI%9Q>fsugF}o-=uuC4DGI?OOHNR`nu}nH;VJ$(-gdSwdhq6NdZ#d`u?6~~Z{9B`t z1-wD7iVv{1TrJ$)^S%f-D(W5jPFReasvb;xyJU+{ge@XLF!sW1Y>t#pxHf&n1 zT#>nH|1Pz8XL!_BlgzYrRr(xN=QBka^;w~<(os*A)DqVV3{f`x~wu*<2rlCTY(;`{I>jL zIg(cYQuReK+EM8DP0?Fb7i+$1ey6Rcv#0a&>5I>wJl%P&@mbk{muvs|59Qaf*EhbW z_U+#I{v1%Pj(mLjABWnTWxgjboH*Xqepc3gw(i1Z<%PWN^t0;pv+-Sq_cH?QCUG% zdPQ{U<|=F`!^+a9%Ut<>^NXIy4^bDT=A~pM$7FvlUt%w-s(;S!0?Is#=3GHno8CWo>lpI)FKe$jT79zST+OkX zwj*_?YR}i6x1XsyQCHPo(E_mQ%IeFS(o1y3!G*H?$*YP&RM{3=S)>NP*O)ZkUffX9 zT;l&u;qy61(`3n|nI*aE+#T^)mAc-5XO|S1md4@P{+a8x;&v0(YMUovWmkUrJ&Pu zXoQi+mlzyVO8Y8*2502splvA@57<9pE;b(RGHHC@z@yN7Q&))11UB+fcs{K&H5xCf zKDlFG%!H&Hbw@N1lr{f|?xO7oSi+$#0O~rDel$eo146*S?V*`hq6(0H%NP%`pACJIXr6*_&%wUIKAOx$>g;p&(WnhH6fYKMq71sza*elGHFyzT zNPIVF5n6Pb9n8$&3wSgMoXv3B$C6Mh1fewGk~#e>zp;A#;b65xG}uIkv|TbiuX_H{ zk&Epb2jy&{55H9X#uX)4CZOX@#Zq2#rw<$&plbvIOi;aXCP=0bJUn3c-RxUQ+%1X* z{>fL~SNpafs_Cq6Q#Z8rzSI7;tgaj)tW-6%1zF{q_Q!hHHYCdG6KgDHrSE2tnfv2@ z*#3!n`zLrG>Rg06WEV2S+hbHQ5ecCgnnkz+d`6wy7t4G@cPx&bJ`uY72A&*2kiR() z6bXoV6U+i~@qib)t=M{V>dOo`ML-S4(`fXOqhDdqDM`!8!N1|({Bm;AN^(==Jist4j@u&|VHkfH@Du$@Qy2AQ$ zyS=B!4Apu-Qm z??=AR!Q1>cw5nx=g{6hW@|2gSS+|amKUv#qsXH{+_oKfB=iXcIlJfGBa)=elxEVFOi~iUHd&I=pcASXucdT%& zI1%%L?ZgRx=S$9)Xz&P5Vg--jbHH8UD3D7bnD#I%oeT0z8Q3~q@{90U0|W>Iq7TOh z1NXBNgAP&M96-(t7<7ax5CV`lsF`;0Kr{)mF%V-31dg>2)dn!v5Y0Px-e3)^bLR_u zAk-tD0EPi=Wb4oq5)tMOdh~ZfmOf-|vv(;;YY^!I0+^8?SJRo`dC@ukP#kZu9gS@X z7R zCS-&8Ac`H_`5nyExf3wSe-KjId?+zTryShb!;;qltDAkOl@Z$Z084;cCoF^bIV@Ee zi3{;N-Umb2864mq;zq|m6=t(Nu}cM>#x8r?A+v@+MLw**Gn*WdKniw(tq8euTdsi8Zq0W~rrMOat z%m0Qa9T0xxB&|C-8&94BV}cy@fj6lSv`8TpH^P5~fbH1MJPwr1O5YI>fq5L>0N%zO zpw)L380LDgt&xsGhe10dgc}3xt5^u(a<_ofE8Q_ik&>4J5mvKj)0vr&g(IvQf*&EM z=Wz@dRD$rSN=YG=v%iJN&b$_g?5u8v$WA1*LC~f?kA!H=1=V$Z2@4m*i z!)jf11|vI|n8CTKI0gr=6lqxSh(fRxsD;zUZFwYAz1w8iX;p%+pFb`A>8H=%KcT*I z^vK~Cl@~X6uZ!LX%cM?9PfXsuNtT-rdYCFNudJd#gZ+NZs4Z-@H~OP-Um>6O(8DSS zoDRl3UI$DI2g5tT@K!iGt*{MN6a;gygZes?bp@Y!A_yRcap%RV1Aj6_&7Kx;2d?wJhEtaB~olpbt#z|334}xAjCm}zo^*y)xKLutVI8W?{JDyFB1Q@ zZ_8I|ht9Q2;aCbEKK)ESZ-CDnes(Q&ErZV-ejfVF;b+G(wNC)OE>Uz9__G-Nz3=RO zZ6z2L7<36;qB{jz2UcO}R4@MkgsPa&d5c9es2Nn#RuU84VO2XdgMo>XE1Z^x!2y&xJLkH-3zbN3m%kH8KljihAJNb-ug>0nsnuBd*6X?d6;)zd+r*T zW2CS(mmnq)+H`6@{E%?I6J&tp0rb`DATh%L%b^w|O)E&6u#ND-5T68qh?oB|I~X|p z2@cFJ@H7ifZHSfthPe--wSjaqP6Yd#K)hyrfmUFjYbnTCJU^_5+x3N53hR# z%hh$(x|pT}S$1`GUZbk5zWG3NVQWdVrl`BPyIbklk4}H?SP7qr0PoF%gUtaaGMsqM zLWgx1?>y+dy%z!%qyh8|Q3L#d1ncPA3r`1b?*eB7@SU5^Ai{UTK*kTiV-(5hX({SM zd~#Y-s|GzOZEb1-=Sncs(wLU4DMm9C=_P4d;9uOpB&F3gYEqmc8a&F?73#_=d%0bO zOpM)LR8XaQxY8$jL6_Ykc&_$lHY{ri9Qr?lgOz-=rM)PkfMXZbcU8L&C61U zPD*?Y2U(X+x>f4h?fglZc;v8 z4XQz@C<#qQf2!cj1MkmH#g|cl&Gf^j-P?oJ;GFSuJ$4<3t(D<3({U9}#P2J0<+>`p zx+3xLwwx_^=b~}Sgz9{Iih9qH1F>&>{Td2=L3RG-`qbw&u{VB6y{SUe(A4wqAe9D; z`f9Wr?Y)Yw${Ma#zj>8d_#v(fJp@s(pg{&fWG{s1xT8FPC^iG04cu0s8#oI-dO3!C z)ukmxrS$QQT{BkW8dtF1<*URuP!?W^j$vPQNohq19dkwZ{d=g!5q!$w3*la{n*$Ow zUgQWyI(rdKs&+03P}IdMxon^wJ+EegJG^7B0Xxyc%CLKZ^bQ;6Uhr6Dl5U z*PMIqT+i`;$Qlk-w;v`8L*z602~b(lJVNvDvqSXW2=x9Z55$h2lomT!MMg4@`|!bbNtJ)t8(lGj!JyO57)!Bt(Pt>F0vKDH>o6MXX+Gi=;uJYQV7SX zDF7jBiywIBDywp93TsRJOKtE~7}!oUH*Z3GK79S*zYT3e^>CeVRgw<&V*iqIh%Zr9 zSC>^(g0^$Bwx+V7sNNq3IoG3kXx`16S5eTqtNx(10=0Et1*sM6Fn;`rt0#cl1;ImD zSRpS5K1Zw^3dHeOM zu@muwpA$d5brnd044QhC_)A~aod2Qw`&c>N|F)9h5%!0F8W~ zOX7qE><;<;HLE}y1wH9Hs3Sy80@-H}q@3Y{UXUS<^Hw5*49O3md?gc|=`UFU{A{4D zfsjB9Qhx~vM5zLGEd^u)kVD*p1(97&Lo5)Q4r>Qeb258EQC(D1Sf$265MffCpAA7} zu0Bx7gPCP)Q$bU99Yk<~t)Ve9xh6@Kl$@ImT2Y@%PG@Hoq@^K<+=iYnHXFSjIS=0spgd563i}N>f zk6XpVsBFQsxjg;O?JtUpi3k7a-Q)VbjFxT zvu)6pLrfF{lxH+gg0LQH5P-V>h`o9|_GVmVuA$1Ut2S;}6C%w{$x2C4(R#2LTireA zGXTz?AH*3;N=>Ee2jA~L^BMn|dECX&Z;-VqG#0AMi!9bMen9!STMt!W*k*AJ@r}uQ zOwxJ#0$W;D`|_L0>bXB)X}$J3c{4?dR8nb)ib(I>Bhm|}!`AHMjyMjLHP^%~-Mo6` zw)brZ^7oZWu@o)zM-Yj0asEV>kgepk&VHgHWG&VNHI`!fX8XTrvGZR*G;ak; z_W2{SfrA;dl|CgNoxWurPdk&P60(Nu^~V4|r@17&e~&0W^3bDNU~(%E9)-op%uY-c z!!*o*9Hxl@^o{X&85^7#&^;#N47#r>34Hv6m?MO%%Dp&A&K~$gK==z0Z!KOreIzYJ zA#wr=C8jcPn25upDggj}Cvm6@vF=Xfc`&lY418P3?p#c^TJ*y6+{M}Iawy-Ig>1DK zY~u>H*|&zM-k0?pe*4j*+qWO>+>w@4$0gOJ?bxYe?;qVB-jj3QZPzMy(gsqpp^5YA zFX&!-O}Fjd=*mbQYb6XH(N}FJ(GedN384c>e;Q10bUcFbZU6}(KwzBws*Q6FYaiCZ zZ#>h|a>fHt=4mJiy?OObZ6j8`8bz?L28{2 zw?jE)-rUJk=AOM;r}^|8;JYqI*Z+LN$?fbzkl5X$ltsyf3BcYCtWMdHv^{aV?~eVu z_U_y-&9MQ@s@g$iq|>$<&YF(d2q6oj0kB)y(C~t={B60uI#4%?j0yP(YC21tkd&N| z!6z;?Xbnq3Q^JzN5~<{SpB&GQAwU;D7aGMQZ2-R`&61Xr&NZyxwPDBF#4vqW>NfgX zxDR65@rf!rQ<9LESY+hLz;MUbg3zK+-;i~|8$#AgK|X~5LkN-i*M)PyeIgfQ&ov|Y zKxE(5B-QHcQhlqzLP;5J54mbj=OuLx1%qt?^bw&`B{My_)@>-2gp*gR(Pz9{PZ%WcbGeJfMYUJa}R{xq( z!4Wm+0@+>hv3$}5nLGtwdB2d)!dJ|$Z2BieX4oF0#rORpS2BDwoUT1t*y&<5l|L z6PbO#Ve63PCayBPXnBxIzSa7(#u8(Wjs~D}bToL~v?1%ZN$GZW z!(kqL9+nsmT)E>$aPm%m1+I3V)#N2Ly7HrVueeoKd$91>F;#VDO?nmAaHRC?IaN1U zZ&vTC^W|P??H8 zt(!nK+>8$!$*cVzZrvGPA673t_b$aqj8zAT<+D#>a3p8$?kzvX?;}qU@g5?BC5kU9 zNte%;U|{64t-UaPaW-@T5p?cToA-<*J~B<&ohWw)w!cW5@;|KTS&P zdM@^C&=Jm7WvQuF;Sk3XkA)rN%thJ7MXHv_mUYKCt3-bAB$=I!*|QU!uBKhZbP#=E z{Sx{zpByqec&nOX;AWqEGK|~B`?q~EWY@agEBCD0xAy$>Ep+Iw{iNP-%OAfs{d|!=I z%ex;^FJ#^vx*H}$k2uZ0HJ)?}>4_CsabMZA&Jc#Ys@R)F(Rw9Lnly(JKiTo73>MNq zq;8P#^nSs+0)*yGh>sxm?VNs(q>+3~)5-AR<@jg7zvM1>+fC`5PU709ONw3o%D0y+ z7|mswByTJ^_0cCMPF%l!bkVeIUby+#Unxi=_cmXCea8A#Yhts;gSNn2s#9Pz3USvXoF>* z1qz5+X8?tr|2n`1gQ*WEI3#r%uqSZ+d-PuzdxCevO7{WvelUFa4`d{OX2>D4?1)DchD@fD zkx%dkAp|kmQ5vKI{Ml#3kIgO2u;~m?lEMpM-UP%pX}gRT#qSnQ+qz-D6$q_np!we% z#v?kG2bBWvH=AG#w*FfNQ__W`u+YjV21KEFU3k~oQ%RRJQ(xlui|RfS2y{pT?e^Yl zoa-{#q3lO}fkjxdhI{XB1CWzLfSViu(}yU&meJ<>;tZL)HC{G=GR2dFGCGgM(hcOp zc<#XBrr@#!>B(h9OJ=BM1i{H1Fk=7*NWK%0{1(am0WAXt1hurZ6dgNxgexm*+I8T# zlzdnWQp*O$sKYg~>3mgubySt5{$3Fhd@G5fmb|miIhNGRb505zc}JO(V|1k3puUlv zVK8KvQ|##wWHRMgrSb{-)fbf+_Ed`@!;qN;Vuv*?H#5f~&5~GivT_Y}>8uM%b55o; z-2&{m$(U)(uo!Ha)=Zn(Y?0OnDswC*yTN9#rXh)#k(r%lO}85C#+)1}!T?>BW?Q-) z$N&gO7?C!&r8$gJd2c<)gch?+dfA|~r&?1?TuPcDJ&%jV_J>m7EhjX#&CG}$0P zV@ffmr)Q^Sg970&18-w9*`%(;t~pG_3l3q!?yMtxnd!T?G&{m;R=oLg7VQ$ITGp7= z0HX<~kKqLViyF`ZX25vy#L&qLUWauretq((&qI0l`2SD>mMinB4LhRCn7V~eVN$Fu zP8}EPK`3b5+K*vxxV7R}@zhr)XmR%Is!M9}cy4h%WV1ykvRAQnh@pe{fv& z4*p=(dxuqWYvqlw>o-&+{ZrCN-X*Vc=MP?M_+-0u_wDcZ{HT^2{IRNumXT-n?|1B1 z=UB5$IlSCH!4a1o75#4VyDL-+@C;qngg&E|n?r_%!H$Fxa>!;Y#Q zJ9