/* * Copyright 2010 Google, Inc. * * 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 <Python.h> #include <git2.h> 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;\ int own_obj:1;\ _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(Blob, git_object, blob) typedef struct { PyObject_HEAD Repository *repo; int own_obj:1; git_tag *tag; Object *target; } Tag; typedef struct { PyObject_HEAD git_tree_entry *entry; Tree *tree; } TreeEntry; typedef struct { PyObject_HEAD Repository *repo; git_index *index; } Index; static PyTypeObject RepositoryType; static PyTypeObject ObjectType; static PyTypeObject CommitType; static PyTypeObject TreeEntryType; static PyTypeObject TreeType; static PyTypeObject BlobType; static PyTypeObject TagType; static PyTypeObject IndexType; static PyObject *GitError; static PyObject * Error_type(int err) { switch (err) { case GIT_ENOTFOUND: return PyExc_KeyError; case GIT_EOSERR: return PyExc_OSError; case GIT_ENOTOID: return PyExc_ValueError; case GIT_ENOMEM: return PyExc_MemoryError; default: return GitError; } } 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_strerror. */ PyErr_SetNone(PyExc_KeyError); return NULL; } else if (err == GIT_EOSERR) { PyErr_SetFromErrno(GitError); return NULL; } PyErr_SetString(Error_type(err), git_strerror(err)); 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_Format(PyExc_KeyError, "%s", str); return NULL; } PyErr_Format(Error_type(err), "%s: %s", str, git_strerror(err)); return NULL; } static PyObject * Error_set_py_obj(int err, PyObject *py_obj) { PyObject *py_str; char *str; assert(err < 0); if (err == GIT_ENOTOID && !PyString_Check(py_obj)) { PyErr_Format(PyExc_TypeError, "Git object id must be str, not %.200s", py_obj->ob_type->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_Str(py_obj); str = py_str ? PyString_AS_STRING(py_str) : "<error in __str__>"; PyErr_Format(Error_type(err), "%s: %s", str, git_strerror(err)); Py_XDECREF(py_str); return NULL; } static int py_str_to_git_oid(PyObject *py_str, git_oid *oid) { char *hex; hex = PyString_AsString(py_str); if (!hex) return GIT_ENOTOID; return git_oid_mkstr(oid, hex); } static int Repository_init(Repository *self, PyObject *args, PyObject *kwds) { char *path; int err; if (kwds) { PyErr_SetString(PyExc_TypeError, "Repository takes no keyword arugments"); 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) { if (self->repo) git_repository_free(self->repo); Py_XDECREF(self->index); self->ob_type->tp_free((PyObject*)self); } static int Repository_contains(Repository *self, PyObject *value) { git_oid oid; int err; err = py_str_to_git_oid(value, &oid); if (err < 0) { Error_set_py_obj(err, value); return -1; } return git_odb_exists(git_repository_database(self->repo), &oid); } static Object *wrap_object(git_object *, Repository *repo); static Tag * wrap_tag(git_tag *tag, Repository *repo) { Tag *py_tag; Object *py_target; py_tag = (Tag*)TagType.tp_alloc(&TagType, 0); if (!py_tag) return NULL; py_target = wrap_object((git_object*)git_tag_target(tag), repo); if (!py_target) { py_tag->ob_type->tp_free((PyObject*)py_tag); return NULL; } py_tag->target = py_target; Py_INCREF(py_target); return py_tag; } 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*)wrap_tag((git_tag*)obj, repo); 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; int err; git_object *obj; Object *py_obj; err = py_str_to_git_oid(value, &oid); if (err < 0) return Error_set_py_obj(err, value); err = git_repository_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; py_obj->own_obj = 0; return (PyObject*)py_obj; } static int Repository_read_raw(git_rawobj *raw, git_repository *repo, const git_oid *oid) { return git_odb_read(raw, git_repository_database(repo), oid); } static PyObject * Repository_read(Repository *self, PyObject *py_hex) { git_oid oid; int err; git_rawobj raw; PyObject *result; err = py_str_to_git_oid(py_hex, &oid); if (err < 0) return Error_set_py_obj(err, py_hex); err = Repository_read_raw(&raw, self->repo, &oid); if (err < 0) return Error_set_py_obj(err, py_hex); result = Py_BuildValue("(ns#)", raw.type, raw.data, raw.len); free(raw.data); return result; } 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 == GIT_SUCCESS) { py_index = (Index*)IndexType.tp_alloc(&IndexType, 0); if (!py_index) return PyErr_NoMemory(); py_index->repo = self; py_index->index = index; self->index = (PyObject*)py_index; } else if (err == GIT_EBAREINDEX) { Py_INCREF(Py_None); self->index = Py_None; } else { return Error_set(err); } } Py_INCREF(self->index); return self->index; } static PyMethodDef Repository_methods[] = { {"read", (PyCFunction)Repository_read, METH_O, "Read raw object data from the repository."}, {NULL, NULL, 0, NULL} }; static PyGetSetDef Repository_getseters[] = { {"index", (getter)Repository_get_index, NULL, "index file. ", 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 = { 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, /* 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) { if (self->own_obj) git_object_free(self->obj); Py_XDECREF(self->repo); self->ob_type->tp_free((PyObject*)self); } static PyObject * Object_get_type(Object *self) { return PyInt_FromLong(git_object_type(self->obj)); } static PyObject * Object_get_sha(Object *self) { const git_oid *id; char hex[GIT_OID_HEXSZ]; id = git_object_id(self->obj); if (!id) Py_RETURN_NONE; git_oid_fmt(hex, id); return PyString_FromStringAndSize(hex, GIT_OID_HEXSZ); } static PyObject * Object_read_raw(Object *self) { const git_oid *id; git_rawobj raw; int err; PyObject *result = NULL, *py_sha = NULL; id = git_object_id(self->obj); if (!id) Py_RETURN_NONE; /* in-memory object */ err = Repository_read_raw(&raw, self->repo->repo, id); if (err < 0) { py_sha = Object_get_sha(self); Error_set_py_obj(err, py_sha); goto cleanup; } result = PyString_FromStringAndSize(raw.data, raw.len); cleanup: Py_XDECREF(py_sha); free(raw.data); return result; } static PyObject * Object_write(Object *self) { int err; PyObject *py_sha; err = git_object_write(self->obj); if (err < 0) { py_sha = Object_get_sha(self); Error_set_py_obj(err, py_sha); Py_DECREF(py_sha); return NULL; } Py_RETURN_NONE; } static PyGetSetDef Object_getseters[] = { {"type", (getter)Object_get_type, NULL, "type number", NULL}, {"sha", (getter)Object_get_sha, NULL, "hex SHA", NULL}, {NULL} }; static PyMethodDef Object_methods[] = { {"read_raw", (PyCFunction)Object_read_raw, METH_NOARGS, "Read the raw contents of the object from the repo."}, {"write", (PyCFunction)Object_write, METH_NOARGS, "Write the object to the repo, if changed."}, {NULL} }; 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, /* 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 int Object_init_with_type(Object *py_obj, const git_otype type, PyObject *args, PyObject *kwds) { Repository *repo = NULL; git_object *obj; int err; if (kwds) { PyErr_Format(PyExc_TypeError, "%s takes no keyword arugments", py_obj->ob_type->tp_name); return -1; } if (!PyArg_ParseTuple(args, "O", &repo)) return -1; if (!PyObject_TypeCheck(repo, &RepositoryType)) { PyErr_Format(PyExc_TypeError, "repo argument must be %.200s, not %.200s", RepositoryType.tp_name, repo->ob_type->tp_name); return -1; } err = git_repository_newobject(&obj, repo->repo, type); if (err < 0) { Error_set(err); return -1; } Py_INCREF(repo); py_obj->repo = repo; py_obj->own_obj = 1; py_obj->obj = obj; return 0; } static PyObject * build_person(const char *name, const char *email, long long time) { return Py_BuildValue("(ssL)", name, email, time); } static int parse_person(PyObject *value, char **name, char **email, long long *time) { return PyArg_ParseTuple(value, "ssL", name, email, time); } static int Commit_init(Commit *py_commit, PyObject *args, PyObject *kwds) { return Object_init_with_type((Object*)py_commit, GIT_OBJ_COMMIT, args, kwds); } static PyObject * Commit_get_message_short(Commit *commit) { return PyString_FromString(git_commit_message_short(commit->commit)); } static PyObject * Commit_get_message(Commit *commit) { return PyString_FromString(git_commit_message(commit->commit)); } static int Commit_set_message(Commit *commit, PyObject *message) { if (!PyString_Check(message)) { PyErr_Format(PyExc_TypeError, "message must be str, not %.200s", message->ob_type->tp_name); return -1; } git_commit_set_message(commit->commit, PyString_AS_STRING(message)); return 0; } static PyObject * Commit_get_commit_time(Commit *commit) { return PyLong_FromLong(git_commit_time(commit->commit)); } static PyObject * Commit_get_committer(Commit *commit) { const git_signature *committer = git_commit_committer(commit->commit); return build_person(committer->name, committer->email, committer->when.time); } static int Commit_set_committer(Commit *commit, PyObject *value) { char *name = NULL, *email = NULL; long long time; if (!parse_person(value, &name, &email, &time)) return -1; git_signature *signature = git_signature_new(name, email, time, 0); if ( signature == NULL) return -1; git_commit_set_committer(commit->commit, signature); return 0; } static PyObject * Commit_get_author(Commit *commit) { const git_signature *author = git_commit_author(commit->commit); return build_person(author->name, author->email, author->when.time); } static int Commit_set_author(Commit *commit, PyObject *value) { char *name = NULL, *email = NULL; long long time; if (!parse_person(value, &name, &email, &time)) return -1; git_signature *signature = git_signature_new(name, email, time, 0); if ( signature == NULL) return -1; git_commit_set_author(commit->commit, signature); return 0; } static PyGetSetDef Commit_getseters[] = { {"message_short", (getter)Commit_get_message_short, NULL, "short message", NULL}, {"message", (getter)Commit_get_message, (setter)Commit_set_message, "message", NULL}, {"commit_time", (getter)Commit_get_commit_time, NULL, "commit time", NULL}, {"committer", (getter)Commit_get_committer, (setter)Commit_set_committer, "committer", NULL}, {"author", (getter)Commit_get_author, (setter)Commit_set_author, "author", NULL}, {NULL} }; 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, /* 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 */ (initproc)Commit_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; static void TreeEntry_dealloc(TreeEntry *self) { Py_XDECREF(self->tree); self->ob_type->tp_free((PyObject *)self); } static PyObject * TreeEntry_get_attributes(TreeEntry *self) { return PyInt_FromLong(git_tree_entry_attributes(self->entry)); } static int TreeEntry_set_attributes(TreeEntry *self, PyObject *value) { unsigned int attributes; attributes = PyInt_AsLong(value); if (PyErr_Occurred()) return -1; git_tree_entry_set_attributes(self->entry, attributes); return 0; } static PyObject * TreeEntry_get_name(TreeEntry *self) { return PyString_FromString(git_tree_entry_name(self->entry)); } static int TreeEntry_set_name(TreeEntry *self, PyObject *value) { char *name; name = PyString_AsString(value); if (!name) return -1; git_tree_entry_set_name(self->entry, name); return 0; } 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); } static int TreeEntry_set_sha(TreeEntry *self, PyObject *value) { git_oid oid; int err; err = py_str_to_git_oid(value, &oid); if (err < 0) { Error_set_py_obj(err, value); return -1; } git_tree_entry_set_id(self->entry, &oid); return 0; } static PyObject * TreeEntry_to_object(TreeEntry *self) { git_object *obj; int err; char hex[GIT_OID_HEXSZ + 1]; err = git_tree_entry_2object(&obj, 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); } static PyGetSetDef TreeEntry_getseters[] = { {"attributes", (getter)TreeEntry_get_attributes, (setter)TreeEntry_set_attributes, "attributes", NULL}, {"name", (getter)TreeEntry_get_name, (setter)TreeEntry_set_name, "name", NULL}, {"sha", (getter)TreeEntry_get_sha, (setter)TreeEntry_set_sha, "sha", 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 = { 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, /* 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 int Tree_init(Tree *py_tree, PyObject *args, PyObject *kwds) { return Object_init_with_type((Object*)py_tree, GIT_OBJ_TREE, args, kwds); } static Py_ssize_t Tree_len(Tree *self) { return (Py_ssize_t)git_tree_entrycount(self->tree); } static int Tree_contains(Tree *self, PyObject *py_name) { char *name; name = PyString_AsString(py_name); return name && git_tree_entry_byname(self->tree, name) ? 1 : 0; } static TreeEntry * wrap_tree_entry(git_tree_entry *entry, Tree *tree) { TreeEntry *py_entry = NULL; py_entry = (TreeEntry*)TreeEntryType.tp_alloc(&TreeEntryType, 0); if (!py_entry) return NULL; py_entry->entry = entry; py_entry->tree = tree; Py_INCREF(tree); return py_entry; } static TreeEntry * Tree_getitem_by_name(Tree *self, PyObject *py_name) { char *name; 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) { 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 TreeEntry * Tree_getitem_by_index(Tree *self, PyObject *py_index) { int index; 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) { if (PyString_Check(value)) { return Tree_getitem_by_name(self, value); } else 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", value->ob_type->tp_name); return NULL; } } static int Tree_delitem_by_name(Tree *self, PyObject *name) { int err; err = git_tree_remove_entry_byname(self->tree, PyString_AS_STRING(name)); if (err < 0) { PyErr_SetObject(PyExc_KeyError, name); return -1; } return 0; } static int Tree_delitem_by_index(Tree *self, PyObject *py_index) { int index, err; index = Tree_fix_index(self, py_index); if (PyErr_Occurred()) return -1; err = git_tree_remove_entry_byindex(self->tree, index); if (err < 0) { PyErr_SetObject(PyExc_IndexError, py_index); return -1; } return 0; } static int Tree_delitem(Tree *self, PyObject *name, PyObject *value) { /* TODO: This function is only used for deleting items. We may be able to * come up with some reasonable assignment semantics, but it's tricky * because git_tree_entry objects are owned by their containing tree. */ if (value) { PyErr_SetString(PyExc_ValueError, "Cannot set TreeEntry directly; use add_entry."); return -1; } if (PyString_Check(name)) { return Tree_delitem_by_name(self, name); } else if (PyInt_Check(name)) { return Tree_delitem_by_index(self, name); } else { PyErr_Format(PyExc_TypeError, "Tree entry index must be int or str, not %.200s", value->ob_type->tp_name); return -1; } } static TreeEntry * Tree_add_entry(Tree *self, PyObject *args) { PyObject *py_sha; char *name; int attributes, err; git_oid oid; git_tree_entry *entry = NULL; if (!PyArg_ParseTuple(args, "Osi", &py_sha, &name, &attributes)) return NULL; err = py_str_to_git_oid(py_sha, &oid); if (err < 0) return (TreeEntry*)Error_set_py_obj(err, py_sha); if (git_tree_add_entry(&entry, self->tree, &oid, name, attributes) < 0) return (TreeEntry*)PyErr_NoMemory(); return wrap_tree_entry(entry, self); } static PyMethodDef Tree_methods[] = { {"add_entry", (PyCFunction)Tree_add_entry, METH_VARARGS, "Add an entry to a Tree."}, {NULL} }; 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 */ (objobjargproc)Tree_delitem, /* mp_ass_subscript */ }; 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, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Tree_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)Tree_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; static int Blob_init(Blob *py_blob, PyObject *args, PyObject *kwds) { return Object_init_with_type((Object*)py_blob, GIT_OBJ_BLOB, args, kwds); } /* TODO: libgit2 needs some way to set blob data. */ static PyGetSetDef Blob_getseters[] = { {"data", (getter)Object_read_raw, NULL, "raw data", NULL}, {NULL} }; 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, /* 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 */ (initproc)Blob_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; static int Tag_init(Tag *py_tag, PyObject *args, PyObject *kwds) { return Object_init_with_type((Object*)py_tag, GIT_OBJ_TAG, args, kwds); } static void Tag_dealloc(Tag *self) { Py_XDECREF(self->target); self->ob_type->tp_free((PyObject*)self); } static PyObject * Tag_get_target(Tag *self) { git_object *target; target = (git_object*)git_tag_target(self->tag); if (!target) { /* This can only happen if we have a new tag with no target set yet. In * particular, it can't happen if the tag fails to parse, since that * would have returned NULL from git_repository_lookup. */ Py_RETURN_NONE; } return (PyObject*)wrap_object(target, self->repo); } static int Tag_set_target(Tag *self, Object *target) { if (!PyObject_TypeCheck(target, &ObjectType)) { PyErr_Format(PyExc_TypeError, "target must be %.200s, not %.200s", ObjectType.tp_name, target->ob_type->tp_name); return -1; } Py_XDECREF(self->target); self->target = target; Py_INCREF(target); git_tag_set_target(self->tag, target->obj); return 0; } static PyObject * Tag_get_target_type(Tag *self) { if (!self->target) Py_RETURN_NONE; return PyInt_FromLong(git_tag_type(self->tag)); } static PyObject * Tag_get_name(Tag *self) { const char *name; name = git_tag_name(self->tag); if (!name) Py_RETURN_NONE; return PyString_FromString(name); } static int Tag_set_name(Tag *self, PyObject *py_name) { char *name; if (!PyString_Check(py_name)) { PyErr_Format(PyExc_TypeError, "name must be str, not %.200s", py_name->ob_type->tp_name); return -1; } name = PyString_AsString(py_name); if (!name) return -1; git_tag_set_name(self->tag, name); return 0; } static PyObject * Tag_get_tagger(Tag *tag) { const git_signature *tagger = git_tag_tagger(tag->tag); if (!tagger) Py_RETURN_NONE; return build_person(tagger->name, tagger->email, tagger->when.time); } static int Tag_set_tagger(Tag *tag, PyObject *value) { char *name = NULL, *email = NULL; long long time; if (!parse_person(value, &name, &email, &time)) return -1; git_signature *signature = git_signature_new(name, email, time, 0); if ( signature == NULL) return -1; git_tag_set_tagger(tag->tag, signature); return 0; } static PyObject * Tag_get_message(Tag *self) { const char *message; message = git_tag_message(self->tag); if (!message) Py_RETURN_NONE; return PyString_FromString(message); } static int Tag_set_message(Tag *self, PyObject *message) { if (!PyString_Check(message)) { PyErr_Format(PyExc_TypeError, "message must be str, not %.200s", message->ob_type->tp_name); return -1; } git_tag_set_message(self->tag, PyString_AS_STRING(message)); return 0; } static PyGetSetDef Tag_getseters[] = { {"target", (getter)Tag_get_target, (setter)Tag_set_target, "tagged object", NULL}, {"target_type", (getter)Tag_get_target_type, NULL, "type of tagged object", NULL}, {"name", (getter)Tag_get_name, (setter)Tag_set_name, "tag name", NULL}, {"tagger", (getter)Tag_get_tagger, (setter)Tag_set_tagger, "tagger", NULL}, {"message", (getter)Tag_get_message, (setter)Tag_set_message, "tag message", NULL}, {NULL} }; 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, /* 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 */ (initproc)Tag_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; static void Index_dealloc(Index* self) { Py_XDECREF(self->repo); self->ob_type->tp_free((PyObject*)self); } 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 */ 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 */ "Index file", /* 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 */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; static PyMethodDef module_methods[] = { {NULL} }; PyMODINIT_FUNC initpygit2(void) { PyObject* m; GitError = PyErr_NewException("pygit2.GitError", NULL, NULL); RepositoryType.tp_new = PyType_GenericNew; if (PyType_Ready(&RepositoryType) < 0) return; /* Do not set ObjectType.tp_new, to prevent creating Objects directly. */ if (PyType_Ready(&ObjectType) < 0) return; CommitType.tp_base = &ObjectType; CommitType.tp_new = PyType_GenericNew; if (PyType_Ready(&CommitType) < 0) return; TreeEntryType.tp_base = &ObjectType; 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; IndexType.tp_new = PyType_GenericNew; if (PyType_Ready(&IndexType) < 0) return; m = Py_InitModule3("pygit2", module_methods, "Python bindings for libgit2."); if (m == NULL) return; 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); 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); }