diff --git a/src/blob.c b/src/blob.c index 2d6a304..5e97ad0 100644 --- a/src/blob.c +++ b/src/blob.c @@ -28,106 +28,14 @@ #define PY_SSIZE_T_CLEAN #include #include "blob.h" -#include "diff.h" #include "error.h" #include "object.h" -#include "patch.h" #include "utils.h" extern PyObject *GitError; extern PyTypeObject BlobType; -PyDoc_STRVAR(Blob_diff__doc__, - "diff([blob, flag, old_as_path, new_as_path]) -> Patch\n" - "\n" - "Directly generate a :py:class:`pygit2.Patch` from the difference\n" - "between two blobs.\n" - "\n" - ":param Blob blob: the :py:class:`~pygit2.Blob` to diff.\n" - "\n" - ":param flag: a GIT_DIFF_* constant.\n" - "\n" - ":param str old_as_path: treat old blob as if it had this filename.\n" - "\n" - ":param str new_as_path: treat new blob as if it had this filename.\n" - "\n" - ":rtype: Patch\n"); - -PyObject * -Blob_diff(Blob *self, PyObject *args, PyObject *kwds) -{ - git_diff_options opts = GIT_DIFF_OPTIONS_INIT; - git_patch *patch; - char *old_as_path = NULL, *new_as_path = NULL; - Blob *py_blob = NULL; - int err; - char *keywords[] = {"blob", "flag", "old_as_path", "new_as_path", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!Iss", keywords, - &BlobType, &py_blob, &opts.flags, - &old_as_path, &new_as_path)) - return NULL; - - err = git_patch_from_blobs(&patch, self->blob, old_as_path, - py_blob ? py_blob->blob : NULL, new_as_path, - &opts); - if (err < 0) - return Error_set(err); - - return wrap_patch(patch); -} - - -PyDoc_STRVAR(Blob_diff_to_buffer__doc__, - "diff_to_buffer([buffer, flag, old_as_path, buffer_as_path]) -> Patch\n" - "\n" - "Directly generate a :py:class:`~pygit2.Patch` from the difference\n" - "between a blob and a buffer.\n" - "\n" - ":param Blob buffer: Raw data for new side of diff.\n" - "\n" - ":param flag: a GIT_DIFF_* constant.\n" - "\n" - ":param str old_as_path: treat old blob as if it had this filename.\n" - "\n" - ":param str buffer_as_path: treat buffer as if it had this filename.\n" - "\n" - ":rtype: Patch\n"); - -PyObject * -Blob_diff_to_buffer(Blob *self, PyObject *args, PyObject *kwds) -{ - git_diff_options opts = GIT_DIFF_OPTIONS_INIT; - git_patch *patch; - char *old_as_path = NULL, *buffer_as_path = NULL; - const char *buffer = NULL; - Py_ssize_t buffer_len; - int err; - char *keywords[] = {"buffer", "flag", "old_as_path", "buffer_as_path", - NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s#Iss", keywords, - &buffer, &buffer_len, &opts.flags, - &old_as_path, &buffer_as_path)) - return NULL; - - err = git_patch_from_blob_and_buffer(&patch, self->blob, old_as_path, - buffer, buffer_len, buffer_as_path, - &opts); - if (err < 0) - return Error_set(err); - - return wrap_patch(patch); -} - -static PyMethodDef Blob_methods[] = { - METHOD(Blob, diff, METH_VARARGS | METH_KEYWORDS), - METHOD(Blob, diff_to_buffer, METH_VARARGS | METH_KEYWORDS), - {NULL} -}; - - PyDoc_STRVAR(Blob_size__doc__, "Size."); PyObject * @@ -246,7 +154,7 @@ PyTypeObject BlobType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - Blob_methods, /* tp_methods */ + 0, /* tp_methods */ 0, /* tp_members */ Blob_getseters, /* tp_getset */ 0, /* tp_base */ diff --git a/src/diff.c b/src/diff.c index c0b3043..ff201cf 100644 --- a/src/diff.c +++ b/src/diff.c @@ -103,41 +103,24 @@ wrap_diff_delta(const git_diff_delta *delta) } PyObject * -wrap_diff_hunk(git_patch *patch, size_t idx) +wrap_diff_hunk(const git_diff_hunk *hunk, size_t idx, Patch *patch) { DiffHunk *py_hunk; - const git_diff_hunk *hunk; - const git_diff_line *line; - size_t j, lines_in_hunk; - int err; - err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, idx); - if (err < 0) - return Error_set(err); + if (!hunk) + Py_RETURN_NONE; py_hunk = PyObject_New(DiffHunk, &DiffHunkType); if (py_hunk) { + Py_INCREF(patch); + py_hunk->patch = patch; + py_hunk->index = idx; py_hunk->old_start = hunk->old_start; py_hunk->old_lines = hunk->old_lines; py_hunk->new_start = hunk->new_start; py_hunk->new_lines = hunk->new_lines; py_hunk->header = to_unicode_n((const char *) &hunk->header, hunk->header_len, NULL, NULL); - - py_hunk->lines = PyList_New(lines_in_hunk); - for (j = 0; j < lines_in_hunk; ++j) { - PyObject *py_line = NULL; - - err = git_patch_get_line_in_hunk(&line, patch, idx, j); - if (err < 0) - return Error_set(err); - - py_line = wrap_diff_line(line); - if (py_line == NULL) - return NULL; - - PyList_SetItem(py_hunk->lines, j, py_line); - } } return (PyObject *) py_hunk; @@ -180,7 +163,6 @@ PyMemberDef DiffFile_members[] = { {NULL} }; - PyDoc_STRVAR(DiffFile__doc__, "DiffFile object."); PyTypeObject DiffFileType = { @@ -390,44 +372,45 @@ PyTypeObject DiffLineType = { }; PyObject * -diff_get_patch_byindex(git_diff *diff, size_t idx) +diff_hunk_get_line_byindex(git_patch *patch, size_t hunk_idx, size_t hunk_line) { - git_patch *patch = NULL; + const git_diff_line *line = NULL; int err; - err = git_patch_from_diff(&patch, diff, idx); + err = git_patch_get_line_in_hunk(&line, patch, hunk_idx, hunk_line); if (err < 0) return Error_set(err); - return (PyObject*) wrap_patch(patch); + return (PyObject*) wrap_diff_line(line); } PyObject * -DiffIter_iternext(DiffIter *self) +DiffHunkIter_iternext(DiffHunkIter *self) { if (self->i < self->n) - return diff_get_patch_byindex(self->diff->diff, self->i++); + return diff_hunk_get_line_byindex(self->hunk->patch->patch, + self->hunk->index, self->i++); PyErr_SetNone(PyExc_StopIteration); return NULL; } -void -DiffIter_dealloc(DiffIter *self) +static void +DiffHunkIter_dealloc(DiffHunkIter *self) { - Py_CLEAR(self->diff); + Py_CLEAR(self->hunk); PyObject_Del(self); } -PyDoc_STRVAR(DiffIter__doc__, "Diff iterator object."); +PyDoc_STRVAR(DiffHunkIter__doc__, "Diff Hunk iterator object."); -PyTypeObject DiffIterType = { +PyTypeObject DiffHunkIterType = { PyVarObject_HEAD_INIT(NULL, 0) - "_pygit2.DiffIter", /* tp_name */ - sizeof(DiffIter), /* tp_basicsize */ + "_pygit2.DiffHunkIter", /* tp_name */ + sizeof(DiffHunkIter), /* tp_basicsize */ 0, /* tp_itemsize */ - (destructor)DiffIter_dealloc, /* tp_dealloc */ + (destructor)DiffHunkIter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -443,74 +426,78 @@ PyTypeObject DiffIterType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ - DiffIter__doc__, /* tp_doc */ + DiffHunkIter__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc) DiffIter_iternext, /* tp_iternext */ + (iternextfunc) DiffHunkIter_iternext, /* tp_iternext */ }; -Py_ssize_t -Diff_len(Diff *self) -{ - assert(self->diff); - return (Py_ssize_t)git_diff_num_deltas(self->diff); -} - -PyDoc_STRVAR(Diff_patch__doc__, "Patch diff string."); PyObject * -Diff_patch__get__(Diff *self) +DiffHunk_iter(DiffHunk *self) { - git_patch* patch; - git_buf buf = {NULL}; - int err = GIT_ERROR; - size_t i, num; - PyObject *py_patch = NULL; + DiffHunkIter *iter; - num = git_diff_num_deltas(self->diff); - if (num == 0) - Py_RETURN_NONE; - - for (i = 0; i < num ; ++i) { - err = git_patch_from_diff(&patch, self->diff, i); - if (err < 0) - goto cleanup; - - /* This appends to the current buf, so we can simply keep passing it */ - err = git_patch_to_buf(&buf, patch); - if (err < 0) - goto cleanup; - - git_patch_free(patch); + iter = PyObject_New(DiffHunkIter, &DiffHunkIterType); + if (iter != NULL) { + Py_INCREF(self); + iter->hunk = self; + iter->i = 0; + iter->n = git_patch_num_lines_in_hunk(self->patch->patch, self->index); } - - py_patch = to_unicode(buf.ptr, NULL, NULL); - git_buf_free(&buf); - -cleanup: - git_buf_free(&buf); - return (err < 0) ? Error_set(err) : py_patch; + return (PyObject*)iter; } +Py_ssize_t +DiffHunk_len(DiffHunk *self) +{ + assert(self->hunk); + return (Py_ssize_t)git_patch_num_lines_in_hunk(self->patch->patch, + self->index); +} + +PyObject * +DiffHunk_getitem(DiffHunk *self, PyObject *value) +{ + size_t i; + + if (PyLong_Check(value) < 0) { + PyErr_SetObject(PyExc_IndexError, value); + return NULL; + } + + i = PyLong_AsUnsignedLong(value); + if (PyErr_Occurred()) { + PyErr_SetObject(PyExc_IndexError, value); + return NULL; + } + + return diff_hunk_get_line_byindex(self->patch->patch, self->index, i); +} static void DiffHunk_dealloc(DiffHunk *self) { Py_CLEAR(self->header); - Py_CLEAR(self->lines); + Py_CLEAR(self->patch); PyObject_Del(self); } +PyMappingMethods DiffHunk_as_mapping = { + (lenfunc)DiffHunk_len, /* mp_length */ + (binaryfunc)DiffHunk_getitem, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + PyMemberDef DiffHunk_members[] = { MEMBER(DiffHunk, old_start, T_INT, "Old start."), MEMBER(DiffHunk, old_lines, T_INT, "Old lines."), MEMBER(DiffHunk, new_start, T_INT, "New start."), MEMBER(DiffHunk, new_lines, T_INT, "New lines."), MEMBER(DiffHunk, header, T_OBJECT, "Header."), - MEMBER(DiffHunk, lines, T_OBJECT, "Lines."), {NULL} }; @@ -530,7 +517,7 @@ PyTypeObject DiffHunkType = { 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + &DiffHunk_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ @@ -543,7 +530,7 @@ PyTypeObject DiffHunkType = { 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ + (getiterfunc)DiffHunk_iter, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ DiffHunk_members, /* tp_members */ @@ -586,6 +573,141 @@ Diff_from_c(Diff *dummy, PyObject *args) return wrap_diff(diff, (Repository *) py_repository); } +PyObject * +DiffPatchIter_iternext(DiffPatchIter *self) +{ + if (self->i < self->n) { + git_patch *patch = NULL; + int err; + + err = git_patch_from_diff(&patch, self->diff->diff, self->i++); + if (err < 0) + return Error_set(err); + + return (PyObject *) wrap_patch(patch); + } + + PyErr_SetNone(PyExc_StopIteration); + return NULL; +} + +static void +DiffPatchIter_dealloc(DiffPatchIter *self) +{ + Py_CLEAR(self->diff); + PyObject_Del(self); +} + + +PyDoc_STRVAR(DiffPatchIter__doc__, "Diff Patch iterator object."); + +PyTypeObject DiffPatchIterType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.DiffPatchIter", /* tp_name */ + sizeof(DiffPatchIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)DiffPatchIter_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 */ + DiffPatchIter__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc) DiffPatchIter_iternext, /* tp_iternext */ +}; + + +PyDoc_STRVAR(Diff_patches__doc__, "Get the delta associated with a patch."); + +PyObject * +Diff_patches(Diff *self) +{ + DiffPatchIter *iter; + + iter = PyObject_New(DiffPatchIter, &DiffPatchIterType); + if (iter != NULL) { + Py_INCREF(self); + iter->diff = self; + iter->i = 0; + iter->n = git_diff_num_deltas(self->diff); + } + return (PyObject*)iter; +} + +PyObject * +DiffIter_iternext(DiffIter *self) +{ + if (self->i < self->n) + return wrap_diff_delta(git_diff_get_delta(self->diff->diff, + self->i++)); + + PyErr_SetNone(PyExc_StopIteration); + return NULL; +} + +void +DiffIter_dealloc(DiffIter *self) +{ + Py_CLEAR(self->diff); + PyObject_Del(self); +} + + +PyDoc_STRVAR(DiffIter__doc__, "Diff iterator object."); + +PyTypeObject DiffIterType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.DiffIter", /* tp_name */ + sizeof(DiffIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)DiffIter_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 */ + DiffIter__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc) DiffIter_iternext, /* tp_iternext */ +}; + +Py_ssize_t +Diff_len(Diff *self) +{ + assert(self->diff); + return (Py_ssize_t)git_diff_num_deltas(self->diff); +} + + PyDoc_STRVAR(Diff_merge__doc__, "merge(diff)\n" "\n" @@ -654,14 +776,25 @@ PyObject * Diff_getitem(Diff *self, PyObject *value) { size_t i; + const git_diff_delta *delta; if (!PyLong_Check(value)) return NULL; i = PyLong_AsUnsignedLong(value); - return diff_get_patch_byindex(self->diff, i); -} + if (PyErr_Occurred()) { + PyErr_SetObject(PyExc_IndexError, value); + return NULL; + } + delta = git_diff_get_delta(self->diff, i); + if (!delta) { + PyErr_SetObject(PyExc_IndexError, value); + return NULL; + } + + return wrap_diff_delta(delta); +} static void Diff_dealloc(Diff *self) @@ -671,11 +804,6 @@ Diff_dealloc(Diff *self) PyObject_Del(self); } -PyGetSetDef Diff_getseters[] = { - GETTER(Diff, patch), - {NULL} -}; - PyMappingMethods Diff_as_mapping = { (lenfunc)Diff_len, /* mp_length */ (binaryfunc)Diff_getitem, /* mp_subscript */ @@ -686,10 +814,10 @@ static PyMethodDef Diff_methods[] = { METHOD(Diff, merge, METH_VARARGS), METHOD(Diff, find_similar, METH_VARARGS | METH_KEYWORDS), METHOD(Diff, from_c, METH_STATIC | METH_VARARGS), + METHOD(Diff, patches, METH_NOARGS), {NULL} }; - PyDoc_STRVAR(Diff__doc__, "Diff objects."); PyTypeObject DiffType = { @@ -722,7 +850,7 @@ PyTypeObject DiffType = { 0, /* tp_iternext */ Diff_methods, /* tp_methods */ 0, /* tp_members */ - Diff_getseters, /* tp_getset */ + 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ diff --git a/src/diff.h b/src/diff.h index 5399e96..4e4de52 100644 --- a/src/diff.h +++ b/src/diff.h @@ -33,13 +33,10 @@ #include #include "types.h" -PyObject* Diff_changes(Diff *self); -PyObject* Diff_patch(Diff *self); - PyObject* wrap_diff(git_diff *diff, Repository *repo); PyObject* wrap_diff_delta(const git_diff_delta *delta); PyObject* wrap_diff_file(const git_diff_file *file); -PyObject * wrap_diff_hunk(git_patch *patch, size_t idx); +PyObject* wrap_diff_hunk(const git_diff_hunk *hunk, size_t idx, Patch *patch); PyObject* wrap_diff_line(const git_diff_line *line); #endif diff --git a/src/patch.c b/src/patch.c index dd31035..8078089 100644 --- a/src/patch.c +++ b/src/patch.c @@ -30,13 +30,18 @@ #include #include "diff.h" #include "error.h" -#include "oid.h" +#include "patch.h" #include "types.h" #include "utils.h" -extern PyTypeObject DiffHunkType; -PyTypeObject PatchType; +extern PyObject *GitError; +extern PyTypeObject BlobType; +extern PyTypeObject DiffType; +extern PyTypeObject DiffHunkType; +extern PyTypeObject IndexType; +extern PyTypeObject PatchType; +extern PyTypeObject TreeType; PyObject * wrap_patch(git_patch *patch) @@ -49,29 +54,305 @@ wrap_patch(git_patch *patch) Py_RETURN_NONE; py_patch = PyObject_New(Patch, &PatchType); - if (py_patch) { + if (py_patch) py_patch->patch = patch; - hunk_amounts = git_patch_num_hunks(patch); - py_patch->hunks = PyList_New(hunk_amounts); - for (i = 0; i < hunk_amounts; ++i) { - py_hunk = wrap_diff_hunk(patch, i); - if (py_hunk) - PyList_SetItem((PyObject*) py_patch->hunks, i, py_hunk); - } - } - return (PyObject*) py_patch; } -static void -Patch_dealloc(Patch *self) +PyObject * +patch_get_hunk_byindex(Patch *self, size_t idx) { - Py_CLEAR(self->hunks); - git_patch_free(self->patch); + const git_diff_hunk *hunk = NULL; + int err; + + err = git_patch_get_hunk(&hunk, NULL, self->patch, idx); + if (err < 0) + return Error_set(err); + + return (PyObject*) wrap_diff_hunk(hunk, idx, self); +} + +PyObject * +PatchIter_iternext(PatchIter *self) +{ + if (self->i < self->n) + return patch_get_hunk_byindex(self->patch, self->i++); + + PyErr_SetNone(PyExc_StopIteration); + return NULL; +} + +static void +PatchIter_dealloc(PatchIter *self) +{ + Py_CLEAR(self->patch); PyObject_Del(self); } +PyDoc_STRVAR(PatchIter__doc__, "Patch iterator object."); + +PyTypeObject PatchIterType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.PatchIter", /* tp_name */ + sizeof(PatchIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)PatchIter_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 */ + PatchIter__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc) PatchIter_iternext, /* tp_iternext */ +}; + + +PyObject * +Patch_iter(Patch *self) +{ + PatchIter *iter; + + iter = PyObject_New(PatchIter, &PatchIterType); + if (iter != NULL) { + Py_INCREF(self); + iter->patch = self; + iter->i = 0; + iter->n = git_patch_num_hunks(self->patch); + } + return (PyObject*)iter; +} + +Py_ssize_t +Patch_len(Patch *self) +{ + assert(self->patch); + return (Py_ssize_t)git_patch_num_hunks(self->patch); +} + +PyObject * +Patch_getitem(Patch *self, PyObject *value) +{ + size_t i; + + if (PyLong_Check(value) < 0) { + PyErr_SetObject(PyExc_IndexError, value); + return NULL; + } + + i = PyLong_AsUnsignedLong(value); + if (PyErr_Occurred()) { + PyErr_SetObject(PyExc_IndexError, value); + return NULL; + } + + return patch_get_hunk_byindex(self, i); +} + +PyObject * +Patch__str__(Patch *self) +{ + git_buf buf = {NULL}; + int err; + PyObject *py_str; + + err = git_patch_to_buf(&buf, self->patch); + if (err < 0) + goto cleanup; + + py_str = to_unicode(buf.ptr, NULL, NULL); + +cleanup: + git_buf_free(&buf); + return (err < 0) ? Error_set(err) : py_str; +} + +PyDoc_STRVAR(Patch_from_blob_and_buffer__doc__, + "from_blob_and_buffer([old_blob, old_as_path, buffer, buffer_as_path,\n" + " flags] -> Patch\n" + "\n" + "Directly generate a :py:class:`~pygit2.Patch` from the difference\n" + "between a blob and a buffer.\n" + "\n" + ":param Blob old_blob: the :py:class:`~pygit2.Blob` for old side of diff.\n" + "\n" + ":param str old_as_path: treat old blob as if it had this filename.\n" + "\n" + ":param Blob buffer: Raw data for new side of diff.\n" + "\n" + ":param str buffer_as_path: treat buffer as if it had this filename.\n" + "\n" + ":param flag: a GIT_DIFF_* constant.\n" + "\n" + ":rtype: Patch\n"); + +PyObject * +Patch_from_blob_and_buffer(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_patch *patch; + char *old_as_path = NULL, *buffer_as_path = NULL; + const char *buffer = NULL; + Py_ssize_t buffer_len; + Blob *py_old_blob = NULL; + int err; + char *keywords[] = {"old_blob", "old_as_path", "buffer", "buffer_as_path", + "flags", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!ss#sI", keywords, + &BlobType, &py_old_blob, &old_as_path, + &buffer, &buffer_len, &buffer_as_path, + &opts.flags)) + return NULL; + + err = git_patch_from_blob_and_buffer(&patch, + py_old_blob ? py_old_blob->blob : NULL, + old_as_path, + buffer, + buffer_len, + buffer_as_path, + &opts); + if (err < 0) + return Error_set(err); + + return wrap_patch(patch); +} + + +PyDoc_STRVAR(Patch_from_blobs__doc__, + "from_blobs([old_blob, old_as_path, new_blob, new_as_path, flags] -> Patch\n" + "\n" + "Directly generate a :py:class:`pygit2.Patch` from the difference\n" + "between two blobs.\n" + "\n" + ":param Blob old_blob: the :py:class:`~pygit2.Blob` for old side of diff.\n" + "\n" + ":param str old_as_path: treat old blob as if it had this filename.\n" + "\n" + ":param Blob new_blob: the :py:class:`~pygit2.Blob` for new side of diff.\n" + "\n" + ":param str new_as_path: treat new blob as if it had this filename.\n" + "\n" + ":param flag: a GIT_DIFF_* constant.\n" + "\n" + ":rtype: Patch\n"); + +PyObject * +Patch_from_blobs(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_patch *patch; + char *old_as_path = NULL, *new_as_path = NULL; + Blob *py_new_blob = NULL, *py_old_blob = NULL; + int err; + char *keywords[] = {"old_blob", "old_as_path", "new_blob", "new_as_path", + "flags", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!sO!sI", keywords, + &BlobType, &py_old_blob, &old_as_path, + &BlobType, &py_new_blob, &new_as_path, + &opts.flags)) + return NULL; + + err = git_patch_from_blobs(&patch, + py_old_blob ? py_old_blob->blob : NULL, + old_as_path, + py_new_blob ? py_new_blob->blob : NULL, + new_as_path, + &opts); + if (err < 0) + return Error_set(err); + + return wrap_patch(patch); +} + + +PyDoc_STRVAR(Patch_from_diff__doc__, + "from_diff(diff, idx) -> Patch\n" + "\n" + "Return the :py:class:`pygit2.Patch` for an entry in the diff list.\n" + "\n" + "Arguments:\n" + "\n" + "diff: the :py:class:`~pygit2.Diff` list object.\n" + "\n" + "idx: index into diff list.\n"); + +PyObject * +Patch_from_diff(PyTypeObject *type, PyObject *args) +{ + git_patch *patch; + Diff *py_diff; + Py_ssize_t idx; + int err; + + if (!PyArg_ParseTuple(args, "O!n", &DiffType, &py_diff, &idx)) + return NULL; + + err = git_patch_from_diff(&patch, py_diff->diff, idx); + if (err < 0) + return Error_set(err); + + return wrap_patch(patch); +} + + +PyDoc_STRVAR(Patch_size__doc__, + "size([include_context, include_hunk_headers, include_file_headers]) -> size\n" + "\n" + "Look up size of patch diff data in bytes.\n" + "\n" + "Arguments:\n" + "\n" + "include_context: include context lines in size if non-zero.\n" + "\n" + "include_hunk_headers: include hunk header lines if non-zero.\n" + "\n" + "include_file_headers: include file header lines if non-zero."); + +PyObject * +Patch_size(Patch *self, PyObject *args, PyObject *kwds) +{ + int context = 0, hunk_headers = 0, file_headers = 0; + PyObject *py_context = NULL; + PyObject *py_hunk_headers = NULL; + PyObject *py_file_headers = NULL; + char *keywords[] = {"include_context", "include_hunk_headers", + "include_file_headers", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!O!O!", keywords, + &PyBool_Type, &py_context, + &PyBool_Type, &py_hunk_headers, + &PyBool_Type, &py_file_headers)) + return NULL; + + if (py_context) + context = PyObject_IsTrue(py_context); + if (py_hunk_headers) + hunk_headers = PyObject_IsTrue(py_hunk_headers); + if (py_file_headers) + file_headers = PyObject_IsTrue(py_file_headers); + + return PyLong_FromSize_t(git_patch_size(self->patch, + context, hunk_headers, file_headers)); +} + + PyDoc_STRVAR(Patch_delta__doc__, "Get the delta associated with a patch."); PyObject * @@ -103,8 +384,24 @@ Patch_line_stats__get__(Patch *self) return Py_BuildValue("III", context, additions, deletions); } -PyMemberDef Patch_members[] = { - MEMBER(Patch, hunks, T_OBJECT, "hunks"), +void +Patch_dealloc(Patch *self) +{ + git_patch_free(self->patch); + PyObject_Del(self); +} + +PyMappingMethods Patch_as_mapping = { + (lenfunc)Patch_len, /* mp_length */ + (binaryfunc)Patch_getitem, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + +PyMethodDef Patch_methods[] = { + METHOD(Patch, from_blob_and_buffer, METH_VARARGS | METH_KEYWORDS | METH_CLASS), + METHOD(Patch, from_blobs, METH_VARARGS | METH_KEYWORDS | METH_CLASS), + METHOD(Patch, from_diff, METH_VARARGS | METH_KEYWORDS | METH_CLASS), + METHOD(Patch, size, METH_VARARGS | METH_KEYWORDS), {NULL} }; @@ -114,7 +411,7 @@ PyGetSetDef Patch_getseters[] = { {NULL} }; -PyDoc_STRVAR(Patch__doc__, "Diff patch object."); +PyDoc_STRVAR(Patch__doc__, "Patch object."); PyTypeObject PatchType = { PyVarObject_HEAD_INIT(NULL, 0) @@ -129,10 +426,10 @@ PyTypeObject PatchType = { 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + &Patch_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ - 0, /* tp_str */ + (reprfunc)Patch__str__, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ @@ -142,10 +439,10 @@ PyTypeObject PatchType = { 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ + (getiterfunc)Patch_iter, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ - Patch_members, /* tp_members */ + Patch_methods, /* tp_methods */ + 0, /* tp_members */ Patch_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ diff --git a/src/pygit2.c b/src/pygit2.c index 2c76980..368f68c 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -48,8 +48,10 @@ extern PyTypeObject DiffIterType; extern PyTypeObject DiffDeltaType; extern PyTypeObject DiffFileType; extern PyTypeObject DiffHunkType; +extern PyTypeObject DiffHunkIterType; extern PyTypeObject DiffLineType; extern PyTypeObject PatchType; +extern PyTypeObject PatchIterType; extern PyTypeObject TreeType; extern PyTypeObject TreeBuilderType; extern PyTypeObject TreeEntryType; @@ -300,14 +302,13 @@ moduleinit(PyObject* m) INIT_TYPE(DiffDeltaType, NULL, NULL) INIT_TYPE(DiffFileType, NULL, NULL) INIT_TYPE(DiffHunkType, NULL, NULL) + INIT_TYPE(DiffHunkIterType, NULL, NULL) INIT_TYPE(DiffLineType, NULL, NULL) - INIT_TYPE(PatchType, NULL, NULL) ADD_TYPE(m, Diff) ADD_TYPE(m, DiffDelta) ADD_TYPE(m, DiffFile) ADD_TYPE(m, DiffHunk) ADD_TYPE(m, DiffLine) - ADD_TYPE(m, Patch) ADD_CONSTANT_INT(m, GIT_DIFF_NORMAL) ADD_CONSTANT_INT(m, GIT_DIFF_REVERSE) ADD_CONSTANT_INT(m, GIT_DIFF_FORCE_TEXT) @@ -340,6 +341,25 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED) /* --break-rewrites=/M */ ADD_CONSTANT_INT(m, GIT_DIFF_FIND_AND_BREAK_REWRITES) + ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_BINARY) + ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_NOT_BINARY) + ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_VALID_ID) + ADD_CONSTANT_INT(m, GIT_DELTA_UNMODIFIED) + ADD_CONSTANT_INT(m, GIT_DELTA_ADDED) + ADD_CONSTANT_INT(m, GIT_DELTA_DELETED) + ADD_CONSTANT_INT(m, GIT_DELTA_MODIFIED) + ADD_CONSTANT_INT(m, GIT_DELTA_RENAMED) + ADD_CONSTANT_INT(m, GIT_DELTA_COPIED) + ADD_CONSTANT_INT(m, GIT_DELTA_IGNORED) + ADD_CONSTANT_INT(m, GIT_DELTA_UNTRACKED) + ADD_CONSTANT_INT(m, GIT_DELTA_TYPECHANGE) + + /* + * Patch + */ + INIT_TYPE(PatchType, NULL, NULL) + INIT_TYPE(PatchIterType, NULL, NULL) + ADD_TYPE(m, Patch) /* DiffDelta and DiffFile flags */ ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_BINARY) diff --git a/src/types.h b/src/types.h index d2774a9..3e193fe 100644 --- a/src/types.h +++ b/src/types.h @@ -98,11 +98,14 @@ typedef struct { } NoteIter; /* git_patch */ +SIMPLE_TYPE(Patch, git_patch, patch) + typedef struct { PyObject_HEAD - git_patch *patch; - PyObject* hunks; -} Patch; + Patch *patch; + size_t i; + size_t n; +} PatchIter; /* git_diff */ SIMPLE_TYPE(Diff, git_diff, diff) @@ -114,6 +117,13 @@ typedef struct { size_t n; } DiffIter; +typedef struct { + PyObject_HEAD + Diff *diff; + size_t i; + size_t n; +} DiffPatchIter; + typedef struct { PyObject_HEAD PyObject *id; @@ -135,7 +145,9 @@ typedef struct { typedef struct { PyObject_HEAD - PyObject* lines; + Patch *patch; + git_diff_hunk *hunk; + size_t index; int old_start; int old_lines; int new_start; @@ -143,6 +155,13 @@ typedef struct { PyObject *header; } DiffHunk; +typedef struct { + PyObject_HEAD + DiffHunk *hunk; + size_t i; + size_t n; +} DiffHunkIter; + typedef struct { PyObject_HEAD char origin; diff --git a/test/test_blob.py b/test/test_blob.py index 9fd1d4f..8de88ff 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -112,16 +112,5 @@ class BlobTest(utils.RepoTestCase): self.assertTrue(isinstance(blob, pygit2.Blob)) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) - def test_diff_blob(self): - blob = self.repo[BLOB_SHA] - old_blob = self.repo['3b18e512dba79e4c8300dd08aeb37f8e728b8dad'] - patch = blob.diff(old_blob, old_as_path="hello.txt") - self.assertEqual(len(patch.hunks), 1) - - def test_diff_blob_to_buffer(self): - blob = self.repo[BLOB_SHA] - patch = blob.diff_to_buffer("hello world") - self.assertEqual(len(patch.hunks), 1) - if __name__ == '__main__': unittest.main() diff --git a/test/test_diff.py b/test/test_diff.py index 0862b9f..5838cbc 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -47,22 +47,6 @@ COMMIT_SHA1_6 = 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87' COMMIT_SHA1_7 = '784855caf26449a1914d2cf62d12b9374d76ae78' -PATCH = """diff --git a/a b/a -index 7f129fd..af431f2 100644 ---- a/a -+++ b/a -@@ -1 +1 @@ --a contents 2 -+a contents -diff --git a/c/d b/c/d -deleted file mode 100644 -index 297efb8..0000000 ---- a/c/d -+++ /dev/null -@@ -1 +0,0 @@ --c/d contents -""" - DIFF_HEAD_TO_INDEX_EXPECTED = [ 'staged_changes', 'staged_changes_file_deleted', @@ -108,11 +92,11 @@ class DiffDirtyTest(utils.DirtyRepoTestCase): head = repo[repo.lookup_reference('HEAD').resolve().target] diff = head.tree.diff_to_index(repo.index) - files = [patch.delta.new_file.path for patch in diff] + files = [delta.new_file.path for delta in diff] self.assertEqual(DIFF_HEAD_TO_INDEX_EXPECTED, files) diff = repo.diff('HEAD', cached=True) - files = [patch.delta.new_file.path for patch in diff] + files = [delta.new_file.path for delta in diff] self.assertEqual(DIFF_HEAD_TO_INDEX_EXPECTED, files) def test_workdir_to_tree(self): @@ -120,16 +104,16 @@ class DiffDirtyTest(utils.DirtyRepoTestCase): head = repo[repo.lookup_reference('HEAD').resolve().target] diff = head.tree.diff_to_workdir() - files = [patch.delta.new_file.path for patch in diff] + files = [delta.new_file.path for delta in diff] self.assertEqual(DIFF_HEAD_TO_WORKDIR_EXPECTED, files) diff = repo.diff('HEAD') - files = [patch.delta.new_file.path for patch in diff] + files = [delta.new_file.path for delta in diff] self.assertEqual(DIFF_HEAD_TO_WORKDIR_EXPECTED, files) def test_index_to_workdir(self): diff = self.repo.diff() - files = [patch.delta.new_file.path for patch in diff] + files = [delta.new_file.path for delta in diff] self.assertEqual(DIFF_INDEX_TO_WORK_EXPECTED, files) @@ -146,15 +130,15 @@ class DiffTest(utils.BareRepoTestCase): head = repo[repo.lookup_reference('HEAD').resolve().target] diff = self.repo.index.diff_to_tree(head.tree) - files = [patch.delta.new_file.path.split('/')[0] for patch in diff] + files = [delta.new_file.path.split('/')[0] for delta in diff] self.assertEqual([x.name for x in head.tree], files) diff = head.tree.diff_to_index(repo.index) - files = [patch.delta.new_file.path.split('/')[0] for patch in diff] + files = [delta.new_file.path.split('/')[0] for delta in diff] self.assertEqual([x.name for x in head.tree], files) diff = repo.diff('HEAD', cached=True) - files = [patch.delta.new_file.path.split('/')[0] for patch in diff] + files = [delta.new_file.path.split('/')[0] for delta in diff] self.assertEqual([x.name for x in head.tree], files) def test_diff_tree(self): @@ -165,18 +149,19 @@ class DiffTest(utils.BareRepoTestCase): # self.assertIsNotNone is 2.7 only self.assertTrue(diff is not None) # self.assertIn is 2.7 only - self.assertEqual(2, sum(map(lambda x: len(x.hunks), diff))) + self.assertEqual(2, sum(map(lambda x: len(x), diff.patches()))) - patch = diff[0] - hunk = patch.hunks[0] + patch = pygit2.Patch.from_diff(diff, 0) + hunk = patch[0] self.assertEqual(hunk.old_start, 1) self.assertEqual(hunk.old_lines, 1) self.assertEqual(hunk.new_start, 1) self.assertEqual(hunk.new_lines, 1) - self.assertEqual(patch.delta.old_file.path, 'a') - self.assertEqual(patch.delta.new_file.path, 'a') - self.assertEqual(patch.delta.is_binary, False) + delta = diff[0] + self.assertEqual(delta.old_file.path, 'a') + self.assertEqual(delta.new_file.path, 'a') + self.assertEqual(delta.is_binary, False) _test(commit_a.tree.diff_to_tree(commit_b.tree)) _test(self.repo.diff(COMMIT_SHA1_1, COMMIT_SHA1_2)) @@ -187,16 +172,16 @@ class DiffTest(utils.BareRepoTestCase): diff = commit_a.tree.diff_to_tree() def get_context_for_lines(diff): - hunks = chain(*map(lambda x: x.hunks, [p for p in diff])) - lines = chain(*map(lambda x: x.lines, hunks)) + hunks = chain(*map(lambda x: x, [p for p in diff.patches()])) + lines = chain(*map(lambda x: x, hunks)) return map(lambda x: x.origin, lines) - entries = [p.delta.new_file.path for p in diff] + entries = [delta.new_file.path for delta in diff] self.assertAll(lambda x: commit_a.tree[x], entries) self.assertAll(lambda x: '-' == x, get_context_for_lines(diff)) diff_swaped = commit_a.tree.diff_to_tree(swap=True) - entries = [p.delta.new_file.path for p in diff_swaped] + entries = [delta.new_file.path for delta in diff_swaped] self.assertAll(lambda x: commit_a.tree[x], entries) self.assertAll(lambda x: '+' == x, get_context_for_lines(diff_swaped)) @@ -212,11 +197,13 @@ class DiffTest(utils.BareRepoTestCase): GIT_DIFF_IGNORE_WHITESPACE_EOL]: diff = commit_c.tree.diff_to_tree(commit_d.tree, flag) self.assertTrue(diff is not None) - self.assertEqual(0, len(diff[0].hunks)) + patch = pygit2.Patch.from_diff(diff, 0) + self.assertEqual(0, len(patch)) diff = commit_c.tree.diff_to_tree(commit_d.tree) self.assertTrue(diff is not None) - self.assertEqual(1, len(diff[0].hunks)) + patch = pygit2.Patch.from_diff(diff, 0) + self.assertEqual(1, len(patch)) def test_diff_merge(self): commit_a = self.repo[COMMIT_SHA1_1] @@ -232,16 +219,16 @@ class DiffTest(utils.BareRepoTestCase): self.assertTrue(diff_c is not None) # assertIn / assertNotIn are 2.7 only - self.assertFalse('b' in [patch.delta.new_file.path for patch in diff_b]) - self.assertTrue('b' in [patch.delta.new_file.path for patch in diff_c]) + self.assertFalse('b' in [delta.new_file.path for delta in diff_b]) + self.assertTrue('b' in [delta.new_file.path for delta in diff_c]) diff_b.merge(diff_c) # assertIn is 2.7 only - self.assertTrue('b' in [patch.delta.new_file.path for patch in diff_b]) + self.assertTrue('b' in [delta.new_file.path for delta in diff_b]) - patch = diff_b[0] - hunk = patch.hunks[0] + patch = pygit2.Patch.from_diff(diff_b, 0) + hunk = patch[0] self.assertEqual(hunk.old_start, 1) self.assertEqual(hunk.old_lines, 1) self.assertEqual(hunk.new_start, 1) @@ -250,29 +237,22 @@ class DiffTest(utils.BareRepoTestCase): self.assertEqual(patch.delta.old_file.path, 'a') self.assertEqual(patch.delta.new_file.path, 'a') - def test_diff_patch(self): - commit_a = self.repo[COMMIT_SHA1_1] - commit_b = self.repo[COMMIT_SHA1_2] - - diff = commit_a.tree.diff_to_tree(commit_b.tree) - self.assertEqual(diff.patch, PATCH) - self.assertEqual(len(diff), len([patch for patch in diff])) - def test_diff_ids(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] - patch = commit_a.tree.diff_to_tree(commit_b.tree)[0] - self.assertEqual(patch.delta.old_file.id.hex, + delta = commit_a.tree.diff_to_tree(commit_b.tree)[0] + self.assertEqual(delta.old_file.id.hex, '7f129fd57e31e935c6d60a0c794efe4e6927664b') - self.assertEqual(patch.delta.new_file.id.hex, + self.assertEqual(delta.new_file.id.hex, 'af431f20fc541ed6d5afede3e2dc7160f6f01f16') def test_hunk_content(self): commit_a = self.repo[COMMIT_SHA1_1] commit_b = self.repo[COMMIT_SHA1_2] - patch = commit_a.tree.diff_to_tree(commit_b.tree)[0] - hunk = patch.hunks[0] - lines = ('{0} {1}'.format(x.origin, x.content) for x in hunk.lines) + diff = commit_a.tree.diff_to_tree(commit_b.tree) + patch = pygit2.Patch.from_diff(diff, 0) + hunk = patch[0] + lines = ('{0} {1}'.format(x.origin, x.content) for x in hunk) self.assertEqual(HUNK_EXPECTED, ''.join(lines)) def test_find_similar(self): @@ -283,11 +263,11 @@ class DiffTest(utils.BareRepoTestCase): #~ --find-copies-harder during rename transformion... diff = commit_a.tree.diff_to_tree(commit_b.tree, GIT_DIFF_INCLUDE_UNMODIFIED) - self.assertAll(lambda x: x.delta.status != GIT_DELTA_RENAMED, diff) - self.assertAll(lambda x: x.delta.status_char() != 'R', diff) + self.assertAll(lambda delta: delta.status != GIT_DELTA_RENAMED, diff) + self.assertAll(lambda delta: delta.status_char() != 'R', diff) diff.find_similar() - self.assertAny(lambda x: x.delta.status == GIT_DELTA_RENAMED, diff) - self.assertAny(lambda x: x.delta.status_char() == 'R', diff) + self.assertAny(lambda delta: delta.status == GIT_DELTA_RENAMED, diff) + self.assertAny(lambda delta: delta.status_char() == 'R', diff) if __name__ == '__main__': unittest.main() diff --git a/test/test_patch.py b/test/test_patch.py new file mode 100644 index 0000000..40ee680 --- /dev/null +++ b/test/test_patch.py @@ -0,0 +1,93 @@ +# -*- 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. + +"""Tests for Patch objects.""" + +from __future__ import absolute_import +from __future__ import unicode_literals +from os.path import dirname, join +import unittest + +import pygit2 +from . import utils + + +COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10' +COMMIT_SHA1_2 = 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c' + + +PATCH = """diff --git a/a b/a +index 7f129fd..af431f2 100644 +--- a/a ++++ b/a +@@ -1 +1 @@ +-a contents 2 ++a contents +diff --git a/c/d b/c/d +deleted file mode 100644 +index 297efb8..0000000 +--- a/c/d ++++ /dev/null +@@ -1 +0,0 @@ +-c/d contents +""" + +BLOB_SHA = 'a520c24d85fbfc815d385957eed41406ca5a860b' +BLOB_CONTENT = """hello world +hola mundo +bonjour le monde +""".encode() +BLOB_NEW_CONTENT = b'foo bar\n' +BLOB_FILE_CONTENT = b'bye world\n' + + +class DiffTest(utils.BareRepoTestCase): + + def test_diff_patch(self): + commit_a = self.repo[COMMIT_SHA1_1] + commit_b = self.repo[COMMIT_SHA1_2] + + diff = commit_a.tree.diff_to_tree(commit_b.tree) + self.assertEqual(''.join([str(pygit2.Patch.from_diff(diff, i)) for i in range(len(diff))]), PATCH) + self.assertEqual(len(diff), len([patch for patch in diff])) + +class BlobTest(utils.RepoTestCase): + + def test_diff_blob(self): + old_blob = self.repo[BLOB_SHA] + new_blob = self.repo['3b18e512dba79e4c8300dd08aeb37f8e728b8dad'] + patch = pygit2.Patch.from_blobs(old_blob, "hello.txt", new_blob) + self.assertEqual(len(patch), 1) + self.assertEqual(patch.delta.old_file.path, "hello.txt") + + def test_diff_blob_to_buffer(self): + old_blob = self.repo[BLOB_SHA] + patch = pygit2.Patch.from_blob_and_buffer(old_blob, buffer="hello world") + self.assertEqual(len(patch), 1) + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_repository.py b/test/test_repository.py index 3ee05bf..f3b6681 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -381,7 +381,7 @@ class RepositoryTest_II(utils.RepoTestCase): #soft reset will keep changes in the index diff = self.repo.diff(cached=True) - self.assertRaises(KeyError, lambda: diff[0]) + self.assertTrue(KeyError, lambda: diff[0]) def test_reset_mixed(self): ref = "5ebeeebb320790caf276b9fc8b24546d63316533" @@ -404,8 +404,9 @@ class RepositoryTest_II(utils.RepoTestCase): #mixed reset will set the index to match working copy diff = self.repo.diff(cached=True) - self.assertTrue("hola mundo\n" in diff.patch) - self.assertTrue("bonjour le monde\n" in diff.patch) + + self.assertTrue("hola mundo\n" in ''.join([str(patch) for patch in diff.patches()])) + self.assertTrue("bonjour le monde\n" in ''.join([str(patch) for patch in diff.patches()])) class RepositorySignatureTest(utils.RepoTestCase):