578 lines
18 KiB
C
578 lines
18 KiB
C
/*
|
|
* 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 <Python.h>
|
|
#include <string.h>
|
|
#include <structmember.h>
|
|
#include "object.h"
|
|
#include "error.h"
|
|
#include "types.h"
|
|
#include "utils.h"
|
|
#include "oid.h"
|
|
#include "signature.h"
|
|
#include "reference.h"
|
|
|
|
|
|
extern PyObject *GitError;
|
|
extern PyTypeObject RefLogEntryType;
|
|
extern PyTypeObject SignatureType;
|
|
|
|
|
|
void RefLogIter_dealloc(RefLogIter *self)
|
|
{
|
|
git_reflog_free(self->reflog);
|
|
PyObject_Del(self);
|
|
}
|
|
|
|
PyObject* RefLogIter_iternext(RefLogIter *self)
|
|
{
|
|
const git_reflog_entry *entry;
|
|
RefLogEntry *py_entry;
|
|
|
|
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));
|
|
|
|
++(self->i);
|
|
|
|
return (PyObject*) py_entry;
|
|
}
|
|
|
|
PyErr_SetNone(PyExc_StopIteration);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(RefLogIterType__doc__, "Internal reflog iterator object.");
|
|
|
|
PyTypeObject RefLogIterType = {
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
"_pygit2.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
|
|
Reference_dealloc(Reference *self)
|
|
{
|
|
Py_CLEAR(self->repo);
|
|
git_reference_free(self->reference);
|
|
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)
|
|
{
|
|
int err;
|
|
|
|
CHECK_REFERENCE(self);
|
|
|
|
/* Delete the reference */
|
|
err = git_reference_delete(self->reference);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
git_reference_free(self->reference);
|
|
self->reference = NULL; /* Invalidate the pointer */
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Reference_rename__doc__,
|
|
"rename(new_name)\n"
|
|
"\n"
|
|
"Rename the reference.");
|
|
|
|
PyObject *
|
|
Reference_rename(Reference *self, PyObject *py_name)
|
|
{
|
|
char *c_name;
|
|
int err;
|
|
git_reference *new_reference;
|
|
|
|
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(&new_reference, self->reference, c_name, 0);
|
|
git_reference_free(self->reference);
|
|
free(c_name);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
self->reference = new_reference;
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
git_reference *c_reference;
|
|
int err;
|
|
|
|
CHECK_REFERENCE(self);
|
|
|
|
/* Direct: return myself */
|
|
if (git_reference_type(self->reference) == GIT_REF_OID) {
|
|
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, self->repo);
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
const char * c_name;
|
|
|
|
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));
|
|
|
|
/* Case 2: Symbolic */
|
|
c_name = git_reference_symbolic_target(self->reference);
|
|
if (c_name == NULL) {
|
|
PyErr_SetString(PyExc_ValueError, "no target available");
|
|
return NULL;
|
|
}
|
|
return to_path(c_name);
|
|
}
|
|
|
|
int
|
|
Reference_target__set__(Reference *self, PyObject *py_target)
|
|
{
|
|
git_oid oid;
|
|
char *c_name;
|
|
int err;
|
|
git_reference *new_ref;
|
|
|
|
CHECK_REFERENCE_INT(self);
|
|
|
|
/* Case 1: Direct */
|
|
if (GIT_REF_OID == git_reference_type(self->reference)) {
|
|
err = py_oid_to_git_oid_expand(self->repo->repo, py_target, &oid);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
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;
|
|
|
|
err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name);
|
|
free(c_name);
|
|
if (err < 0)
|
|
goto error;
|
|
|
|
git_reference_free(self->reference);
|
|
self->reference = new_ref;
|
|
return 0;
|
|
|
|
error:
|
|
Error_set(err);
|
|
return -1;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Reference_name__doc__, "The full name of the reference.");
|
|
|
|
PyObject *
|
|
Reference_name__get__(Reference *self)
|
|
{
|
|
CHECK_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.");
|
|
|
|
PyObject *
|
|
Reference_type__get__(Reference *self)
|
|
{
|
|
git_ref_t c_type;
|
|
|
|
CHECK_REFERENCE(self);
|
|
c_type = git_reference_type(self->reference);
|
|
return PyLong_FromLong(c_type);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Reference_log__doc__,
|
|
"log() -> RefLogIter\n"
|
|
"\n"
|
|
"Retrieves the current reference log.");
|
|
|
|
PyObject *
|
|
Reference_log(Reference *self)
|
|
{
|
|
RefLogIter *iter;
|
|
|
|
CHECK_REFERENCE(self);
|
|
|
|
iter = PyObject_New(RefLogIter, &RefLogIterType);
|
|
if (iter != NULL) {
|
|
git_reflog_read(&iter->reflog, self->reference);
|
|
iter->size = git_reflog_entrycount(iter->reflog);
|
|
iter->i = 0;
|
|
}
|
|
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"
|
|
"\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 *
|
|
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 = NULL;
|
|
self->oid_new = NULL;
|
|
self->message = NULL;
|
|
self->signature = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
RefLogEntry_dealloc(RefLogEntry *self)
|
|
{
|
|
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_STRING, "New oid."),
|
|
MEMBER(RefLogEntry, oid_old, T_STRING, "Old oid."),
|
|
MEMBER(RefLogEntry, message, T_STRING, "Message."),
|
|
{NULL}
|
|
};
|
|
|
|
PyGetSetDef RefLogEntry_getseters[] = {
|
|
GETTER(RefLogEntry, committer),
|
|
{NULL}
|
|
};
|
|
|
|
|
|
PyDoc_STRVAR(RefLogEntry__doc__, "Reference log object.");
|
|
|
|
PyTypeObject RefLogEntryType = {
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
"_pygit2.RefLogEntry", /* tp_name */
|
|
sizeof(RefLogEntry), /* tp_basicsize */
|
|
0, /* tp_itemsize */
|
|
(destructor)RefLogEntry_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 */
|
|
RefLogEntry__doc__, /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
0, /* tp_methods */
|
|
RefLogEntry_members, /* tp_members */
|
|
RefLogEntry_getseters, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
(initproc)RefLogEntry_init, /* tp_init */
|
|
0, /* tp_alloc */
|
|
0, /* tp_new */
|
|
};
|
|
|
|
PyMethodDef Reference_methods[] = {
|
|
METHOD(Reference, delete, METH_NOARGS),
|
|
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}
|
|
};
|
|
|
|
PyGetSetDef Reference_getseters[] = {
|
|
GETTER(Reference, name),
|
|
GETTER(Reference, shorthand),
|
|
GETSET(Reference, target),
|
|
GETTER(Reference, type),
|
|
{NULL}
|
|
};
|
|
|
|
|
|
PyDoc_STRVAR(Reference__doc__, "Reference.");
|
|
|
|
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__doc__, /* 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, Repository *repo)
|
|
{
|
|
Reference *py_reference=NULL;
|
|
|
|
py_reference = PyObject_New(Reference, &ReferenceType);
|
|
if (py_reference) {
|
|
py_reference->reference = c_reference;
|
|
if (repo) {
|
|
py_reference->repo = repo;
|
|
Py_INCREF(repo);
|
|
}
|
|
}
|
|
|
|
return (PyObject *)py_reference;
|
|
}
|