Diff and Patch interface refactored
This commit is contained in:
committed by
J. David Ibáñez
parent
da98890bd1
commit
a3cb12e928
94
src/blob.c
94
src/blob.c
@@ -28,106 +28,14 @@
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
#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 */
|
||||
|
||||
306
src/diff.c
306
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 */
|
||||
|
||||
@@ -33,13 +33,10 @@
|
||||
#include <git2.h>
|
||||
#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
|
||||
|
||||
347
src/patch.c
347
src/patch.c
@@ -30,13 +30,18 @@
|
||||
#include <structmember.h>
|
||||
#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 */
|
||||
|
||||
24
src/pygit2.c
24
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)
|
||||
|
||||
27
src/types.h
27
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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
93
test/test_patch.py
Normal file
93
test/test_patch.py
Normal file
@@ -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()
|
||||
@@ -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):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user