Support diff for blobs
This commit is contained in:
97
src/blob.c
97
src/blob.c
@@ -27,10 +27,105 @@
|
|||||||
|
|
||||||
#define PY_SSIZE_T_CLEAN
|
#define PY_SSIZE_T_CLEAN
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
#include "diff.h"
|
||||||
|
#include "error.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include "blob.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.");
|
PyDoc_STRVAR(Blob_size__doc__, "Size.");
|
||||||
|
|
||||||
@@ -94,7 +189,7 @@ PyTypeObject BlobType = {
|
|||||||
0, /* tp_weaklistoffset */
|
0, /* tp_weaklistoffset */
|
||||||
0, /* tp_iter */
|
0, /* tp_iter */
|
||||||
0, /* tp_iternext */
|
0, /* tp_iternext */
|
||||||
0, /* tp_methods */
|
Blob_methods, /* tp_methods */
|
||||||
0, /* tp_members */
|
0, /* tp_members */
|
||||||
Blob_getseters, /* tp_getset */
|
Blob_getseters, /* tp_getset */
|
||||||
0, /* tp_base */
|
0, /* tp_base */
|
||||||
|
|||||||
74
src/diff.c
74
src/diff.c
@@ -57,27 +57,24 @@ wrap_diff(git_diff *diff, Repository *repo)
|
|||||||
return (PyObject*) py_diff;
|
return (PyObject*) py_diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject*
|
PyObject *
|
||||||
diff_get_patch_byindex(git_diff* diff, size_t idx)
|
wrap_patch(git_patch *patch)
|
||||||
{
|
{
|
||||||
const git_diff_delta* delta;
|
Patch *py_patch;
|
||||||
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;
|
|
||||||
|
|
||||||
err = git_patch_from_diff(&patch, diff, idx);
|
if (!patch)
|
||||||
if (err < 0)
|
Py_RETURN_NONE;
|
||||||
return Error_set(err);
|
|
||||||
|
|
||||||
delta = git_patch_get_delta(patch);
|
|
||||||
|
|
||||||
py_patch = PyObject_New(Patch, &PatchType);
|
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->old_file_path = delta->old_file.path;
|
||||||
py_patch->new_file_path = delta->new_file.path;
|
py_patch->new_file_path = delta->new_file.path;
|
||||||
py_patch->status = git_diff_status_char(delta->status);
|
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);
|
hunk_amounts = git_patch_num_hunks(patch);
|
||||||
py_patch->hunks = PyList_New(hunk_amounts);
|
py_patch->hunks = PyList_New(hunk_amounts);
|
||||||
for (i=0; i < hunk_amounts; ++i) {
|
for (i = 0; i < hunk_amounts; ++i) {
|
||||||
err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, i);
|
Hunk *py_hunk = NULL;
|
||||||
|
|
||||||
|
err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, i);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto cleanup;
|
return Error_set(err);
|
||||||
|
|
||||||
py_hunk = PyObject_New(Hunk, &HunkType);
|
py_hunk = PyObject_New(Hunk, &HunkType);
|
||||||
if (py_hunk != NULL) {
|
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->new_lines = hunk->new_lines;
|
||||||
|
|
||||||
py_hunk->lines = PyList_New(lines_in_hunk);
|
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);
|
err = git_patch_get_line_in_hunk(&line, patch, i, j);
|
||||||
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto cleanup;
|
return Error_set(err);
|
||||||
|
|
||||||
py_line_origin = to_unicode_n(&line->origin, 1, NULL, NULL);
|
py_line_origin = to_unicode_n(&line->origin, 1,
|
||||||
py_line = to_unicode_n(line->content, line->content_len, NULL, NULL);
|
NULL, NULL);
|
||||||
|
py_line = to_unicode_n(line->content, line->content_len,
|
||||||
|
NULL, NULL);
|
||||||
PyList_SetItem(py_hunk->lines, j,
|
PyList_SetItem(py_hunk->lines, j,
|
||||||
Py_BuildValue("OO",
|
Py_BuildValue("OO", py_line_origin, py_line));
|
||||||
py_line_origin,
|
|
||||||
py_line
|
|
||||||
)
|
|
||||||
);
|
|
||||||
Py_DECREF(py_line_origin);
|
Py_DECREF(py_line_origin);
|
||||||
Py_DECREF(py_line);
|
Py_DECREF(py_line);
|
||||||
}
|
}
|
||||||
@@ -130,10 +128,20 @@ diff_get_patch_byindex(git_diff* diff, size_t idx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
return (PyObject*) py_patch;
|
||||||
git_patch_free(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
|
static void
|
||||||
|
|||||||
@@ -42,5 +42,6 @@ PyObject* Diff_changes(Diff *self);
|
|||||||
PyObject* Diff_patch(Diff *self);
|
PyObject* Diff_patch(Diff *self);
|
||||||
|
|
||||||
PyObject* wrap_diff(git_diff *diff, Repository *repo);
|
PyObject* wrap_diff(git_diff *diff, Repository *repo);
|
||||||
|
PyObject* wrap_patch(git_patch *patch);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -105,5 +105,16 @@ class BlobTest(utils.RepoTestCase):
|
|||||||
self.assertTrue(isinstance(blob, pygit2.Blob))
|
self.assertTrue(isinstance(blob, pygit2.Blob))
|
||||||
self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type)
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user