diff --git a/docs/working-copy.rst b/docs/working-copy.rst index ab8c6fe23..76bcada87 100644 --- a/docs/working-copy.rst +++ b/docs/working-copy.rst @@ -22,6 +22,9 @@ Index write:: >>> del index['path/to/file'] # git rm >>> index.write() # don't forget to save the changes +Custom entries:: + >>> entry = pygit2.IndexEntry('README.md', blob_id, blob_filemode) + >>> repo.index.add(entry) The Index type ==================== diff --git a/src/index.c b/src/index.c index ce47f13a0..2149a70e2 100644 --- a/src/index.c +++ b/src/index.c @@ -39,6 +39,8 @@ 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) @@ -81,7 +83,7 @@ Index_traverse(Index *self, visitproc visit, void *arg) PyDoc_STRVAR(Index_add__doc__, - "add(path)\n" + "add([path|entry])\n" "\n" "Add or update an index entry from a file in disk."); @@ -90,6 +92,16 @@ 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; @@ -314,8 +326,15 @@ wrap_index_entry(const git_index_entry *entry, Index *index) IndexEntry *py_entry; py_entry = PyObject_New(IndexEntry, &IndexEntryType); - if (py_entry) - py_entry->entry = entry; + 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; } @@ -410,17 +429,26 @@ Index_read_tree(Index *self, PyObject *value) PyDoc_STRVAR(Index_write_tree__doc__, - "write_tree() -> Oid\n" + "write_tree([repo]) -> Oid\n" "\n" - "Create a tree object from the index file, return its oid."); + "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) +Index_write_tree(Index *self, PyObject *args) { git_oid oid; + Repository *repo = NULL; int err; - err = git_index_write_tree(&oid, self->index); + 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); @@ -437,7 +465,7 @@ PyMethodDef Index_methods[] = { METHOD(Index, read, METH_VARARGS), METHOD(Index, write, METH_NOARGS), METHOD(Index, read_tree, METH_O), - METHOD(Index, write_tree, METH_NOARGS), + METHOD(Index, write_tree, METH_VARARGS), {NULL} }; @@ -557,6 +585,31 @@ PyTypeObject IndexIterType = { (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)); + if (c_path) + self->entry.path = c_path; + + if (id) + git_oid_cpy(&self->entry.oid, &id->oid); + + if (mode) + self->entry.mode = mode; + + return 0; +} + void IndexEntry_dealloc(IndexEntry *self) { @@ -569,40 +622,81 @@ PyDoc_STRVAR(IndexEntry_mode__doc__, "Mode."); PyObject * IndexEntry_mode__get__(IndexEntry *self) { - return PyLong_FromLong(self->entry->mode); + 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); + return to_path(self->entry.path); } +int +IndexEntry_path__set__(IndexEntry *self, PyObject *py_path) +{ + char *c_inner, *c_path; + + c_inner = py_str_to_c_str(py_path, NULL); + if (!c_inner) + return -1; + + c_path = strdup(c_inner); + if (!c_path) { + PyErr_NoMemory(); + return -1; + } + + free(self->entry.path); + self->entry.path = c_path; + + return 0; +} PyDoc_STRVAR(IndexEntry_oid__doc__, "Object id."); PyObject * IndexEntry_oid__get__(IndexEntry *self) { - return git_oid_to_python(&self->entry->oid); + return git_oid_to_python(&self->entry.oid); } +int +IndexEntry_oid__set__(IndexEntry *self, PyObject *py_id) +{ + if (!py_oid_to_git_oid(py_id, &self->entry.oid)) + 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->oid); + return git_oid_to_py_str(&self->entry.oid); } PyGetSetDef IndexEntry_getseters[] = { - GETTER(IndexEntry, mode), - GETTER(IndexEntry, path), - GETTER(IndexEntry, oid), + GETSET(IndexEntry, mode), + GETSET(IndexEntry, path), + GETSET(IndexEntry, oid), GETTER(IndexEntry, hex), {NULL}, }; @@ -645,7 +739,7 @@ PyTypeObject IndexEntryType = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - 0, /* tp_init */ + (initproc)IndexEntry_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; diff --git a/src/index.h b/src/index.h index f38cb4a04..e59262ca6 100644 --- a/src/index.h +++ b/src/index.h @@ -40,7 +40,7 @@ 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* Index_write_tree(Index *self, PyObject *args); Py_ssize_t Index_len(Index *self); int Index_setitem(Index *self, PyObject *key, PyObject *value); diff --git a/src/pygit2.c b/src/pygit2.c index 7c72e509c..aeb5d8f67 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -343,7 +343,7 @@ moduleinit(PyObject* m) * Index & Working copy */ INIT_TYPE(IndexType, NULL, PyType_GenericNew) - INIT_TYPE(IndexEntryType, NULL, NULL) + INIT_TYPE(IndexEntryType, NULL, PyType_GenericNew) INIT_TYPE(IndexIterType, NULL, NULL) ADD_TYPE(m, Index) ADD_TYPE(m, IndexEntry) diff --git a/src/types.h b/src/types.h index 63a5672d5..1918c9d24 100644 --- a/src/types.h +++ b/src/types.h @@ -153,7 +153,7 @@ SIMPLE_TYPE(Index, git_index, index) typedef struct { PyObject_HEAD - const git_index_entry *entry; + git_index_entry entry; } IndexEntry; typedef struct { diff --git a/test/test_index.py b/test/test_index.py index 8613020e4..1afdf56ce 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -31,6 +31,7 @@ from __future__ import unicode_literals import os import unittest +import tempfile import pygit2 from . import utils @@ -139,6 +140,36 @@ def test_remove(self): index.remove('hello.txt') self.assertFalse('hello.txt' in index) + def test_change_attributes(self): + index = self.repo.index + entry = index['hello.txt'] + ign_entry = index['.gitignore'] + self.assertNotEqual(ign_entry.oid, entry.oid) + self.assertNotEqual(entry.mode, pygit2.GIT_FILEMODE_BLOB_EXECUTABLE) + entry.path = 'foo.txt' + entry.oid = ign_entry.oid + entry.mode = pygit2.GIT_FILEMODE_BLOB_EXECUTABLE + self.assertEqual('foo.txt', entry.path) + self.assertEqual(ign_entry.oid, entry.oid) + self.assertEqual(pygit2.GIT_FILEMODE_BLOB_EXECUTABLE, entry.mode) + + def test_write_tree_to(self): + path = tempfile.mkdtemp() + pygit2.init_repository(path) + nrepo = pygit2.Repository(path) + + id = self.repo.index.write_tree(nrepo) + self.assertNotEqual(None, nrepo[id]) + +class IndexEntryTest(utils.RepoTestCase): + + def test_create_entry(self): + index = self.repo.index + hello_entry = index['hello.txt'] + entry = pygit2.IndexEntry('README.md', hello_entry.oid, hello_entry.mode) + index.add(entry) + tree_id = index.write_tree() + self.assertEqual('60e769e57ae1d6a2ab75d8d253139e6260e1f912', str(tree_id)) if __name__ == '__main__': unittest.main()