Support diff for blobs

This commit is contained in:
Petr Hosek 2014-01-15 15:40:04 +00:00
parent cde2456327
commit dcc9051a8c
4 changed files with 149 additions and 34 deletions

@ -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 */

@ -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

@ -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

@ -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()