1727 lines
46 KiB
C
1727 lines
46 KiB
C
/*
|
|
* Copyright 2010-2015 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 "error.h"
|
|
#include "types.h"
|
|
#include "reference.h"
|
|
#include "utils.h"
|
|
#include "object.h"
|
|
#include "oid.h"
|
|
#include "note.h"
|
|
#include "repository.h"
|
|
#include "branch.h"
|
|
#include "signature.h"
|
|
#include <git2/odb_backend.h>
|
|
|
|
extern PyObject *GitError;
|
|
|
|
extern PyTypeObject IndexType;
|
|
extern PyTypeObject WalkerType;
|
|
extern PyTypeObject SignatureType;
|
|
extern PyTypeObject ObjectType;
|
|
extern PyTypeObject OidType;
|
|
extern PyTypeObject CommitType;
|
|
extern PyTypeObject TreeType;
|
|
extern PyTypeObject TreeBuilderType;
|
|
extern PyTypeObject ConfigType;
|
|
extern PyTypeObject DiffType;
|
|
extern PyTypeObject ReferenceType;
|
|
extern PyTypeObject NoteType;
|
|
extern PyTypeObject NoteIterType;
|
|
|
|
/* forward-declaration for Repsository._from_c() */
|
|
PyTypeObject RepositoryType;
|
|
|
|
git_otype
|
|
int_to_loose_object_type(int type_id)
|
|
{
|
|
switch((git_otype)type_id) {
|
|
case GIT_OBJ_COMMIT: return GIT_OBJ_COMMIT;
|
|
case GIT_OBJ_TREE: return GIT_OBJ_TREE;
|
|
case GIT_OBJ_BLOB: return GIT_OBJ_BLOB;
|
|
case GIT_OBJ_TAG: return GIT_OBJ_TAG;
|
|
default: return GIT_OBJ_BAD;
|
|
}
|
|
}
|
|
|
|
PyObject *
|
|
wrap_repository(git_repository *c_repo)
|
|
{
|
|
Repository *py_repo = PyObject_GC_New(Repository, &RepositoryType);
|
|
|
|
if (py_repo) {
|
|
py_repo->repo = c_repo;
|
|
py_repo->config = NULL;
|
|
py_repo->index = NULL;
|
|
py_repo->owned = 1;
|
|
}
|
|
|
|
return (PyObject *)py_repo;
|
|
}
|
|
|
|
int
|
|
Repository_init(Repository *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
char *path;
|
|
int err;
|
|
|
|
if (kwds && PyDict_Size(kwds) > 0) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"Repository takes no keyword arguments");
|
|
return -1;
|
|
}
|
|
|
|
if (!PyArg_ParseTuple(args, "s", &path))
|
|
return -1;
|
|
|
|
err = git_repository_open(&self->repo, path);
|
|
if (err < 0) {
|
|
Error_set_str(err, path);
|
|
return -1;
|
|
}
|
|
|
|
self->owned = 1;
|
|
self->config = NULL;
|
|
self->index = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository__from_c__doc__, "Init a Repository from a pointer. For internal use only.");
|
|
PyObject *
|
|
Repository__from_c(Repository *py_repo, PyObject *args)
|
|
{
|
|
PyObject *py_pointer, *py_free;
|
|
char *buffer;
|
|
Py_ssize_t len;
|
|
int err;
|
|
|
|
py_repo->repo = NULL;
|
|
py_repo->config = NULL;
|
|
py_repo->index = NULL;
|
|
|
|
if (!PyArg_ParseTuple(args, "OO!", &py_pointer, &PyBool_Type, &py_free))
|
|
return NULL;
|
|
|
|
err = PyBytes_AsStringAndSize(py_pointer, &buffer, &len);
|
|
if (err < 0)
|
|
return NULL;
|
|
|
|
if (len != sizeof(git_repository *)) {
|
|
PyErr_SetString(PyExc_TypeError, "invalid pointer length");
|
|
return NULL;
|
|
}
|
|
|
|
py_repo->repo = *((git_repository **) buffer);
|
|
py_repo->owned = py_free == Py_True;
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository__disown__doc__, "Mark the object as not-owned by us. For internal use only.");
|
|
PyObject *
|
|
Repository__disown(Repository *py_repo)
|
|
{
|
|
py_repo->owned = 0;
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
void
|
|
Repository_dealloc(Repository *self)
|
|
{
|
|
PyObject_GC_UnTrack(self);
|
|
Py_CLEAR(self->index);
|
|
Py_CLEAR(self->config);
|
|
|
|
if (self->owned)
|
|
git_repository_free(self->repo);
|
|
|
|
Py_TYPE(self)->tp_free(self);
|
|
}
|
|
|
|
int
|
|
Repository_traverse(Repository *self, visitproc visit, void *arg)
|
|
{
|
|
Py_VISIT(self->index);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Repository_clear(Repository *self)
|
|
{
|
|
Py_CLEAR(self->index);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
Repository_build_as_iter(const git_oid *oid, void *accum)
|
|
{
|
|
int err;
|
|
PyObject *py_oid = git_oid_to_python(oid);
|
|
|
|
err = PyList_Append((PyObject*)accum, py_oid);
|
|
Py_DECREF(py_oid);
|
|
return err;
|
|
}
|
|
|
|
PyObject *
|
|
Repository_as_iter(Repository *self)
|
|
{
|
|
git_odb *odb;
|
|
int err;
|
|
PyObject *accum = PyList_New(0);
|
|
PyObject *ret;
|
|
|
|
err = git_repository_odb(&odb, self->repo);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
err = git_odb_foreach(odb, Repository_build_as_iter, (void*)accum);
|
|
git_odb_free(odb);
|
|
if (err == GIT_EUSER)
|
|
return NULL;
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
ret = PyObject_GetIter(accum);
|
|
Py_DECREF(accum);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_head__doc__,
|
|
"Current head reference of the repository.");
|
|
|
|
PyObject *
|
|
Repository_head__get__(Repository *self)
|
|
{
|
|
git_reference *head;
|
|
int err;
|
|
|
|
err = git_repository_head(&head, self->repo);
|
|
if (err < 0) {
|
|
if (err == GIT_ENOTFOUND)
|
|
PyErr_SetString(GitError, "head reference does not exist");
|
|
else
|
|
Error_set(err);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return wrap_reference(head, self);
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_head_is_detached__doc__,
|
|
"A repository's HEAD is detached when it points directly to a commit\n"
|
|
"instead of a branch.");
|
|
|
|
PyObject *
|
|
Repository_head_is_detached__get__(Repository *self)
|
|
{
|
|
if (git_repository_head_detached(self->repo) > 0)
|
|
Py_RETURN_TRUE;
|
|
|
|
Py_RETURN_FALSE;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_head_is_unborn__doc__,
|
|
"An unborn branch is one named from HEAD but which doesn't exist in the\n"
|
|
"refs namespace, because it doesn't have any commit to point to.");
|
|
|
|
PyObject *
|
|
Repository_head_is_unborn__get__(Repository *self)
|
|
{
|
|
if (git_repository_head_unborn(self->repo) > 0)
|
|
Py_RETURN_TRUE;
|
|
|
|
Py_RETURN_FALSE;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_is_empty__doc__,
|
|
"Check if a repository is empty.");
|
|
|
|
PyObject *
|
|
Repository_is_empty__get__(Repository *self)
|
|
{
|
|
if (git_repository_is_empty(self->repo) > 0)
|
|
Py_RETURN_TRUE;
|
|
|
|
Py_RETURN_FALSE;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_is_bare__doc__,
|
|
"Check if a repository is a bare repository.");
|
|
|
|
PyObject *
|
|
Repository_is_bare__get__(Repository *self)
|
|
{
|
|
if (git_repository_is_bare(self->repo) > 0)
|
|
Py_RETURN_TRUE;
|
|
|
|
Py_RETURN_FALSE;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_git_object_lookup_prefix__doc__,
|
|
"git_object_lookup_prefix(oid) -> Object\n"
|
|
"\n"
|
|
"Returns the Git object with the given oid.");
|
|
|
|
PyObject *
|
|
Repository_git_object_lookup_prefix(Repository *self, PyObject *key)
|
|
{
|
|
int err;
|
|
size_t len;
|
|
git_oid oid;
|
|
git_object *obj;
|
|
|
|
len = py_oid_to_git_oid(key, &oid);
|
|
if (len == 0)
|
|
return NULL;
|
|
|
|
err = git_object_lookup_prefix(&obj, self->repo, &oid, len, GIT_OBJ_ANY);
|
|
if (err == 0)
|
|
return wrap_object(obj, self);
|
|
|
|
if (err == GIT_ENOTFOUND)
|
|
Py_RETURN_NONE;
|
|
|
|
return Error_set_oid(err, &oid, len);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_lookup_branch__doc__,
|
|
"lookup_branch(branch_name, [branch_type]) -> Branch\n"
|
|
"\n"
|
|
"Returns the Git reference for the given branch name (local or remote).\n"
|
|
"If branch_type is GIT_BRANCH_REMOTE, you must include the remote name\n"
|
|
"in the branch name (eg 'origin/master').");
|
|
|
|
PyObject *
|
|
Repository_lookup_branch(Repository *self, PyObject *args)
|
|
{
|
|
git_reference *c_reference;
|
|
const char *c_name;
|
|
git_branch_t branch_type = GIT_BRANCH_LOCAL;
|
|
int err;
|
|
|
|
if (!PyArg_ParseTuple(args, "s|I", &c_name, &branch_type))
|
|
return NULL;
|
|
|
|
err = git_branch_lookup(&c_reference, self->repo, c_name, branch_type);
|
|
if (err == 0)
|
|
return wrap_branch(c_reference, self);
|
|
|
|
if (err == GIT_ENOTFOUND)
|
|
Py_RETURN_NONE;
|
|
|
|
return Error_set(err);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_path_is_ignored__doc__,
|
|
"Check if a path is ignored in the repository.");
|
|
|
|
PyObject *
|
|
Repository_path_is_ignored(Repository *self, PyObject *args)
|
|
{
|
|
int ignored;
|
|
char *path;
|
|
|
|
if (!PyArg_ParseTuple(args, "s", &path))
|
|
return NULL;
|
|
|
|
git_ignore_path_is_ignored(&ignored, self->repo, path);
|
|
if (ignored == 1)
|
|
Py_RETURN_TRUE;
|
|
|
|
Py_RETURN_FALSE;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_revparse_single__doc__,
|
|
"revparse_single(revision) -> Object\n"
|
|
"\n"
|
|
"Find an object, as specified by a revision string. See\n"
|
|
"`man gitrevisions`, or the documentation for `git rev-parse` for\n"
|
|
"information on the syntax accepted.");
|
|
|
|
PyObject *
|
|
Repository_revparse_single(Repository *self, PyObject *py_spec)
|
|
{
|
|
git_object *c_obj;
|
|
const char *c_spec;
|
|
PyObject *tspec;
|
|
int err;
|
|
|
|
/* 1- Get the C revision spec */
|
|
c_spec = py_str_borrow_c_str(&tspec, py_spec, NULL);
|
|
if (c_spec == NULL)
|
|
return NULL;
|
|
|
|
/* 2- Lookup */
|
|
err = git_revparse_single(&c_obj, self->repo, c_spec);
|
|
|
|
if (err < 0) {
|
|
PyObject *err_obj = Error_set_str(err, c_spec);
|
|
Py_DECREF(tspec);
|
|
return err_obj;
|
|
}
|
|
Py_DECREF(tspec);
|
|
|
|
return wrap_object(c_obj, self);
|
|
}
|
|
|
|
git_odb_object *
|
|
Repository_read_raw(git_repository *repo, const git_oid *oid, size_t len)
|
|
{
|
|
git_odb *odb;
|
|
git_odb_object *obj;
|
|
int err;
|
|
|
|
err = git_repository_odb(&odb, repo);
|
|
if (err < 0) {
|
|
Error_set(err);
|
|
return NULL;
|
|
}
|
|
|
|
err = git_odb_read_prefix(&obj, odb, oid, (unsigned int)len);
|
|
git_odb_free(odb);
|
|
if (err < 0) {
|
|
Error_set_oid(err, oid, len);
|
|
return NULL;
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_read__doc__,
|
|
"read(oid) -> type, data, size\n"
|
|
"\n"
|
|
"Read raw object data from the repository.");
|
|
|
|
PyObject *
|
|
Repository_read(Repository *self, PyObject *py_hex)
|
|
{
|
|
git_oid oid;
|
|
git_odb_object *obj;
|
|
size_t len;
|
|
PyObject* tuple;
|
|
|
|
len = py_oid_to_git_oid(py_hex, &oid);
|
|
if (len == 0)
|
|
return NULL;
|
|
|
|
obj = Repository_read_raw(self->repo, &oid, len);
|
|
if (obj == NULL)
|
|
return NULL;
|
|
|
|
tuple = Py_BuildValue(
|
|
#if PY_MAJOR_VERSION == 2
|
|
"(ns#)",
|
|
#else
|
|
"(ny#)",
|
|
#endif
|
|
git_odb_object_type(obj),
|
|
git_odb_object_data(obj),
|
|
git_odb_object_size(obj));
|
|
|
|
git_odb_object_free(obj);
|
|
return tuple;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_write__doc__,
|
|
"write(type, data) -> Oid\n"
|
|
"\n"
|
|
"Write raw object data into the repository. First arg is the object\n"
|
|
"type, the second one a buffer with data. Return the Oid of the created\n"
|
|
"object.");
|
|
|
|
PyObject *
|
|
Repository_write(Repository *self, PyObject *args)
|
|
{
|
|
int err;
|
|
git_oid oid;
|
|
git_odb *odb;
|
|
git_odb_stream* stream;
|
|
int type_id;
|
|
const char* buffer;
|
|
Py_ssize_t buflen;
|
|
git_otype type;
|
|
|
|
if (!PyArg_ParseTuple(args, "Is#", &type_id, &buffer, &buflen))
|
|
return NULL;
|
|
|
|
type = int_to_loose_object_type(type_id);
|
|
if (type == GIT_OBJ_BAD)
|
|
return PyErr_Format(PyExc_ValueError, "%d", type_id);
|
|
|
|
err = git_repository_odb(&odb, self->repo);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
err = git_odb_open_wstream(&stream, odb, buflen, type);
|
|
git_odb_free(odb);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
err = git_odb_stream_write(stream, buffer, buflen);
|
|
if (err) {
|
|
git_odb_stream_free(stream);
|
|
return Error_set(err);
|
|
}
|
|
|
|
err = git_odb_stream_finalize_write(&oid, stream);
|
|
git_odb_stream_free(stream);
|
|
if (err)
|
|
return Error_set(err);
|
|
|
|
return git_oid_to_python(&oid);
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_path__doc__,
|
|
"The normalized path to the git repository.");
|
|
|
|
PyObject *
|
|
Repository_path__get__(Repository *self, void *closure)
|
|
{
|
|
return to_path(git_repository_path(self->repo));
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_workdir__doc__,
|
|
"The normalized path to the working directory of the repository. If the\n"
|
|
"repository is bare, None will be returned.");
|
|
|
|
PyObject *
|
|
Repository_workdir__get__(Repository *self, void *closure)
|
|
{
|
|
const char *c_path;
|
|
|
|
c_path = git_repository_workdir(self->repo);
|
|
if (c_path == NULL)
|
|
Py_RETURN_NONE;
|
|
|
|
return to_path(c_path);
|
|
}
|
|
|
|
int
|
|
Repository_workdir__set__(Repository *self, PyObject *py_workdir)
|
|
{
|
|
int err;
|
|
const char *workdir;
|
|
PyObject *tworkdir;
|
|
|
|
workdir = py_str_borrow_c_str(&tworkdir, py_workdir, NULL);
|
|
if (workdir == NULL)
|
|
return -1;
|
|
|
|
err = git_repository_set_workdir(self->repo, workdir, 0 /* update_gitlink */);
|
|
Py_DECREF(tworkdir);
|
|
if (err < 0) {
|
|
Error_set_str(err, workdir);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_merge_base__doc__,
|
|
"merge_base(oid, oid) -> Oid\n"
|
|
"\n"
|
|
"Find as good common ancestors as possible for a merge.\n"
|
|
"Returns None if there is no merge base between the commits");
|
|
|
|
PyObject *
|
|
Repository_merge_base(Repository *self, PyObject *args)
|
|
{
|
|
PyObject *value1;
|
|
PyObject *value2;
|
|
git_oid oid;
|
|
git_oid oid1;
|
|
git_oid oid2;
|
|
int err;
|
|
|
|
if (!PyArg_ParseTuple(args, "OO", &value1, &value2))
|
|
return NULL;
|
|
|
|
err = py_oid_to_git_oid_expand(self->repo, value1, &oid1);
|
|
if (err < 0)
|
|
return NULL;
|
|
|
|
err = py_oid_to_git_oid_expand(self->repo, value2, &oid2);
|
|
if (err < 0)
|
|
return NULL;
|
|
|
|
err = git_merge_base(&oid, self->repo, &oid1, &oid2);
|
|
|
|
if (err == GIT_ENOTFOUND)
|
|
Py_RETURN_NONE;
|
|
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
return git_oid_to_python(&oid);
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_merge_analysis__doc__,
|
|
"merge_analysis(id) -> (Integer, Integer)\n"
|
|
"\n"
|
|
"Analyzes the given branch and determines the opportunities for merging\n"
|
|
"them into the HEAD of the repository\n"
|
|
"\n"
|
|
"The first returned value is a mixture of the GIT_MERGE_ANALYSIS_NONE, _NORMAL,\n"
|
|
"_UP_TO_DATE, _FASTFORWARD and _UNBORN flags.\n"
|
|
"The second value is the user's preference from 'merge.ff'");
|
|
|
|
PyObject *
|
|
Repository_merge_analysis(Repository *self, PyObject *py_id)
|
|
{
|
|
int err;
|
|
size_t len;
|
|
git_oid id;
|
|
git_annotated_commit *commit;
|
|
git_merge_analysis_t analysis;
|
|
git_merge_preference_t preference;
|
|
|
|
len = py_oid_to_git_oid(py_id, &id);
|
|
if (len == 0)
|
|
return NULL;
|
|
|
|
err = git_annotated_commit_lookup(&commit, self->repo, &id);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
err = git_merge_analysis(&analysis, &preference, self->repo, (const git_annotated_commit **) &commit, 1);
|
|
git_annotated_commit_free(commit);
|
|
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
return Py_BuildValue("(ii)", analysis, preference);
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_merge__doc__,
|
|
"merge(id)\n"
|
|
"\n"
|
|
"Merges the given id into HEAD.\n"
|
|
"\n"
|
|
"Merges the given commit(s) into HEAD, writing the results into the\n"
|
|
"working directory. Any changes are staged for commit and any conflicts\n"
|
|
"are written to the index. Callers should inspect the repository's\n"
|
|
"index after this completes, resolve any conflicts and prepare a\n"
|
|
"commit.");
|
|
|
|
PyObject *
|
|
Repository_merge(Repository *self, PyObject *py_oid)
|
|
{
|
|
git_annotated_commit *commit;
|
|
git_oid oid;
|
|
int err;
|
|
size_t len;
|
|
git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
|
|
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
|
|
|
|
len = py_oid_to_git_oid(py_oid, &oid);
|
|
if (len == 0)
|
|
return NULL;
|
|
|
|
err = git_annotated_commit_lookup(&commit, self->repo, &oid);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
|
|
err = git_merge(self->repo,
|
|
(const git_annotated_commit **)&commit, 1,
|
|
&merge_opts, &checkout_opts);
|
|
|
|
git_annotated_commit_free(commit);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_cherrypick__doc__,
|
|
"cherrypick(id)\n"
|
|
"\n"
|
|
"Cherry-pick the given oid, producing changes in the index and working directory.\n"
|
|
"\n"
|
|
"Merges the given commit into HEAD as a cherrypick, writing the results into the\n"
|
|
"working directory. Any changes are staged for commit and any conflicts\n"
|
|
"are written to the index. Callers should inspect the repository's\n"
|
|
"index after this completes, resolve any conflicts and prepare a\n"
|
|
"commit.");
|
|
|
|
PyObject *
|
|
Repository_cherrypick(Repository *self, PyObject *py_oid)
|
|
{
|
|
git_commit *commit;
|
|
git_oid oid;
|
|
int err;
|
|
size_t len;
|
|
git_cherrypick_options cherrypick_opts = GIT_CHERRYPICK_OPTIONS_INIT;
|
|
|
|
len = py_oid_to_git_oid(py_oid, &oid);
|
|
if (len == 0)
|
|
return NULL;
|
|
|
|
err = git_commit_lookup(&commit, self->repo, &oid);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
cherrypick_opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
|
|
err = git_cherrypick(self->repo,
|
|
commit,
|
|
(const git_cherrypick_options *)&cherrypick_opts);
|
|
|
|
git_commit_free(commit);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_walk__doc__,
|
|
"walk(oid[, sort_mode]) -> iterator\n"
|
|
"\n"
|
|
"Generator that traverses the history starting from the given commit.\n"
|
|
"The following types of sorting could be used to control traversing\n"
|
|
"direction:\n"
|
|
"\n"
|
|
"* GIT_SORT_NONE. This is the default sorting for new walkers.\n"
|
|
" Sort the repository contents in no particular ordering\n"
|
|
"* GIT_SORT_TOPOLOGICAL. Sort the repository contents in topological order\n"
|
|
" (parents before children); this sorting mode can be combined with\n"
|
|
" time sorting.\n"
|
|
"* GIT_SORT_TIME. Sort the repository contents by commit time\n"
|
|
"* GIT_SORT_REVERSE. Iterate through the repository contents in reverse\n"
|
|
" order; this sorting mode can be combined with any of the above.\n"
|
|
"\n"
|
|
"Example:\n"
|
|
"\n"
|
|
" >>> from pygit2 import Repository\n"
|
|
" >>> from pygit2 import GIT_SORT_TOPOLOGICAL, GIT_SORT_REVERSE\n"
|
|
" >>> repo = Repository('.git')\n"
|
|
" >>> for commit in repo.walk(repo.head.target, GIT_SORT_TOPOLOGICAL):\n"
|
|
" ... print commit.message\n"
|
|
" >>> for commit in repo.walk(repo.head.target, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE):\n"
|
|
" ... print commit.message\n"
|
|
" >>>\n");
|
|
|
|
PyObject *
|
|
Repository_walk(Repository *self, PyObject *args)
|
|
{
|
|
PyObject *value;
|
|
unsigned int sort = GIT_SORT_NONE;
|
|
int err;
|
|
git_oid oid;
|
|
git_revwalk *walk;
|
|
Walker *py_walker;
|
|
|
|
if (!PyArg_ParseTuple(args, "O|I", &value, &sort))
|
|
return NULL;
|
|
|
|
err = git_revwalk_new(&walk, self->repo);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
/* Sort */
|
|
git_revwalk_sorting(walk, sort);
|
|
|
|
/* Push */
|
|
if (value != Py_None) {
|
|
err = py_oid_to_git_oid_expand(self->repo, value, &oid);
|
|
if (err < 0) {
|
|
git_revwalk_free(walk);
|
|
return NULL;
|
|
}
|
|
|
|
err = git_revwalk_push(walk, &oid);
|
|
if (err < 0) {
|
|
git_revwalk_free(walk);
|
|
return Error_set(err);
|
|
}
|
|
}
|
|
|
|
py_walker = PyObject_New(Walker, &WalkerType);
|
|
if (!py_walker) {
|
|
git_revwalk_free(walk);
|
|
return NULL;
|
|
}
|
|
|
|
Py_INCREF(self);
|
|
py_walker->repo = self;
|
|
py_walker->walk = walk;
|
|
return (PyObject*)py_walker;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_create_blob__doc__,
|
|
"create_blob(data) -> Oid\n"
|
|
"\n"
|
|
"Create a new blob from a bytes string. The blob is added to the Git\n"
|
|
"object database. Returns the oid of the blob.");
|
|
|
|
PyObject *
|
|
Repository_create_blob(Repository *self, PyObject *args)
|
|
{
|
|
git_oid oid;
|
|
const char *raw;
|
|
Py_ssize_t size;
|
|
int err;
|
|
|
|
if (!PyArg_ParseTuple(args, "s#", &raw, &size))
|
|
return NULL;
|
|
|
|
err = git_blob_create_frombuffer(&oid, self->repo, (const void*)raw, size);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
return git_oid_to_python(&oid);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_create_blob_fromworkdir__doc__,
|
|
"create_blob_fromworkdir(path) -> Oid\n"
|
|
"\n"
|
|
"Create a new blob from a file within the working directory. The given\n"
|
|
"path must be relative to the working directory, if it is not an error\n"
|
|
"is raised.");
|
|
|
|
PyObject *
|
|
Repository_create_blob_fromworkdir(Repository *self, PyObject *args)
|
|
{
|
|
git_oid oid;
|
|
const char* path;
|
|
int err;
|
|
|
|
if (!PyArg_ParseTuple(args, "s", &path))
|
|
return NULL;
|
|
|
|
err = git_blob_create_fromworkdir(&oid, self->repo, path);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
return git_oid_to_python(&oid);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_create_blob_fromdisk__doc__,
|
|
"create_blob_fromdisk(path) -> Oid\n"
|
|
"\n"
|
|
"Create a new blob from a file anywhere (no working directory check).");
|
|
|
|
PyObject *
|
|
Repository_create_blob_fromdisk(Repository *self, PyObject *args)
|
|
{
|
|
git_oid oid;
|
|
const char* path;
|
|
int err;
|
|
|
|
if (!PyArg_ParseTuple(args, "s", &path))
|
|
return NULL;
|
|
|
|
err = git_blob_create_fromdisk(&oid, self->repo, path);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
return git_oid_to_python(&oid);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_create_blob_fromiobase__doc__,
|
|
"create_blob_fromiobase(io.IOBase) -> Oid\n"
|
|
"\n"
|
|
"Create a new blob from an IOBase object.");
|
|
|
|
|
|
int read_chunk(char *content, size_t max_length, void *payload)
|
|
{
|
|
PyObject *py_file;
|
|
PyObject *py_bytes;
|
|
char *bytes;
|
|
Py_ssize_t size;
|
|
|
|
py_file = (PyObject *)payload;
|
|
py_bytes = PyObject_CallMethod(py_file, "read", "i", max_length);
|
|
if (!py_bytes)
|
|
return -1;
|
|
|
|
size = 0;
|
|
if (py_bytes != Py_None) {
|
|
bytes = PyBytes_AsString(py_bytes);
|
|
size = PyBytes_Size(py_bytes);
|
|
memcpy(content, bytes, size);
|
|
}
|
|
|
|
Py_DECREF(py_bytes);
|
|
return size;
|
|
}
|
|
|
|
PyObject *
|
|
Repository_create_blob_fromiobase(Repository *self, PyObject *py_file)
|
|
{
|
|
git_oid oid;
|
|
PyObject *py_is_readable;
|
|
int is_readable;
|
|
int err;
|
|
|
|
py_is_readable = PyObject_CallMethod(py_file, "readable", NULL);
|
|
if (!py_is_readable) {
|
|
if (PyErr_ExceptionMatches(PyExc_AttributeError))
|
|
PyErr_SetObject(PyExc_TypeError, py_file);
|
|
return NULL;
|
|
}
|
|
|
|
is_readable = PyObject_IsTrue(py_is_readable);
|
|
Py_DECREF(py_is_readable);
|
|
|
|
if (!is_readable) {
|
|
Py_DECREF(py_file);
|
|
PyErr_SetString(PyExc_TypeError, "expected readable IO type");
|
|
return NULL;
|
|
}
|
|
|
|
err = git_blob_create_fromchunks(&oid, self->repo, NULL, &read_chunk,
|
|
py_file);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
return git_oid_to_python(&oid);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_create_commit__doc__,
|
|
"create_commit(reference_name, author, committer, message, tree, parents[, encoding]) -> Oid\n"
|
|
"\n"
|
|
"Create a new commit object, return its oid.");
|
|
|
|
PyObject *
|
|
Repository_create_commit(Repository *self, PyObject *args)
|
|
{
|
|
Signature *py_author, *py_committer;
|
|
PyObject *py_oid, *py_message, *py_parents, *py_parent;
|
|
PyObject *py_result = NULL;
|
|
PyObject *tmessage;
|
|
const char *message = NULL;
|
|
char *update_ref = NULL;
|
|
char *encoding = NULL;
|
|
git_oid oid;
|
|
git_tree *tree = NULL;
|
|
int parent_count;
|
|
git_commit **parents = NULL;
|
|
int err = 0, i = 0;
|
|
size_t len;
|
|
|
|
if (!PyArg_ParseTuple(args, "zO!O!OOO!|s",
|
|
&update_ref,
|
|
&SignatureType, &py_author,
|
|
&SignatureType, &py_committer,
|
|
&py_message,
|
|
&py_oid,
|
|
&PyList_Type, &py_parents,
|
|
&encoding))
|
|
return NULL;
|
|
|
|
len = py_oid_to_git_oid(py_oid, &oid);
|
|
if (len == 0)
|
|
return NULL;
|
|
|
|
message = py_str_borrow_c_str(&tmessage, py_message, encoding);
|
|
if (message == NULL)
|
|
return NULL;
|
|
|
|
err = git_tree_lookup_prefix(&tree, self->repo, &oid, len);
|
|
if (err < 0) {
|
|
Error_set(err);
|
|
goto out;
|
|
}
|
|
|
|
parent_count = (int)PyList_Size(py_parents);
|
|
parents = malloc(parent_count * sizeof(git_commit*));
|
|
if (parents == NULL) {
|
|
PyErr_SetNone(PyExc_MemoryError);
|
|
goto out;
|
|
}
|
|
for (; i < parent_count; i++) {
|
|
py_parent = PyList_GET_ITEM(py_parents, i);
|
|
len = py_oid_to_git_oid(py_parent, &oid);
|
|
if (len == 0)
|
|
goto out;
|
|
err = git_commit_lookup_prefix(&parents[i], self->repo, &oid, len);
|
|
if (err < 0) {
|
|
Error_set(err);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
err = git_commit_create(&oid, self->repo, update_ref,
|
|
py_author->signature, py_committer->signature,
|
|
encoding, message, tree, parent_count,
|
|
(const git_commit**)parents);
|
|
if (err < 0) {
|
|
Error_set(err);
|
|
goto out;
|
|
}
|
|
|
|
py_result = git_oid_to_python(&oid);
|
|
|
|
out:
|
|
Py_DECREF(tmessage);
|
|
git_tree_free(tree);
|
|
while (i > 0) {
|
|
i--;
|
|
git_commit_free(parents[i]);
|
|
}
|
|
free(parents);
|
|
return py_result;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_create_tag__doc__,
|
|
"create_tag(name, oid, type, tagger, message) -> Oid\n"
|
|
"\n"
|
|
"Create a new tag object, return its oid.");
|
|
|
|
PyObject *
|
|
Repository_create_tag(Repository *self, PyObject *args)
|
|
{
|
|
PyObject *py_oid;
|
|
Signature *py_tagger;
|
|
char *tag_name, *message;
|
|
git_oid oid;
|
|
git_object *target = NULL;
|
|
int err, target_type;
|
|
size_t len;
|
|
|
|
if (!PyArg_ParseTuple(args, "sOiO!s",
|
|
&tag_name,
|
|
&py_oid,
|
|
&target_type,
|
|
&SignatureType, &py_tagger,
|
|
&message))
|
|
return NULL;
|
|
|
|
len = py_oid_to_git_oid(py_oid, &oid);
|
|
if (len == 0)
|
|
return NULL;
|
|
|
|
err = git_object_lookup_prefix(&target, self->repo, &oid, len,
|
|
target_type);
|
|
err = err < 0 ? err : git_tag_create(&oid, self->repo, tag_name, target,
|
|
py_tagger->signature, message, 0);
|
|
git_object_free(target);
|
|
if (err < 0)
|
|
return Error_set_oid(err, &oid, len);
|
|
return git_oid_to_python(&oid);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_create_branch__doc__,
|
|
"create_branch(name, commit, force=False) -> Branch\n"
|
|
"\n"
|
|
"Create a new branch \"name\" which points to a commit.\n"
|
|
"\n"
|
|
"Arguments:\n"
|
|
"\n"
|
|
"force\n"
|
|
" If True branches will be overridden, otherwise (the default) an\n"
|
|
" exception is raised.\n"
|
|
"\n"
|
|
"Examples::\n"
|
|
"\n"
|
|
" repo.create_branch('foo', repo.head.get_object(), force=False)");
|
|
|
|
PyObject *
|
|
Repository_create_branch(Repository *self, PyObject *args)
|
|
{
|
|
Commit *py_commit;
|
|
git_reference *c_reference;
|
|
char *c_name;
|
|
int err, force = 0;
|
|
|
|
if (!PyArg_ParseTuple(args, "sO!|i", &c_name, &CommitType, &py_commit, &force))
|
|
return NULL;
|
|
|
|
err = git_branch_create(&c_reference, self->repo, c_name, py_commit->commit, force);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
return wrap_branch(c_reference, self);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_listall_references__doc__,
|
|
"listall_references() -> [str, ...]\n"
|
|
"\n"
|
|
"Return a list with all the references in the repository.");
|
|
|
|
PyObject *
|
|
Repository_listall_references(Repository *self, PyObject *args)
|
|
{
|
|
git_strarray c_result;
|
|
PyObject *py_result, *py_string;
|
|
unsigned index;
|
|
int err;
|
|
|
|
/* Get the C result */
|
|
err = git_reference_list(&c_result, self->repo);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
/* Create a new PyTuple */
|
|
py_result = PyList_New(c_result.count);
|
|
if (py_result == NULL)
|
|
goto out;
|
|
|
|
/* Fill it */
|
|
for (index=0; index < c_result.count; index++) {
|
|
py_string = to_path(c_result.strings[index]);
|
|
if (py_string == NULL) {
|
|
Py_CLEAR(py_result);
|
|
goto out;
|
|
}
|
|
PyList_SET_ITEM(py_result, index, py_string);
|
|
}
|
|
|
|
out:
|
|
git_strarray_free(&c_result);
|
|
return py_result;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_listall_branches__doc__,
|
|
"listall_branches([flag]) -> [str, ...]\n"
|
|
"\n"
|
|
"Return a list with all the branches in the repository.\n"
|
|
"\n"
|
|
"The *flag* may be:\n"
|
|
"\n"
|
|
"- GIT_BRANCH_LOCAL - return all local branches (set by default)\n"
|
|
"- GIT_BRANCH_REMOTE - return all remote-tracking branches\n"
|
|
"- GIT_BRANCH_ALL - return local branches and remote-tracking branches");
|
|
|
|
PyObject *
|
|
Repository_listall_branches(Repository *self, PyObject *args)
|
|
{
|
|
git_branch_t list_flags = GIT_BRANCH_LOCAL;
|
|
git_branch_iterator *iter;
|
|
git_reference *ref = NULL;
|
|
int err;
|
|
git_branch_t type;
|
|
PyObject *list;
|
|
|
|
/* 1- Get list_flags */
|
|
if (!PyArg_ParseTuple(args, "|I", &list_flags))
|
|
return NULL;
|
|
|
|
list = PyList_New(0);
|
|
if (list == NULL)
|
|
return NULL;
|
|
|
|
if ((err = git_branch_iterator_new(&iter, self->repo, list_flags)) < 0)
|
|
return Error_set(err);
|
|
|
|
while ((err = git_branch_next(&ref, &type, iter)) == 0) {
|
|
PyObject *py_branch_name = to_path(git_reference_shorthand(ref));
|
|
git_reference_free(ref);
|
|
|
|
if (py_branch_name == NULL)
|
|
goto error;
|
|
|
|
err = PyList_Append(list, py_branch_name);
|
|
Py_DECREF(py_branch_name);
|
|
|
|
if (err < 0)
|
|
goto error;
|
|
}
|
|
|
|
git_branch_iterator_free(iter);
|
|
if (err == GIT_ITEROVER)
|
|
err = 0;
|
|
|
|
if (err < 0) {
|
|
Py_CLEAR(list);
|
|
return Error_set(err);
|
|
}
|
|
|
|
return list;
|
|
|
|
error:
|
|
git_branch_iterator_free(iter);
|
|
Py_CLEAR(list);
|
|
return NULL;
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_listall_submodules__doc__,
|
|
"listall_submodules() -> [str, ...]\n"
|
|
"\n"
|
|
"Return a list with all submodule paths in the repository.\n");
|
|
|
|
static int foreach_path_cb(git_submodule *submodule, const char *name, void *payload)
|
|
{
|
|
PyObject *list = (PyObject *)payload;
|
|
PyObject *path = to_unicode(git_submodule_path(submodule), NULL, NULL);
|
|
|
|
return PyList_Append(list, path);
|
|
}
|
|
|
|
PyObject *
|
|
Repository_listall_submodules(Repository *self, PyObject *args)
|
|
{
|
|
int err;
|
|
PyObject *list;
|
|
|
|
list = PyList_New(0);
|
|
if (list == NULL)
|
|
return NULL;
|
|
|
|
err = git_submodule_foreach(self->repo, foreach_path_cb, list);
|
|
if (err != 0) {
|
|
Py_DECREF(list);
|
|
return Py_None;
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_lookup_reference__doc__,
|
|
"lookup_reference(name) -> Reference\n"
|
|
"\n"
|
|
"Lookup a reference by its name in a repository.");
|
|
|
|
PyObject *
|
|
Repository_lookup_reference(Repository *self, PyObject *py_name)
|
|
{
|
|
git_reference *c_reference;
|
|
char *c_name;
|
|
int err;
|
|
|
|
/* 1- Get the C name */
|
|
c_name = py_path_to_c_str(py_name);
|
|
if (c_name == NULL)
|
|
return NULL;
|
|
|
|
/* 2- Lookup */
|
|
err = git_reference_lookup(&c_reference, self->repo, c_name);
|
|
if (err < 0) {
|
|
PyObject *err_obj = Error_set_str(err, c_name);
|
|
free(c_name);
|
|
return err_obj;
|
|
}
|
|
free(c_name);
|
|
|
|
/* 3- Make an instance of Reference and return it */
|
|
return wrap_reference(c_reference, self);
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_create_reference_direct__doc__,
|
|
"create_reference_direct(name, target, force) -> Reference\n"
|
|
"\n"
|
|
"Create a new reference \"name\" which points to an object.\n"
|
|
"\n"
|
|
"Arguments:\n"
|
|
"\n"
|
|
"force\n"
|
|
" If True references will be overridden, otherwise (the default) an\n"
|
|
" exception is raised.\n"
|
|
"\n"
|
|
"Examples::\n"
|
|
"\n"
|
|
" repo.create_reference_direct('refs/heads/foo', repo.head.target, False)");
|
|
|
|
PyObject *
|
|
Repository_create_reference_direct(Repository *self, PyObject *args,
|
|
PyObject *kw)
|
|
{
|
|
PyObject *py_obj;
|
|
git_reference *c_reference;
|
|
char *c_name;
|
|
git_oid oid;
|
|
int err, force;
|
|
|
|
if (!PyArg_ParseTuple(args, "sOi", &c_name, &py_obj, &force))
|
|
return NULL;
|
|
|
|
err = py_oid_to_git_oid_expand(self->repo, py_obj, &oid);
|
|
if (err < 0)
|
|
return NULL;
|
|
|
|
err = git_reference_create(&c_reference, self->repo, c_name, &oid, force, NULL);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
return wrap_reference(c_reference, self);
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_create_reference_symbolic__doc__,
|
|
"create_reference_symbolic(name, source, force) -> Reference\n"
|
|
"\n"
|
|
"Create a new reference \"name\" which points to another reference.\n"
|
|
"\n"
|
|
"Arguments:\n"
|
|
"\n"
|
|
"force\n"
|
|
" If True references will be overridden, otherwise (the default) an\n"
|
|
" exception is raised.\n"
|
|
"\n"
|
|
"Examples::\n"
|
|
"\n"
|
|
" repo.create_reference_symbolic('refs/tags/foo', 'refs/heads/master', False)");
|
|
|
|
PyObject *
|
|
Repository_create_reference_symbolic(Repository *self, PyObject *args,
|
|
PyObject *kw)
|
|
{
|
|
git_reference *c_reference;
|
|
char *c_name, *c_target;
|
|
int err, force;
|
|
|
|
if (!PyArg_ParseTuple(args, "ssi", &c_name, &c_target, &force))
|
|
return NULL;
|
|
|
|
err = git_reference_symbolic_create(&c_reference, self->repo, c_name,
|
|
c_target, force, NULL);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
return wrap_reference(c_reference, self);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_status__doc__,
|
|
"status() -> {str: int}\n"
|
|
"\n"
|
|
"Reads the status of the repository and returns a dictionary with file\n"
|
|
"paths as keys and status flags as values. See pygit2.GIT_STATUS_*.");
|
|
|
|
PyObject *
|
|
Repository_status(Repository *self)
|
|
{
|
|
PyObject *dict;
|
|
int err;
|
|
size_t len, i;
|
|
git_status_list *list;
|
|
|
|
dict = PyDict_New();
|
|
if (dict == NULL)
|
|
return NULL;
|
|
|
|
err = git_status_list_new(&list, self->repo, NULL);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
len = git_status_list_entrycount(list);
|
|
for (i = 0; i < len; i++) {
|
|
const git_status_entry *entry;
|
|
const char *path;
|
|
PyObject *status;
|
|
|
|
entry = git_status_byindex(list, i);
|
|
if (entry == NULL)
|
|
goto error;
|
|
|
|
/* We need to choose one of the strings */
|
|
if (entry->head_to_index)
|
|
path = entry->head_to_index->old_file.path;
|
|
else
|
|
path = entry->index_to_workdir->old_file.path;
|
|
status = PyLong_FromLong((long) entry->status);
|
|
|
|
err = PyDict_SetItemString(dict, path, status);
|
|
Py_CLEAR(status);
|
|
|
|
if (err < 0)
|
|
goto error;
|
|
|
|
}
|
|
|
|
git_status_list_free(list);
|
|
return dict;
|
|
|
|
error:
|
|
git_status_list_free(list);
|
|
Py_CLEAR(dict);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_status_file__doc__,
|
|
"status_file(path) -> int\n"
|
|
"\n"
|
|
"Returns the status of the given file path.");
|
|
|
|
PyObject *
|
|
Repository_status_file(Repository *self, PyObject *value)
|
|
{
|
|
char *path;
|
|
unsigned int status;
|
|
int err;
|
|
|
|
path = py_path_to_c_str(value);
|
|
if (!path)
|
|
return NULL;
|
|
|
|
err = git_status_file(&status, self->repo, path);
|
|
if (err < 0) {
|
|
PyObject *err_obj = Error_set_str(err, path);
|
|
free(path);
|
|
return err_obj;
|
|
}
|
|
return PyLong_FromLong(status);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_TreeBuilder__doc__,
|
|
"TreeBuilder([tree]) -> TreeBuilder\n"
|
|
"\n"
|
|
"Create a TreeBuilder object for this repository.");
|
|
|
|
PyObject *
|
|
Repository_TreeBuilder(Repository *self, PyObject *args)
|
|
{
|
|
TreeBuilder *builder;
|
|
git_treebuilder *bld;
|
|
PyObject *py_src = NULL;
|
|
git_oid oid;
|
|
git_tree *tree = NULL;
|
|
git_tree *must_free = NULL;
|
|
int err;
|
|
|
|
if (!PyArg_ParseTuple(args, "|O", &py_src))
|
|
return NULL;
|
|
|
|
if (py_src) {
|
|
if (PyObject_TypeCheck(py_src, &TreeType)) {
|
|
Tree *py_tree = (Tree *)py_src;
|
|
if (py_tree->repo->repo != self->repo) {
|
|
/* return Error_set(GIT_EINVALIDARGS); */
|
|
return Error_set(GIT_ERROR);
|
|
}
|
|
tree = py_tree->tree;
|
|
} else {
|
|
err = py_oid_to_git_oid_expand(self->repo, py_src, &oid);
|
|
if (err < 0)
|
|
return NULL;
|
|
|
|
err = git_tree_lookup(&tree, self->repo, &oid);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
must_free = tree;
|
|
}
|
|
}
|
|
|
|
err = git_treebuilder_new(&bld, self->repo, tree);
|
|
if (must_free != NULL)
|
|
git_tree_free(must_free);
|
|
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
builder = PyObject_New(TreeBuilder, &TreeBuilderType);
|
|
if (builder) {
|
|
builder->repo = self;
|
|
builder->bld = bld;
|
|
Py_INCREF(self);
|
|
}
|
|
|
|
return (PyObject*)builder;
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_default_signature__doc__, "Return the signature according to the repository's configuration");
|
|
|
|
PyObject *
|
|
Repository_default_signature__get__(Repository *self)
|
|
{
|
|
git_signature *sig;
|
|
int err;
|
|
|
|
if ((err = git_signature_default(&sig, self->repo)) < 0)
|
|
return Error_set(err);
|
|
|
|
return build_signature(NULL, sig, "utf-8");
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository__pointer__doc__, "Get the repo's pointer. For internal use only.");
|
|
PyObject *
|
|
Repository__pointer__get__(Repository *self)
|
|
{
|
|
/* Bytes means a raw buffer */
|
|
return PyBytes_FromStringAndSize((char *) &self->repo, sizeof(git_repository *));
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_notes__doc__, "");
|
|
|
|
PyObject *
|
|
Repository_notes(Repository *self, PyObject *args)
|
|
{
|
|
NoteIter *iter = NULL;
|
|
char *ref = "refs/notes/commits";
|
|
int err = GIT_ERROR;
|
|
|
|
if (!PyArg_ParseTuple(args, "|s", &ref))
|
|
return NULL;
|
|
|
|
iter = PyObject_New(NoteIter, &NoteIterType);
|
|
if (iter != NULL) {
|
|
iter->repo = self;
|
|
iter->ref = ref;
|
|
|
|
err = git_note_iterator_new(&iter->iter, self->repo, iter->ref);
|
|
if (err == GIT_OK) {
|
|
Py_INCREF(self);
|
|
return (PyObject*)iter;
|
|
}
|
|
}
|
|
|
|
return Error_set(err);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_create_note__doc__,
|
|
"create_note(message, author, committer, annotated_id [,ref, force]) -> Oid\n"
|
|
"\n"
|
|
"Create a new note for an object, return its SHA-ID."
|
|
"If no ref is given 'refs/notes/commits' will be used.");
|
|
|
|
PyObject *
|
|
Repository_create_note(Repository *self, PyObject* args)
|
|
{
|
|
git_oid note_id, annotated_id;
|
|
char *annotated = NULL, *message = NULL, *ref = "refs/notes/commits";
|
|
int err = GIT_ERROR;
|
|
unsigned int force = 0;
|
|
Signature *py_author, *py_committer;
|
|
|
|
if (!PyArg_ParseTuple(args, "sO!O!s|si",
|
|
&message,
|
|
&SignatureType, &py_author,
|
|
&SignatureType, &py_committer,
|
|
&annotated, &ref, &force))
|
|
return NULL;
|
|
|
|
err = git_oid_fromstr(&annotated_id, annotated);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
err = git_note_create(¬e_id, self->repo, ref, py_author->signature,
|
|
py_committer->signature,
|
|
&annotated_id, message, force);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
return git_oid_to_python(¬e_id);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(Repository_lookup_note__doc__,
|
|
"lookup_note(annotated_id [, ref]) -> Note\n"
|
|
"\n"
|
|
"Lookup a note for an annotated object in a repository.");
|
|
|
|
PyObject *
|
|
Repository_lookup_note(Repository *self, PyObject* args)
|
|
{
|
|
git_oid annotated_id;
|
|
char* annotated = NULL, *ref = "refs/notes/commits";
|
|
int err;
|
|
|
|
if (!PyArg_ParseTuple(args, "s|s", &annotated, &ref))
|
|
return NULL;
|
|
|
|
err = git_oid_fromstr(&annotated_id, annotated);
|
|
if (err < 0)
|
|
return Error_set(err);
|
|
|
|
return (PyObject*) wrap_note(self, &annotated_id, ref);
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_reset__doc__,
|
|
"reset(oid, reset_type)\n"
|
|
"\n"
|
|
"Resets current head to the provided oid.\n"
|
|
"reset_type:\n"
|
|
"GIT_RESET_SOFT: resets head to point to oid, but does not modfy working copy, and leaves the changes in the index.\n"
|
|
"GIT_RESET_MIXED: resets head to point to oid, but does not modfy working copy. It empties the index too.\n"
|
|
"GIT_RESET_HARD: resets head to point to oid, and resets too the working copy and the content of the index.\n");
|
|
|
|
PyObject *
|
|
Repository_reset(Repository *self, PyObject* args)
|
|
{
|
|
PyObject *py_oid;
|
|
git_oid oid;
|
|
git_object *target = NULL;
|
|
int err, reset_type;
|
|
size_t len;
|
|
|
|
if (!PyArg_ParseTuple(args, "Oi",
|
|
&py_oid,
|
|
&reset_type
|
|
))
|
|
return NULL;
|
|
|
|
len = py_oid_to_git_oid(py_oid, &oid);
|
|
if (len == 0)
|
|
return NULL;
|
|
|
|
err = git_object_lookup_prefix(&target, self->repo, &oid, len,
|
|
GIT_OBJ_ANY);
|
|
err = err < 0 ? err : git_reset(self->repo, target, reset_type, NULL);
|
|
git_object_free(target);
|
|
if (err < 0)
|
|
return Error_set_oid(err, &oid, len);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PyDoc_STRVAR(Repository_expand_id__doc__,
|
|
"expand_id(hex) -> Oid\n"
|
|
"\n"
|
|
"Expand a string into a full Oid according to the objects in this repsitory.\n");
|
|
|
|
PyObject *
|
|
Repository_expand_id(Repository *self, PyObject *py_hex)
|
|
{
|
|
git_oid oid;
|
|
int err;
|
|
|
|
err = py_oid_to_git_oid_expand(self->repo, py_hex, &oid);
|
|
if (err < 0)
|
|
return NULL;
|
|
|
|
return git_oid_to_python(&oid);
|
|
}
|
|
|
|
PyMethodDef Repository_methods[] = {
|
|
METHOD(Repository, create_blob, METH_VARARGS),
|
|
METHOD(Repository, create_blob_fromworkdir, METH_VARARGS),
|
|
METHOD(Repository, create_blob_fromdisk, METH_VARARGS),
|
|
METHOD(Repository, create_blob_fromiobase, METH_O),
|
|
METHOD(Repository, create_commit, METH_VARARGS),
|
|
METHOD(Repository, create_tag, METH_VARARGS),
|
|
METHOD(Repository, TreeBuilder, METH_VARARGS),
|
|
METHOD(Repository, walk, METH_VARARGS),
|
|
METHOD(Repository, merge_base, METH_VARARGS),
|
|
METHOD(Repository, merge_analysis, METH_O),
|
|
METHOD(Repository, merge, METH_O),
|
|
METHOD(Repository, cherrypick, METH_O),
|
|
METHOD(Repository, read, METH_O),
|
|
METHOD(Repository, write, METH_VARARGS),
|
|
METHOD(Repository, create_reference_direct, METH_VARARGS),
|
|
METHOD(Repository, create_reference_symbolic, METH_VARARGS),
|
|
METHOD(Repository, listall_references, METH_NOARGS),
|
|
METHOD(Repository, listall_submodules, METH_NOARGS),
|
|
METHOD(Repository, lookup_reference, METH_O),
|
|
METHOD(Repository, revparse_single, METH_O),
|
|
METHOD(Repository, status, METH_NOARGS),
|
|
METHOD(Repository, status_file, METH_O),
|
|
METHOD(Repository, notes, METH_VARARGS),
|
|
METHOD(Repository, create_note, METH_VARARGS),
|
|
METHOD(Repository, lookup_note, METH_VARARGS),
|
|
METHOD(Repository, git_object_lookup_prefix, METH_O),
|
|
METHOD(Repository, lookup_branch, METH_VARARGS),
|
|
METHOD(Repository, path_is_ignored, METH_VARARGS),
|
|
METHOD(Repository, listall_branches, METH_VARARGS),
|
|
METHOD(Repository, create_branch, METH_VARARGS),
|
|
METHOD(Repository, reset, METH_VARARGS),
|
|
METHOD(Repository, expand_id, METH_O),
|
|
METHOD(Repository, _from_c, METH_VARARGS),
|
|
METHOD(Repository, _disown, METH_NOARGS),
|
|
{NULL}
|
|
};
|
|
|
|
PyGetSetDef Repository_getseters[] = {
|
|
GETTER(Repository, path),
|
|
GETTER(Repository, head),
|
|
GETTER(Repository, head_is_detached),
|
|
GETTER(Repository, head_is_unborn),
|
|
GETTER(Repository, is_empty),
|
|
GETTER(Repository, is_bare),
|
|
GETSET(Repository, workdir),
|
|
GETTER(Repository, default_signature),
|
|
GETTER(Repository, _pointer),
|
|
{NULL}
|
|
};
|
|
|
|
|
|
PyDoc_STRVAR(Repository__doc__,
|
|
"Repository(path) -> Repository\n"
|
|
"\n"
|
|
"Git repository.");
|
|
|
|
PyTypeObject RepositoryType = {
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
"_pygit2.Repository", /* tp_name */
|
|
sizeof(Repository), /* tp_basicsize */
|
|
0, /* tp_itemsize */
|
|
(destructor)Repository_dealloc, /* tp_dealloc */
|
|
0, /* tp_print */
|
|
0, /* tp_getattr */
|
|
0, /* tp_setattr */
|
|
0, /* tp_compare */
|
|
0, /* tp_repr */
|
|
0, /* tp_as_number */
|
|
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 |
|
|
Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
|
Repository__doc__, /* tp_doc */
|
|
(traverseproc)Repository_traverse, /* tp_traverse */
|
|
(inquiry)Repository_clear, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
(getiterfunc)Repository_as_iter, /* 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 */
|
|
};
|