Add Tag class.
Change-Id: Ia7240e96dd6da69d373f385bde67e8d3aa70f7b1
This commit is contained in:
		
							
								
								
									
										213
									
								
								pygit2.c
									
									
									
									
									
								
							
							
						
						
									
										213
									
								
								pygit2.c
									
									
									
									
									
								
							@@ -32,13 +32,14 @@
 | 
			
		||||
#include <git/repository.h>
 | 
			
		||||
#include <git/commit.h>
 | 
			
		||||
#include <git/odb.h>
 | 
			
		||||
#include <git/tag.h>
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
    git_repository *repo;
 | 
			
		||||
} Repository;
 | 
			
		||||
 | 
			
		||||
/* The structs for the various object subtypes are identical except for the type
 | 
			
		||||
/* 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 {\
 | 
			
		||||
@@ -53,6 +54,14 @@ 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;
 | 
			
		||||
@@ -65,6 +74,7 @@ static PyTypeObject CommitType;
 | 
			
		||||
static PyTypeObject TreeEntryType;
 | 
			
		||||
static PyTypeObject TreeType;
 | 
			
		||||
static PyTypeObject BlobType;
 | 
			
		||||
static PyTypeObject TagType;
 | 
			
		||||
 | 
			
		||||
static PyObject *GitError;
 | 
			
		||||
 | 
			
		||||
@@ -182,7 +192,29 @@ 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) {
 | 
			
		||||
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:
 | 
			
		||||
@@ -195,7 +227,7 @@ static Object *wrap_object(git_object *obj, Repository *repo) {
 | 
			
		||||
            py_obj = (Object*)BlobType.tp_alloc(&BlobType, 0);
 | 
			
		||||
            break;
 | 
			
		||||
        case GIT_OBJ_TAG:
 | 
			
		||||
            py_obj = (Object*)ObjectType.tp_alloc(&ObjectType, 0);
 | 
			
		||||
            py_obj = (Object*)wrap_tag((git_tag*)obj, repo);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            assert(0);
 | 
			
		||||
@@ -500,7 +532,8 @@ Commit_get_message(Commit *commit) {
 | 
			
		||||
static int
 | 
			
		||||
Commit_set_message(Commit *commit, PyObject *message) {
 | 
			
		||||
    if (!PyString_Check(message)) {
 | 
			
		||||
        PyErr_SetString(PyExc_TypeError, "Expected string for commit 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));
 | 
			
		||||
@@ -1018,6 +1051,171 @@ static PyTypeObject BlobType = {
 | 
			
		||||
    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 Object *
 | 
			
		||||
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 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) {
 | 
			
		||||
    git_person *tagger;
 | 
			
		||||
    tagger = (git_person*)git_tag_tagger(tag->tag);
 | 
			
		||||
    if (!tagger)
 | 
			
		||||
        Py_RETURN_NONE;
 | 
			
		||||
    return Py_BuildValue("(ssl)", git_person_name(tagger),
 | 
			
		||||
                         git_person_email(tagger),
 | 
			
		||||
                         git_person_time(tagger));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
Tag_set_tagger(Tag *tag, PyObject *value) {
 | 
			
		||||
    char *name = NULL, *email = NULL;
 | 
			
		||||
    long long time;
 | 
			
		||||
    if (!PyArg_ParseTuple(value, "ssL", &name, &email, &time))
 | 
			
		||||
        return -1;
 | 
			
		||||
    git_tag_set_tagger(tag->tag, name, email, time);
 | 
			
		||||
    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 PyMethodDef module_methods[] = {
 | 
			
		||||
    {NULL}
 | 
			
		||||
};
 | 
			
		||||
@@ -1051,6 +1249,10 @@ initpygit2(void)
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    m = Py_InitModule3("pygit2", module_methods,
 | 
			
		||||
                       "Python bindings for libgit2.");
 | 
			
		||||
@@ -1079,6 +1281,9 @@ initpygit2(void)
 | 
			
		||||
    Py_INCREF(&BlobType);
 | 
			
		||||
    PyModule_AddObject(m, "Blob", (PyObject *)&BlobType);
 | 
			
		||||
 | 
			
		||||
    Py_INCREF(&TagType);
 | 
			
		||||
    PyModule_AddObject(m, "Tag", (PyObject *)&TagType);
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ import unittest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_suite():
 | 
			
		||||
    names = ['blob', 'commit', 'repository', 'tree']
 | 
			
		||||
    names = ['blob', 'commit', 'repository', 'tag', 'tree']
 | 
			
		||||
    modules = ['test.test_%s' % n for n in names]
 | 
			
		||||
    return unittest.defaultTestLoader.loadTestsFromNames(modules)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,2 @@
 | 
			
		||||
x-<2D>A
 | 
			
		||||
<EFBFBD>0@Q<>9<EFBFBD>\<5C><><EFBFBD>m<EFBFBD>@<11><><05><>$T,#eP<65><50><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>A<EFBFBD><07><02><>H%a<><61><EFBFBD>:<3A><><EFBFBD><EFBFBD>`{B<17>іl<D196>%<25><><03><Ojt5<16>-*/pKo<4B><6F>,<2C><><EFBFBD>C<1E><>T<EFBFBD><54><EFBFBD>f<EFBFBD><66><EFBFBD>0<04>m<11><>h<EFBFBD><68><EFBFBD>Z9o<39>.7<EFBFBD>yP1<50>
 | 
			
		||||
							
								
								
									
										1
									
								
								test/data/testrepo.git/refs/tags/root
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/data/testrepo.git/refs/tags/root
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
3d2962987c695a29f1f80b6c3aa4ec046ef44369
 | 
			
		||||
							
								
								
									
										95
									
								
								test/test_tag.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								test/test_tag.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
#
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
"""Tests for Tag objects."""
 | 
			
		||||
 | 
			
		||||
__author__ = 'dborowitz@google.com (Dave Borowitz)'
 | 
			
		||||
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
import pygit2
 | 
			
		||||
import utils
 | 
			
		||||
 | 
			
		||||
TAG_SHA = '3d2962987c695a29f1f80b6c3aa4ec046ef44369'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TagTest(utils.TestRepoTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_read_tag(self):
 | 
			
		||||
        tag = self.repo[TAG_SHA]
 | 
			
		||||
        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('root', tag.name)
 | 
			
		||||
        self.assertEqual(('Dave Borowitz', 'dborowitz@google.com', 1288724692),
 | 
			
		||||
                         tag.tagger)
 | 
			
		||||
        self.assertEqual('Tagged root commit.\n', tag.message)
 | 
			
		||||
 | 
			
		||||
        commit = tag.target
 | 
			
		||||
        del tag
 | 
			
		||||
        self.assertEqual('Initial test data commit.', commit.message_short)
 | 
			
		||||
 | 
			
		||||
    def test_new_tag(self):
 | 
			
		||||
        name = 'thetag'
 | 
			
		||||
        message = 'Tag a blob.\n'
 | 
			
		||||
        tagger = ('John Doe', 'jdoe@example.com', 12347)
 | 
			
		||||
 | 
			
		||||
        tag = pygit2.Tag(self.repo)
 | 
			
		||||
        self.assertEqual(None, tag.sha)
 | 
			
		||||
        self.assertEqual(None, tag.name)
 | 
			
		||||
        self.assertEqual(None, tag.tagger)
 | 
			
		||||
        self.assertEqual(None, tag.message)
 | 
			
		||||
        self.assertEqual(None, tag.target)
 | 
			
		||||
 | 
			
		||||
        tag.name = name
 | 
			
		||||
        tag.tagger = tagger
 | 
			
		||||
        tag.message = message
 | 
			
		||||
        target = self.repo['af431f20fc541ed6d5afede3e2dc7160f6f01f16']
 | 
			
		||||
        target_sha = target.sha
 | 
			
		||||
        tag.target = target
 | 
			
		||||
        del target
 | 
			
		||||
        self.assertEqual(name, tag.name)
 | 
			
		||||
        self.assertEqual(tagger, tag.tagger)
 | 
			
		||||
        self.assertEqual(target_sha, tag.target.sha)
 | 
			
		||||
 | 
			
		||||
        tag.write()
 | 
			
		||||
        self.assertEqual(name, self.repo[tag.sha].name)
 | 
			
		||||
 | 
			
		||||
    def test_modify_tag(self):
 | 
			
		||||
        message = 'Different message.\n'
 | 
			
		||||
        tag = self.repo[TAG_SHA]
 | 
			
		||||
        old_sha = tag.sha
 | 
			
		||||
        self.assertNotEqual(message, tag.message)
 | 
			
		||||
        tag.message = message
 | 
			
		||||
        self.assertEqual(message, tag.message)
 | 
			
		||||
        tag.write()
 | 
			
		||||
        self.assertNotEqual(old_sha, tag.sha)
 | 
			
		||||
        self.assertEqual(tag.sha, self.repo[tag.sha].sha)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
  unittest.main()
 | 
			
		||||
		Reference in New Issue
	
	Block a user