From 6ccb8306d40bcda9d0603bcaa52d601488504748 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 28 May 2012 15:32:37 +0200 Subject: [PATCH 1/7] 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 0000000..16a88d1 --- /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 0000000..8b63037 --- /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 480a56adcfa8da2dbc516039626b1312464e102a Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 30 May 2012 07:27:47 +0200 Subject: [PATCH 2/7] 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 45fd02a..246787c 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 3/7] 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 7e1ad75..6153578 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 4/7] 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 | 188 ++++++++++++++++++++++++++--------------- src/pygit2/tree.c | 27 ++++-- test/test_diff.py | 48 +++++++++++ 5 files changed, 195 insertions(+), 77 deletions(-) diff --git a/include/pygit2/diff.h b/include/pygit2/diff.h index 3060234..1f1b793 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 81f302f..37edf89 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 c957b9c..81be398 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,66 +99,77 @@ 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"); + + 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); } - 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 77c77d1..3bb671b 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 fadaff2..bc88af2 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -39,6 +39,7 @@ __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 @@ -55,6 +56,45 @@ index 297efb8..0000000 -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 @@ class DiffTest(utils.BareRepoTestCase): 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 ce4e41113569936c4bc14e906c8d6740cff79a82 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Wed, 30 May 2012 14:39:02 +0200 Subject: [PATCH 5/7] 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 81f302f..264a939 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 7e1ad75..d2b1ba8 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 37f24c4..8f8e6a9 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 9cfb0d2..47058ac 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -46,6 +46,23 @@ log = [ '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 6/7] 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 c957b9c..47aae46 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 7/7] use generator instead of python list --- include/pygit2/types.h | 11 ++- src/pygit2.c | 5 +- src/pygit2/reference.c | 164 +++++++++++++++++++++++++++-------------- 3 files changed, 121 insertions(+), 59 deletions(-) diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 72d851e..a8e1ef8 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 70b0550..bbe89ae 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 8f8e6a9..60da959 100644 --- a/src/pygit2/reference.c +++ b/src/pygit2/reference.c @@ -9,9 +9,92 @@ #include #include -extern PyObject *GitError; -extern PyTypeObject ReferenceLogEntryType; +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; +} + +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); + 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; - 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); + 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 */ };