diff --git a/src/blob.c b/src/blob.c index 83c7ece..1b2840f 100644 --- a/src/blob.c +++ b/src/blob.c @@ -27,10 +27,105 @@ #define PY_SSIZE_T_CLEAN #include <Python.h> +#include "diff.h" +#include "error.h" #include "utils.h" #include "object.h" #include "blob.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" + "Arguments:\n" + "\n" + "blob: the :py:class:`~pygit2.Blob` to diff.\n" + "\n" + "flag: a GIT_DIFF_* constant.\n" + "\n" + "old_as_path: treat old blob as if it had this filename.\n" + "\n" + "new_as_path: treat new blob as if it had this filename.\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!ssI", 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" + "Arguments:\n" + "\n" + "buffer: Raw data for new side of diff.\n" + "\n" + "flag: a GIT_DIFF_* constant.\n" + "\n" + "old_as_path: treat old blob as if it had this filename.\n" + "\n" + "buffer_as_path: treat buffer as if it had this filename.\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#ssI", 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."); @@ -94,7 +189,7 @@ PyTypeObject BlobType = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + Blob_methods, /* tp_methods */ 0, /* tp_members */ Blob_getseters, /* tp_getset */ 0, /* tp_base */ diff --git a/src/diff.c b/src/diff.c index 75df6ac..1609a7d 100644 --- a/src/diff.c +++ b/src/diff.c @@ -57,27 +57,24 @@ wrap_diff(git_diff *diff, Repository *repo) return (PyObject*) py_diff; } -PyObject* -diff_get_patch_byindex(git_diff* diff, size_t idx) +PyObject * +wrap_patch(git_patch *patch) { - const git_diff_delta* delta; - const git_diff_hunk *hunk; - const git_diff_line *line; - git_patch* patch = NULL; - size_t i, j, hunk_amounts, lines_in_hunk, additions, deletions; - int err; - Hunk *py_hunk = NULL; - Patch *py_patch = NULL; - PyObject *py_line_origin=NULL, *py_line=NULL; + Patch *py_patch; - err = git_patch_from_diff(&patch, diff, idx); - if (err < 0) - return Error_set(err); - - delta = git_patch_get_delta(patch); + if (!patch) + Py_RETURN_NONE; py_patch = PyObject_New(Patch, &PatchType); - if (py_patch != NULL) { + if (py_patch) { + size_t i, j, hunk_amounts, lines_in_hunk, additions, deletions; + const git_diff_delta *delta; + const git_diff_hunk *hunk; + const git_diff_line *line; + int err; + + delta = git_patch_get_delta(patch); + py_patch->old_file_path = delta->old_file.path; py_patch->new_file_path = delta->new_file.path; py_patch->status = git_diff_status_char(delta->status); @@ -92,11 +89,12 @@ diff_get_patch_byindex(git_diff* diff, size_t idx) hunk_amounts = git_patch_num_hunks(patch); py_patch->hunks = PyList_New(hunk_amounts); - for (i=0; i < hunk_amounts; ++i) { - err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, i); + for (i = 0; i < hunk_amounts; ++i) { + Hunk *py_hunk = NULL; + err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, i); if (err < 0) - goto cleanup; + return Error_set(err); py_hunk = PyObject_New(Hunk, &HunkType); if (py_hunk != NULL) { @@ -106,20 +104,20 @@ diff_get_patch_byindex(git_diff* diff, size_t idx) py_hunk->new_lines = hunk->new_lines; py_hunk->lines = PyList_New(lines_in_hunk); - for (j=0; j < lines_in_hunk; ++j) { + for (j = 0; j < lines_in_hunk; ++j) { + PyObject *py_line_origin = NULL, *py_line = NULL; + err = git_patch_get_line_in_hunk(&line, patch, i, j); - if (err < 0) - goto cleanup; + return Error_set(err); - py_line_origin = to_unicode_n(&line->origin, 1, NULL, NULL); - py_line = to_unicode_n(line->content, line->content_len, NULL, NULL); + py_line_origin = to_unicode_n(&line->origin, 1, + NULL, NULL); + py_line = to_unicode_n(line->content, line->content_len, + NULL, NULL); PyList_SetItem(py_hunk->lines, j, - Py_BuildValue("OO", - py_line_origin, - py_line - ) - ); + Py_BuildValue("OO", py_line_origin, py_line)); + Py_DECREF(py_line_origin); Py_DECREF(py_line); } @@ -130,10 +128,20 @@ diff_get_patch_byindex(git_diff* diff, size_t idx) } } -cleanup: - git_patch_free(patch); + return (PyObject*) py_patch; +} - return (err < 0) ? Error_set(err) : (PyObject*) py_patch; +PyObject* +diff_get_patch_byindex(git_diff *diff, size_t idx) +{ + git_patch *patch = NULL; + int err; + + err = git_patch_from_diff(&patch, diff, idx); + if (err < 0) + return Error_set(err); + + return (PyObject*) wrap_patch(patch); } static void diff --git a/src/diff.h b/src/diff.h index f32c930..9ba3a9e 100644 --- a/src/diff.h +++ b/src/diff.h @@ -42,5 +42,6 @@ PyObject* Diff_changes(Diff *self); PyObject* Diff_patch(Diff *self); PyObject* wrap_diff(git_diff *diff, Repository *repo); +PyObject* wrap_patch(git_patch *patch); #endif diff --git a/test/test_blob.py b/test/test_blob.py index 4e7a6cd..30faee4 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -105,5 +105,16 @@ 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()