diff --git a/pygit2/__init__.py b/pygit2/__init__.py index b4faa03..aab855b 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -38,6 +38,7 @@ from .settings import Settings from .credentials import * from .remote import Remote, get_credentials from .config import Config +from .index import Index, IndexEntry from .errors import check_error from .ffi import ffi, C, to_str diff --git a/pygit2/decl.h b/pygit2/decl.h index 7068e94..b1ac828 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -3,9 +3,10 @@ typedef ... git_remote; typedef ... git_refspec; typedef ... git_push; typedef ... git_cred; -typedef ... git_diff_file; typedef ... git_tree; typedef ... git_signature; +typedef ... git_index; +typedef ... git_diff; #define GIT_OID_RAWSZ ... #define GIT_PATH_MAX ... @@ -25,6 +26,7 @@ typedef struct git_strarray { size_t count; } git_strarray; +typedef int64_t git_off_t; typedef enum { GIT_OK = 0, @@ -181,6 +183,76 @@ int git_cred_ssh_key_new( const char *privatekey, const char *passphrase); +/* + * git_diff + */ + +typedef enum { + GIT_SUBMODULE_IGNORE_RESET = -1, + + GIT_SUBMODULE_IGNORE_NONE = 1, + GIT_SUBMODULE_IGNORE_UNTRACKED = 2, + GIT_SUBMODULE_IGNORE_DIRTY = 3, + GIT_SUBMODULE_IGNORE_ALL = 4, + GIT_SUBMODULE_IGNORE_DEFAULT = 0 +} git_submodule_ignore_t; + +typedef enum { + GIT_DELTA_UNMODIFIED = 0, + GIT_DELTA_ADDED = 1, + GIT_DELTA_DELETED = 2, + GIT_DELTA_MODIFIED = 3, + GIT_DELTA_RENAMED = 4, + GIT_DELTA_COPIED = 5, + GIT_DELTA_IGNORED = 6, + GIT_DELTA_UNTRACKED = 7, + GIT_DELTA_TYPECHANGE = 8, +} git_delta_t; + +typedef struct { + git_oid id; + const char *path; + git_off_t size; + uint32_t flags; + uint16_t mode; +} git_diff_file; + +typedef struct { + git_delta_t status; + uint32_t flags; + uint16_t similarity; + uint16_t nfiles; + git_diff_file old_file; + git_diff_file new_file; +} git_diff_delta; + +typedef int (*git_diff_notify_cb)( + const git_diff *diff_so_far, + const git_diff_delta *delta_to_add, + const char *matched_pathspec, + void *payload); + +typedef struct { + unsigned int version; + uint32_t flags; + + git_submodule_ignore_t ignore_submodules; + git_strarray pathspec; + git_diff_notify_cb notify_cb; + void *notify_payload; + + uint16_t context_lines; + uint16_t interhunk_lines; + uint16_t id_abbrev; + git_off_t max_size; + const char *old_prefix; + const char *new_prefix; +} git_diff_options; + +int git_diff_init_options(git_diff_options *opts, unsigned int version); +int git_diff_index_to_workdir(git_diff **diff, git_repository *repo, git_index *index, const git_diff_options *opts); +int git_diff_tree_to_index(git_diff **diff, git_repository *repo, git_tree *old_tree, git_index *index, const git_diff_options *opts); + /* * git_checkout */ @@ -369,3 +441,54 @@ int git_repository_init_ext( git_repository **out, const char *repo_path, git_repository_init_options *opts); + +/* + * git_index + */ +typedef int64_t git_time_t; + +typedef struct { + git_time_t seconds; + unsigned int nanoseconds; +} git_index_time; + +typedef struct git_index_entry { + git_index_time ctime; + git_index_time mtime; + + unsigned int dev; + unsigned int ino; + unsigned int mode; + unsigned int uid; + unsigned int gid; + git_off_t file_size; + + git_oid id; + + unsigned short flags; + unsigned short flags_extended; + + const char *path; +} git_index_entry; + +typedef int (*git_index_matched_path_cb)( + const char *path, const char *matched_pathspec, void *payload); + +void git_index_free(git_index *index); +int git_repository_index(git_index **out, git_repository *repo); +int git_index_open(git_index **out, const char *index_path); +int git_index_read(git_index *index, int force); +int git_index_write(git_index *index); +size_t git_index_entrycount(const git_index *index); +int git_index_find(size_t *at_pos, git_index *index, const char *path); +int git_index_add_bypath(git_index *index, const char *path); +int git_index_add(git_index *index, const git_index_entry *source_entry); +int git_index_remove(git_index *index, const char *path, int stage); +int git_index_read_tree(git_index *index, const git_tree *tree); +int git_index_clear(git_index *index); +int git_index_write_tree(git_oid *out, git_index *index); +int git_index_write_tree_to(git_oid *out, git_index *index, git_repository *repo); +const git_index_entry *git_index_get_bypath(git_index *index, const char *path, int stage); +const git_index_entry *git_index_get_byindex(git_index *index, size_t n); +int git_index_add_all(git_index *index, const git_strarray *pathspec, unsigned int flags, + git_index_matched_path_cb callback, void *payload); diff --git a/pygit2/index.py b/pygit2/index.py new file mode 100644 index 0000000..c41b4e0 --- /dev/null +++ b/pygit2/index.py @@ -0,0 +1,326 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2010-2014 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. + +# Import from the future +from __future__ import absolute_import, unicode_literals + +from _pygit2 import Oid, Tree, Diff + +from .ffi import ffi, C, to_str, is_string, strings_to_strarray +from .errors import check_error, GitError + +class Index(object): + + def __init__(self, path=None): + """Create a new Index + + If path is supplied, the read and write methods will use that path + to read from and write to. + """ + cindex = ffi.new('git_index **') + err = C.git_index_open(cindex, to_str(path)) + check_error(err) + + self._index = cindex[0] + self._cindex = cindex + + @classmethod + def from_c(cls, repo, ptr): + index = cls.__new__(cls); + index._repo = repo + index._index = ptr[0] + index._cindex = ptr + + return index + + @property + def _pointer(self): + return bytes(ffi.buffer(self._cindex)[:]) + + def __del__(self): + C.git_index_free(self._index) + + def __len__(self): + return C.git_index_entrycount(self._index) + + def __contains__(self, path): + err = C.git_index_find(ffi.NULL, self._index, to_str(path)) + if err == C.GIT_ENOTFOUND: + return False + + check_error(err) + return True + + def __getitem__(self, key): + centry = ffi.NULL + if is_string(key): + centry = C.git_index_get_bypath(self._index, to_str(key), 0) + elif not key >= 0: + raise ValueError(key) + else: + centry = C.git_index_get_byindex(self._index, key) + + if centry == ffi.NULL: + raise KeyError(key) + + return IndexEntry._from_c(centry) + + def __iter__(self): + return IndexIterator(self) + + def read(self, force=True): + """Update the contents the Index + + Update the contents by reading from a file + + Arguments: + + force: if True (the default) allways reload. If False, only if the file has changed + """ + + err = C.git_index_read(self._index, force) + check_error(err, True) + + def write(self): + """Write the contents of the Index to disk + """ + err = C.git_index_write(self._index) + check_error(err, True) + + def clear(self): + err = C.git_index_clear(self._index) + check_error(err) + + def read_tree(self, tree): + """Replace the contents of the Index with those of a tree + + The tree will be read recursively and all its children will also be + inserted into the Index. + """ + if is_string(tree): + tree = self._repo[tree] + + if isinstance(tree, Oid): + if not hasattr(self, '_repo'): + raise TypeError("id given but no associated repository") + + tree = self._repo[tree] + elif not isinstance(tree, Tree): + raise TypeError("argument must be Oid or Tree") + + tree_cptr = ffi.new('git_tree **') + ffi.buffer(tree_cptr)[:] = tree._pointer[:] + err = C.git_index_read_tree(self._index, tree_cptr[0]) + check_error(err) + + def write_tree(self, repo=None): + """Create a tree out of the Index + + The contents of the index will be written out to the object + database. If there is no associated repository, 'repo' must be + passed. If there is an associated repository and 'repo' is + passed, then that repository will be used instead. + + It returns the id of the resulting tree. + """ + coid = ffi.new('git_oid *') + if repo: + err = C.git_index_write_tree_to(coid, self._index, repo._repo) + else: + err = C.git_index_write_tree(coid, self._index) + + check_error(err) + return Oid(raw=bytes(ffi.buffer(coid)[:])) + + + def remove(self, path): + """Remove an entry from the Index. + """ + err = C.git_index_remove(self._index, to_str(path), 0) + check_error(err, True) + + def add_all(self, pathspecs=[]): + """Add or update index entries matching files in the working directory. + + If pathspecs are specified, only files matching those pathspecs will be added. + """ + arr, refs = strings_to_strarray(pathspecs) + err = C.git_index_add_all(self._index, arr, 0, ffi.NULL, ffi.NULL) + check_error(err, True) + + def add(self, path_or_entry): + """Add or update an entry in the Index + + If a path is given, that file will be added. The path must be + relative to the root of the worktree and the Index must be associated + with a repository. + + If an IndexEntry is given, that entry will be added or update in the Index + without checking for the existence of the path or id. + """ + + if is_string(path_or_entry): + path = path_or_entry + err = C.git_index_add_bypath(self._index, to_str(path)) + elif isinstance(path_or_entry, IndexEntry): + entry = path_or_entry + centry, str_ref = entry._to_c() + err = C.git_index_add(self._index, centry) + else: + raise AttributeError('argument must be string or IndexEntry') + + check_error(err, True) + + def diff_to_workdir(self, flags=0, context_lines=3, interhunk_lines=0): + """Diff the index against the working directory + + Return a :py:class:`~pygit2.Diff` object with the differences + between the index and the working copy. + + Arguments: + + flags: a GIT_DIFF_* constant. + + context_lines: the number of unchanged lines that define the + boundary of a hunk (and to display before and after) + + interhunk_lines: the maximum number of unchanged lines between hunk + boundaries before the hunks will be merged into a one + """ + if not hasattr(self, '_repo'): + raise ValueError('diff needs an associated repository') + + copts = ffi.new('git_diff_options *') + err = C.git_diff_init_options(copts, 1) + check_error(err) + + copts.flags = flags + copts.context_lines = context_lines + copts.interhunk_lines = interhunk_lines + + cdiff = ffi.new('git_diff **') + err = C.git_diff_index_to_workdir(cdiff, self._repo._repo, self._index, copts) + check_error(err) + + return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), self._repo) + + def diff_to_tree(self, tree, flags=0, context_lines=3, interhunk_lines=0): + """Diff the index against a tree + + Return a :py:class:`~pygit2.Diff` object with the differences between the + index and the given tree. + + Arguments: + + tree: the tree to diff. + + flags: a GIT_DIFF_* constant. + + context_lines: the number of unchanged lines that define the boundary + of a hunk (and to display before and after) + + interhunk_lines: the maximum number of unchanged lines between hunk + boundaries before the hunks will be merged into a one. + """ + + if not hasattr(self, '_repo'): + raise ValueError('diff needs an associated repository') + + if not isinstance(tree, Tree): + raise TypeError('tree must be a Tree') + + copts = ffi.new('git_diff_options *') + err = C.git_diff_init_options(copts, 1) + check_error(err) + + copts.flags = flags + copts.context_lines = context_lines + copts.interhunk_lines = interhunk_lines + + ctree = ffi.new('git_tree **') + ffi.buffer(ctree)[:] = tree._pointer[:] + + cdiff = ffi.new('git_diff **') + err = C.git_diff_tree_to_index(cdiff, self._repo._repo, ctree[0], self._index, copts) + check_error(err) + + return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), self._repo) + +class IndexEntry(object): + __slots__ = ['id', 'path', 'mode', '_index'] + + def __init__(self, path, object_id, mode): + self.path = path + self.id = object_id + self.mode = mode + + @property + def hex(self): + return self.id.hex + + def _to_c(self): + """Convert this entry into the C structure + + The first returned arg is the pointer, the second is the reference to + the string we allocated, which we need to exist past this function + """ + centry = ffi.new('git_index_entry *') + # basically memcpy() + ffi.buffer(ffi.addressof(centry, 'id'))[:] = self.id.raw[:] + centry.mode = self.mode + path = ffi.new('char[]', to_str(self.path)) + centry.path = path + + return centry, path + + @classmethod + def _from_c(cls, centry): + entry = cls.__new__(cls) + entry.path = ffi.string(centry.path).decode() + entry.mode = centry.mode + entry.id = Oid(raw=bytes(ffi.buffer(ffi.addressof(centry, 'id'))[:])) + + return entry + +class IndexIterator(object): + + def __init__(self, index): + self.index = index + self.n = 0 + self.max = len(index) + + def next(self): + return self.__next__() + + def __next__(self): + if self.n >= self.max: + raise StopIteration + + entry = self.index[self.n] + self.n += 1 + + return entry diff --git a/pygit2/repository.py b/pygit2/repository.py index 1dff878..1be5bbb 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -39,6 +39,7 @@ from .ffi import ffi, C, to_str from .errors import check_error from .remote import Remote from .config import Config +from .index import Index class Repository(_Repository): @@ -307,3 +308,16 @@ class Repository(_Repository): etc. """ C.git_repository_state_cleanup(self._repo) + + # + # Index + # + @property + def index(self): + """Index representing the repository's index file + """ + cindex = ffi.new('git_index **') + err = C.git_repository_index(cindex, self._repo) + check_error(err, True) + + return Index.from_c(self, cindex) diff --git a/src/diff.c b/src/diff.c index 3aeddeb..2554cfc 100644 --- a/src/diff.c +++ b/src/diff.c @@ -39,6 +39,7 @@ extern PyTypeObject TreeType; extern PyTypeObject IndexType; extern PyTypeObject DiffType; extern PyTypeObject HunkType; +extern PyTypeObject RepositoryType; PyTypeObject PatchType; @@ -383,6 +384,33 @@ PyTypeObject HunkType = { 0, /* tp_new */ }; +PyDoc_STRVAR(Diff_from_c__doc__, "Method exposed for Index to hook into"); + +PyObject * +Diff_from_c(Diff *dummy, PyObject *args) +{ + PyObject *py_diff, *py_repository; + git_diff *diff; + char *buffer; + Py_ssize_t length; + + if (!PyArg_ParseTuple(args, "OO!", &py_diff, &RepositoryType, &py_repository)) + return NULL; + + /* Here we need to do the opposite conversion from the _pointer getters */ + if (PyBytes_AsStringAndSize(py_diff, &buffer, &length)) + return NULL; + + if (length != sizeof(git_diff *)) { + PyErr_SetString(PyExc_TypeError, "passed value is not a pointer"); + return NULL; + } + + /* the "buffer" contains the pointer */ + diff = *((git_diff **) buffer); + + return wrap_diff(diff, (Repository *) py_repository); +} PyDoc_STRVAR(Diff_merge__doc__, "merge(diff)\n" @@ -481,6 +509,7 @@ PyMappingMethods Diff_as_mapping = { static PyMethodDef Diff_methods[] = { METHOD(Diff, merge, METH_VARARGS), METHOD(Diff, find_similar, METH_VARARGS), + METHOD(Diff, from_c, METH_STATIC), {NULL} }; diff --git a/src/index.c b/src/index.c deleted file mode 100644 index 714c308..0000000 --- a/src/index.c +++ /dev/null @@ -1,787 +0,0 @@ -/* - * Copyright 2010-2014 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 -#include "error.h" -#include "types.h" -#include "utils.h" -#include "oid.h" -#include "diff.h" -#include "index.h" - -extern PyTypeObject IndexType; -extern PyTypeObject TreeType; -extern PyTypeObject DiffType; -extern PyTypeObject IndexIterType; -extern PyTypeObject IndexEntryType; -extern PyTypeObject OidType; -extern PyTypeObject RepositoryType; - -int -Index_init(Index *self, PyObject *args, PyObject *kwds) -{ - char *path = NULL; - int err; - - if (kwds && PyDict_Size(kwds) > 0) { - PyErr_SetString(PyExc_TypeError, "Index takes no keyword arguments"); - return -1; - } - - if (!PyArg_ParseTuple(args, "|s", &path)) - return -1; - - self->repo = NULL; - err = git_index_open(&self->index, path); - if (err < 0) { - Error_set_str(err, path); - return -1; - } - - return 0; -} - -void -Index_dealloc(Index* self) -{ - PyObject_GC_UnTrack(self); - Py_XDECREF(self->repo); - git_index_free(self->index); - Py_TYPE(self)->tp_free(self); -} - -int -Index_traverse(Index *self, visitproc visit, void *arg) -{ - Py_VISIT(self->repo); - return 0; -} - -PyDoc_STRVAR(Index_add__doc__, - "add([path|entry])\n" - "\n" - "Add or update an index entry from a file in disk."); - -PyObject * -Index_add(Index *self, PyObject *args) -{ - int err; - const char *path; - IndexEntry *py_entry; - - if (PyArg_ParseTuple(args, "O!", &IndexEntryType, &py_entry)) { - err = git_index_add(self->index, &py_entry->entry); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; - } - PyErr_Clear(); - - if (!PyArg_ParseTuple(args, "s", &path)) - return NULL; - - err = git_index_add_bypath(self->index, path); - if (err < 0) - return Error_set_str(err, path); - - Py_RETURN_NONE; -} - - -PyDoc_STRVAR(Index_add_all__doc__, - "add_all([file names|glob pattern])\n" - "\n" - "Add or update index entries matching files in the working directory."); - -PyObject * -Index_add_all(Index *self, PyObject *pylist) -{ - int err; - git_strarray pathspec; - - if (get_strarraygit_from_pylist(&pathspec, pylist) < 0) - return NULL; - - err = git_index_add_all(self->index, &pathspec, 0, NULL, NULL); - git_strarray_free(&pathspec); - - if (err < 0) { - Error_set(err); - return NULL; - } - - Py_RETURN_NONE; -} - -PyDoc_STRVAR(Index_clear__doc__, - "clear()\n" - "\n" - "Clear the contents (all the entries) of an index object."); - -PyObject * -Index_clear(Index *self) -{ - git_index_clear(self->index); - Py_RETURN_NONE; -} - - -PyDoc_STRVAR(Index_diff_to_workdir__doc__, - "diff_to_workdir([flag, context_lines, interhunk_lines]) -> Diff\n" - "\n" - "Return a :py:class:`~pygit2.Diff` object with the differences between the\n" - "index and the working copy.\n" - "\n" - "Arguments:\n" - "\n" - "flag: a GIT_DIFF_* constant.\n" - "\n" - "context_lines: the number of unchanged lines that define the boundary\n" - " of a hunk (and to display before and after)\n" - "\n" - "interhunk_lines: the maximum number of unchanged lines between hunk\n" - " boundaries before the hunks will be merged into a one.\n"); - -PyObject * -Index_diff_to_workdir(Index *self, PyObject *args) -{ - git_diff_options opts = GIT_DIFF_OPTIONS_INIT; - git_diff *diff; - int err; - - if (!PyArg_ParseTuple(args, "|IHH", &opts.flags, &opts.context_lines, - &opts.interhunk_lines)) - return NULL; - - err = git_diff_index_to_workdir( - &diff, - self->repo->repo, - self->index, - &opts); - - if (err < 0) - return Error_set(err); - - return wrap_diff(diff, self->repo); -} - -PyDoc_STRVAR(Index_diff_to_tree__doc__, - "diff_to_tree(tree [, flag, context_lines, interhunk_lines]) -> Diff\n" - "\n" - "Return a :py:class:`~pygit2.Diff` object with the differences between the\n" - "index and the given tree.\n" - "\n" - "Arguments:\n" - "\n" - "tree: the tree to diff.\n" - "\n" - "flag: a GIT_DIFF_* constant.\n" - "\n" - "context_lines: the number of unchanged lines that define the boundary\n" - " of a hunk (and to display before and after)\n" - "\n" - "interhunk_lines: the maximum number of unchanged lines between hunk\n" - " boundaries before the hunks will be merged into a one.\n"); - -PyObject * -Index_diff_to_tree(Index *self, PyObject *args) -{ - Repository *py_repo; - git_diff_options opts = GIT_DIFF_OPTIONS_INIT; - git_diff *diff; - int err; - - Tree *py_tree = NULL; - - if (!PyArg_ParseTuple(args, "O!|IHH", &TreeType, &py_tree, &opts.flags, - &opts.context_lines, &opts.interhunk_lines)) - return NULL; - - py_repo = py_tree->repo; - err = git_diff_tree_to_index(&diff, py_repo->repo, py_tree->tree, - self->index, &opts); - if (err < 0) - return Error_set(err); - - return wrap_diff(diff, py_repo); -} - - -PyDoc_STRVAR(Index__find__doc__, - "_find(path) -> integer\n" - "\n" - "Find the first index of any entries which point to given path in the\n" - "index file."); - -PyObject * -Index__find(Index *self, PyObject *py_path) -{ - char *path; - size_t idx; - int err; - - path = PyBytes_AsString(py_path); - if (!path) - return NULL; - - err = git_index_find(&idx, self->index, path); - if (err < 0) - return Error_set_str(err, path); - - return PyLong_FromSize_t(idx); -} - - -PyDoc_STRVAR(Index_read__doc__, - "read(force=True)\n" - "\n" - "Update the contents of an existing index object in memory by reading from\n" - "the hard disk." - "Arguments:\n" - "\n" - "force: if True (the default) allways reload. If False, only if the file has changed" -); - -PyObject * -Index_read(Index *self, PyObject *args) -{ - int err, force = 1; - - if (!PyArg_ParseTuple(args, "|i", &force)) - return NULL; - - err = git_index_read(self->index, force); - if (err < GIT_OK) - return Error_set(err); - - Py_RETURN_NONE; -} - - -PyDoc_STRVAR(Index_write__doc__, - "write()\n" - "\n" - "Write an existing index object from memory back to disk using an atomic\n" - "file lock."); - -PyObject * -Index_write(Index *self) -{ - int err; - - err = git_index_write(self->index); - if (err < GIT_OK) - return Error_set(err); - - Py_RETURN_NONE; -} - -int -Index_contains(Index *self, PyObject *value) -{ - char *path; - int err; - - path = py_path_to_c_str(value); - if (!path) - return -1; - err = git_index_find(NULL, self->index, path); - if (err == GIT_ENOTFOUND) { - free(path); - return 0; - } - if (err < 0) { - Error_set_str(err, path); - free(path); - return -1; - } - free(path); - return 1; -} - -PyObject * -Index_iter(Index *self) -{ - IndexIter *iter; - - iter = PyObject_New(IndexIter, &IndexIterType); - if (iter) { - Py_INCREF(self); - iter->owner = self; - iter->i = 0; - } - return (PyObject*)iter; -} - -Py_ssize_t -Index_len(Index *self) -{ - return (Py_ssize_t)git_index_entrycount(self->index); -} - -PyObject * -wrap_index_entry(const git_index_entry *entry, Index *index) -{ - IndexEntry *py_entry; - - py_entry = PyObject_New(IndexEntry, &IndexEntryType); - if (!py_entry) - return NULL; - - memcpy(&py_entry->entry, entry, sizeof(struct git_index_entry)); - py_entry->entry.path = strdup(entry->path); - if (!py_entry->entry.path) { - Py_CLEAR(py_entry); - return NULL; - } - - return (PyObject*)py_entry; -} - -PyObject * -Index_getitem(Index *self, PyObject *value) -{ - long idx; - char *path; - const git_index_entry *index_entry; - - /* Case 1: integer */ - if (PyLong_Check(value)) { - idx = PyLong_AsLong(value); - if (idx == -1 && PyErr_Occurred()) - return NULL; - if (idx < 0) { - PyErr_SetObject(PyExc_ValueError, value); - return NULL; - } - index_entry = git_index_get_byindex(self->index, (size_t)idx); - /* Case 2: byte or text string */ - } else { - path = py_path_to_c_str(value); - if (!path) - return NULL; - - index_entry = git_index_get_bypath(self->index, path, 0); - free(path); - } - - if (!index_entry) { - PyErr_SetObject(PyExc_KeyError, value); - return NULL; - } - return wrap_index_entry(index_entry, self); -} - - -PyDoc_STRVAR(Index_remove__doc__, - "remove(path)\n" - "\n" - "Removes an entry from index."); - -PyObject * -Index_remove(Index *self, PyObject *args) -{ - int err; - const char *path; - - if (!PyArg_ParseTuple(args, "s", &path)) - return NULL; - - err = git_index_remove(self->index, path, 0); - if (err < 0) { - Error_set(err); - return NULL; - } - - Py_RETURN_NONE; -} - - -PyDoc_STRVAR(Index_read_tree__doc__, - "read_tree(tree)\n" - "\n" - "Update the index file from the specified tree. The tree can be a Tree object or an Oid.\n" - "Using an Oid is only possible if this index is associated with a repository"); - -PyObject * -Index_read_tree(Index *self, PyObject *value) -{ - git_oid oid; - git_tree *tree = NULL; - int err, need_free = 0; - size_t len; - - len = py_oid_to_git_oid(value, &oid); - if (len == 0) { - Tree *py_tree; - if (!PyObject_TypeCheck(value, &TreeType)) { - return NULL; - } - - PyErr_Clear(); - py_tree = (Tree *) value; - tree = py_tree->tree; - } - - /* - * if the user passed in an id but we're not associated with a - * repo, we can't do anything - */ - if (tree == NULL && self->repo == NULL) { - PyErr_SetString(PyExc_TypeError, "id given but no associated repository"); - return NULL; - } else if (tree == NULL) { - need_free = 1; - err = git_tree_lookup_prefix(&tree, self->repo->repo, &oid, len); - if (err < 0) - return Error_set(err); - } - - err = git_index_read_tree(self->index, tree); - if (need_free) - git_tree_free(tree); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; -} - - -PyDoc_STRVAR(Index_write_tree__doc__, - "write_tree([repo]) -> Oid\n" - "\n" - "Create a tree object from the index file, return its oid.\n" - "If 'repo' is passed, write to that repository's odb."); - -PyObject * -Index_write_tree(Index *self, PyObject *args) -{ - git_oid oid; - Repository *repo = NULL; - int err; - - if (!PyArg_ParseTuple(args, "|O!", &RepositoryType, &repo)) - return NULL; - - if (repo) - err = git_index_write_tree_to(&oid, self->index, repo->repo); - else - err = git_index_write_tree(&oid, self->index); - - if (err < 0) - return Error_set(err); - - return git_oid_to_python(&oid); -} - -PyMethodDef Index_methods[] = { - METHOD(Index, add, METH_VARARGS), - METHOD(Index, add_all, METH_O), - METHOD(Index, remove, METH_VARARGS), - METHOD(Index, clear, METH_NOARGS), - METHOD(Index, diff_to_workdir, METH_VARARGS), - METHOD(Index, diff_to_tree, METH_VARARGS), - METHOD(Index, _find, METH_O), - METHOD(Index, read, METH_VARARGS), - METHOD(Index, write, METH_NOARGS), - METHOD(Index, read_tree, METH_O), - METHOD(Index, write_tree, METH_VARARGS), - {NULL} -}; - -PySequenceMethods Index_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)Index_contains, /* sq_contains */ -}; - -PyMappingMethods Index_as_mapping = { - (lenfunc)Index_len, /* mp_length */ - (binaryfunc)Index_getitem, /* mp_subscript */ - NULL, /* mp_ass_subscript */ -}; - -PyDoc_STRVAR(Index__doc__, "Index file."); - -PyTypeObject IndexType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_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 */ - &Index_as_sequence, /* tp_as_sequence */ - &Index_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 | - Py_TPFLAGS_HAVE_GC, /* tp_flags */ - Index__doc__, /* tp_doc */ - (traverseproc)Index_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)Index_iter, /* tp_iter */ - 0, /* tp_iternext */ - Index_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)Index_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - - -void -IndexIter_dealloc(IndexIter *self) -{ - Py_CLEAR(self->owner); - Py_TYPE(self)->tp_free(self); -} - -PyObject * -IndexIter_iternext(IndexIter *self) -{ - const git_index_entry *index_entry; - - index_entry = git_index_get_byindex(self->owner->index, self->i); - if (!index_entry) - return NULL; - - self->i += 1; - return wrap_index_entry(index_entry, self->owner); -} - - -PyDoc_STRVAR(IndexIter__doc__, "Index iterator."); - -PyTypeObject IndexIterType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.IndexIter", /* tp_name */ - sizeof(IndexIter), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)IndexIter_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 */ - IndexIter__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)IndexIter_iternext, /* tp_iternext */ -}; - -int -IndexEntry_init(IndexEntry *self, PyObject *args, PyObject *kwds) -{ - char *c_path = NULL; - Oid *id = NULL; - unsigned int mode; - char *keywords[] = {"path", "oid", "mode", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO!I", keywords, - &c_path, &OidType, &id, &mode)) - return -1; - - memset(&self->entry, 0, sizeof(struct git_index_entry)); - self->entry.path = strdup(c_path); - if (!self->entry.path) - return -1; - - if (id) - git_oid_cpy(&self->entry.id, &id->oid); - - if (mode) - self->entry.mode = mode; - - return 0; -} - -void -IndexEntry_dealloc(IndexEntry *self) -{ - free(self->entry.path); - PyObject_Del(self); -} - - -PyDoc_STRVAR(IndexEntry_mode__doc__, "Mode."); - -PyObject * -IndexEntry_mode__get__(IndexEntry *self) -{ - return PyLong_FromLong(self->entry.mode); -} - -int -IndexEntry_mode__set__(IndexEntry *self, PyObject *py_mode) -{ - long c_val; - - c_val = PyLong_AsLong(py_mode); - if (c_val == -1 && PyErr_Occurred()) - return -1; - - self->entry.mode = (unsigned int) c_val; - - return 0; -} - -PyDoc_STRVAR(IndexEntry_path__doc__, "Path."); - -PyObject * -IndexEntry_path__get__(IndexEntry *self) -{ - return to_path(self->entry.path); -} - -int -IndexEntry_path__set__(IndexEntry *self, PyObject *py_path) -{ - char *c_path; - - c_path = py_str_to_c_str(py_path, NULL); - if (!c_path) - return -1; - - free(self->entry.path); - self->entry.path = c_path; - - return 0; -} - -PyDoc_STRVAR(IndexEntry_id__doc__, "Object id."); - -PyObject * -IndexEntry_id__get__(IndexEntry *self) -{ - return git_oid_to_python(&self->entry.id); -} - -int -IndexEntry_id__set__(IndexEntry *self, PyObject *py_id) -{ - if (!py_oid_to_git_oid(py_id, &self->entry.id)) - return -1; - - return 0; -} - -PyDoc_STRVAR(IndexEntry_hex__doc__, "Hex id."); - -PyObject * -IndexEntry_hex__get__(IndexEntry *self) -{ - return git_oid_to_py_str(&self->entry.id); -} - -PyGetSetDef IndexEntry_getseters[] = { - GETSET(IndexEntry, mode), - GETSET(IndexEntry, path), - GETSET(IndexEntry, id), - GETTER(IndexEntry, hex), - {NULL}, -}; - -PyDoc_STRVAR(IndexEntry__doc__, "Index entry."); - -PyTypeObject IndexEntryType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.IndexEntry", /* tp_name */ - sizeof(IndexEntry), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)IndexEntry_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 */ - IndexEntry__doc__, /* 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 */ - IndexEntry_getseters, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)IndexEntry_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; diff --git a/src/index.h b/src/index.h deleted file mode 100644 index 44c150d..0000000 --- a/src/index.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2010-2014 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. - */ - -#ifndef INCLUDE_pygit2_index_h -#define INCLUDE_pygit2_index_h - -#define PY_SSIZE_T_CLEAN -#include -#include - -PyObject* Index_add(Index *self, PyObject *args); -PyObject* Index_add_all(Index *self, PyObject *pylist); -PyObject* Index_clear(Index *self); -PyObject* Index_find(Index *self, PyObject *py_path); -PyObject* Index_read(Index *self, PyObject *args); -PyObject* Index_write(Index *self); -PyObject* Index_iter(Index *self); -PyObject* Index_getitem(Index *self, PyObject *value); -PyObject* Index_read_tree(Index *self, PyObject *value); -PyObject* Index_write_tree(Index *self, PyObject *args); -Py_ssize_t Index_len(Index *self); -int Index_setitem(Index *self, PyObject *key, PyObject *value); - -#endif diff --git a/src/object.c b/src/object.c index f737e8b..e169a33 100644 --- a/src/object.c +++ b/src/object.c @@ -100,6 +100,14 @@ Object_type__get__(Object *self) return PyLong_FromLong(git_object_type(self->obj)); } +PyDoc_STRVAR(Object__pointer__doc__, "Get the object's pointer. For internal use only."); +PyObject * +Object__pointer__get__(Object *self) +{ + /* Bytes means a raw buffer */ + return PyBytes_FromStringAndSize((char *) &self->obj, sizeof(git_object *)); +} + PyDoc_STRVAR(Object_read_raw__doc__, "read_raw()\n" @@ -181,6 +189,7 @@ PyGetSetDef Object_getseters[] = { GETTER(Object, id), GETTER(Object, hex), GETTER(Object, type), + GETTER(Object, _pointer), {NULL} }; diff --git a/src/pygit2.c b/src/pygit2.c index ccf4a67..35f87b5 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -52,9 +52,6 @@ extern PyTypeObject TreeEntryType; extern PyTypeObject TreeIterType; extern PyTypeObject BlobType; extern PyTypeObject TagType; -extern PyTypeObject IndexType; -extern PyTypeObject IndexEntryType; -extern PyTypeObject IndexIterType; extern PyTypeObject WalkerType; extern PyTypeObject ReferenceType; extern PyTypeObject RefLogIterType; @@ -268,11 +265,6 @@ moduleinit(PyObject* m) /* * Index & Working copy */ - INIT_TYPE(IndexType, NULL, PyType_GenericNew) - INIT_TYPE(IndexEntryType, NULL, PyType_GenericNew) - INIT_TYPE(IndexIterType, NULL, NULL) - ADD_TYPE(m, Index) - ADD_TYPE(m, IndexEntry) /* Status */ ADD_CONSTANT_INT(m, GIT_STATUS_CURRENT) ADD_CONSTANT_INT(m, GIT_STATUS_INDEX_NEW) diff --git a/src/repository.c b/src/repository.c index bd048c7..2408164 100644 --- a/src/repository.c +++ b/src/repository.c @@ -461,41 +461,6 @@ Repository_write(Repository *self, PyObject *args) return git_oid_to_python(&oid); } - -PyDoc_STRVAR(Repository_index__doc__, "Index file."); - -PyObject * -Repository_index__get__(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 < 0) - return Error_set(err); - - py_index = PyObject_GC_New(Index, &IndexType); - if (!py_index) { - git_index_free(index); - return NULL; - } - - Py_INCREF(self); - py_index->repo = self; - py_index->index = index; - PyObject_GC_Track(py_index); - self->index = (PyObject*)py_index; - } - - Py_INCREF(self->index); - return self->index; -} - - PyDoc_STRVAR(Repository_path__doc__, "The normalized path to the git repository."); @@ -1621,7 +1586,6 @@ PyMethodDef Repository_methods[] = { }; PyGetSetDef Repository_getseters[] = { - GETTER(Repository, index), GETTER(Repository, path), GETSET(Repository, head), GETTER(Repository, head_is_detached), diff --git a/src/tree.c b/src/tree.c index b72d578..e8f7111 100644 --- a/src/tree.c +++ b/src/tree.c @@ -395,19 +395,45 @@ Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff *diff; + git_index *index; + char *buffer; + Py_ssize_t length; Repository *py_repo; + PyObject *py_idx, *py_idx_ptr; int err; - Index *py_idx = NULL; - - if (!PyArg_ParseTuple(args, "O!|IHH", &IndexType, &py_idx, &opts.flags, + if (!PyArg_ParseTuple(args, "O|IHH", &py_idx, &opts.flags, &opts.context_lines, &opts.interhunk_lines)) return NULL; + /* + * This is a hack to check whether we're passed an index, as I + * haven't found a good way to grab a type object for + * pygit2.index.Index. + */ + if (!PyObject_GetAttrString(py_idx, "_index")) { + PyErr_SetString(PyExc_TypeError, "argument must be an Index"); + return NULL; + } + py_idx_ptr = PyObject_GetAttrString(py_idx, "_pointer"); + if (!py_idx_ptr) + return NULL; + + /* Here we need to do the opposite conversion from the _pointer getters */ + if (PyBytes_AsStringAndSize(py_idx_ptr, &buffer, &length)) + return NULL; + + if (length != sizeof(git_index *)) { + PyErr_SetString(PyExc_TypeError, "passed value is not a pointer"); + return NULL; + } + + /* the "buffer" contains the pointer */ + index = *((git_index **) buffer); + py_repo = self->repo; - err = git_diff_tree_to_index(&diff, py_repo->repo, self->tree, - py_idx->index, &opts); + err = git_diff_tree_to_index(&diff, py_repo->repo, self->tree, index, &opts); if (err < 0) return Error_set(err);