basic diff implementation
It is now possible to get a diff between two trees: * new diff-method for Tree-Object * new Diff and Hunk Objects
This commit is contained in:
parent
6825bba8c0
commit
d62b8c590d
371
pygit2.c
371
pygit2.c
@ -30,6 +30,7 @@
|
||||
#include <Python.h>
|
||||
#include <osdefs.h>
|
||||
#include <git2.h>
|
||||
#include <structmember.h>
|
||||
|
||||
/* Python 3 support */
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
@ -111,6 +112,23 @@ OBJECT_STRUCT(Tag, git_tag, tag)
|
||||
OBJECT_STRUCT(Index, git_index, index)
|
||||
OBJECT_STRUCT(Walker, git_revwalk, walk)
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
Repository *repo;
|
||||
Tree *t0;
|
||||
Tree *t1;
|
||||
} Diff;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
int old_start;
|
||||
int old_lines;
|
||||
int new_start;
|
||||
int new_lines;
|
||||
PyObject *old_data;
|
||||
PyObject *new_data;
|
||||
} Hunk;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PyObject *owner; /* Tree or TreeBuilder */
|
||||
@ -149,6 +167,8 @@ typedef struct {
|
||||
static PyTypeObject RepositoryType;
|
||||
static PyTypeObject ObjectType;
|
||||
static PyTypeObject CommitType;
|
||||
static PyTypeObject DiffType;
|
||||
static PyTypeObject HunkType;
|
||||
static PyTypeObject TreeType;
|
||||
static PyTypeObject TreeBuilderType;
|
||||
static PyTypeObject TreeEntryType;
|
||||
@ -1697,6 +1717,337 @@ Tree_getitem(Tree *self, PyObject *value)
|
||||
return wrap_tree_entry(entry, self);
|
||||
}
|
||||
|
||||
static int diff_data_cb(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
char usage,
|
||||
const char *line,
|
||||
size_t line_len)
|
||||
{
|
||||
PyObject *hunks, *tmp;
|
||||
Hunk *hunk;
|
||||
Py_ssize_t size;
|
||||
|
||||
hunks = PyDict_GetItemString(cb_data, "hunks");
|
||||
if(hunks == NULL)
|
||||
return -1;
|
||||
|
||||
size = PyList_Size(hunks);
|
||||
hunk = (Hunk*) PyList_GetItem(hunks, size-1);
|
||||
if(hunk == NULL)
|
||||
return -1;
|
||||
|
||||
tmp = PyBytes_FromStringAndSize(line, line_len);
|
||||
|
||||
if(usage != GIT_DIFF_LINE_DELETION)
|
||||
PyBytes_Concat(&hunk->new_data, tmp);
|
||||
|
||||
if(usage != GIT_DIFF_LINE_ADDITION)
|
||||
PyBytes_Concat(&hunk->old_data, tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diff_hunk_cb(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
const char *header,
|
||||
size_t header_len)
|
||||
{
|
||||
PyObject *hunks, *entry;
|
||||
Hunk *hunk;
|
||||
|
||||
hunks = PyDict_GetItemString(cb_data, "hunks");
|
||||
if(hunks == NULL) {
|
||||
hunks = PyList_New(0);
|
||||
PyDict_SetItemString(cb_data, "hunks", hunks);
|
||||
}
|
||||
|
||||
hunk = (Hunk*) PyType_GenericNew(&HunkType, NULL, NULL);
|
||||
|
||||
hunk->old_start = range->old_start;
|
||||
hunk->old_lines = range->old_lines;
|
||||
hunk->new_start = range->new_start;
|
||||
hunk->new_lines = range->new_lines;
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
hunk->old_data = Py_BuildValue("y", "");
|
||||
hunk->new_data = Py_BuildValue("y", "");
|
||||
#else
|
||||
hunk->old_data = Py_BuildValue("s", "");
|
||||
hunk->new_data = Py_BuildValue("s", "");
|
||||
#endif
|
||||
|
||||
PyList_Append(hunks, (PyObject*) hunk);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int diff_file_cb(void *cb_data, git_diff_delta *delta, float progress)
|
||||
{
|
||||
PyObject *files, *file;
|
||||
|
||||
files = PyDict_GetItemString(cb_data, "files");
|
||||
if(files == NULL) {
|
||||
files = PyList_New(0);
|
||||
PyDict_SetItemString(cb_data, "files", files);
|
||||
}
|
||||
|
||||
file = Py_BuildValue("(s,s,i)",
|
||||
delta->old_file.path,
|
||||
delta->new_file.path,
|
||||
delta->status
|
||||
);
|
||||
|
||||
PyList_Append(files, file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
Diff_changes(Diff *self)
|
||||
{
|
||||
git_diff_options opts = {0};
|
||||
git_diff_list *changes;
|
||||
int err;
|
||||
|
||||
err = git_diff_tree_to_tree(
|
||||
self->repo->repo,
|
||||
&opts,
|
||||
self->t0->tree,
|
||||
self->t1->tree,
|
||||
&changes);
|
||||
|
||||
if(err < 0) {
|
||||
Error_set(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *payload;
|
||||
payload = PyDict_New();
|
||||
|
||||
git_diff_foreach(
|
||||
changes,
|
||||
payload,
|
||||
&diff_file_cb,
|
||||
&diff_hunk_cb,
|
||||
&diff_data_cb
|
||||
);
|
||||
git_diff_list_free(changes);
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
int diff_print_cb(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
char usage,
|
||||
const char *line,
|
||||
size_t line_len)
|
||||
{
|
||||
PyObject *data = PyBytes_FromStringAndSize(line, line_len);
|
||||
PyBytes_ConcatAndDel((PyObject**) cb_data, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
Diff_patch(Diff *self)
|
||||
{
|
||||
git_diff_options opts = {0};
|
||||
git_diff_list *changes;
|
||||
int err;
|
||||
|
||||
err = git_diff_tree_to_tree(
|
||||
self->repo->repo,
|
||||
&opts,
|
||||
self->t0->tree,
|
||||
self->t1->tree,
|
||||
&changes);
|
||||
|
||||
if(err < 0) {
|
||||
Error_set(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *patch = PyBytes_FromString("");
|
||||
|
||||
git_diff_print_patch(changes, &diff, &diff_print_cb);
|
||||
|
||||
return patch;
|
||||
}
|
||||
|
||||
static int
|
||||
Hunk_init(Hunk *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
self->old_start = 0;
|
||||
self->old_lines = 0;
|
||||
|
||||
self->new_start = 0;
|
||||
self->new_lines = 0;
|
||||
|
||||
self->old_data = PyString_FromString("");
|
||||
if (self->old_data == NULL) {
|
||||
Py_DECREF(self);
|
||||
return -1;
|
||||
}
|
||||
|
||||
self->new_data = PyString_FromString("");
|
||||
if (self->new_data == NULL) {
|
||||
Py_DECREF(self);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
Hunk_dealloc(Hunk *self)
|
||||
{
|
||||
Py_XDECREF(self->old_data);
|
||||
Py_XDECREF(self->new_data);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
static PyMemberDef Hunk_members[] = {
|
||||
{"old_start", T_INT, offsetof(Hunk, old_start), 0, "old start"},
|
||||
{"old_lines", T_INT, offsetof(Hunk, old_lines), 0, "old lines"},
|
||||
{"old_data", T_OBJECT, offsetof(Hunk, old_data), 0, "old data"},
|
||||
{"new_start", T_INT, offsetof(Hunk, new_start), 0, "new start"},
|
||||
{"new_lines", T_INT, offsetof(Hunk, new_lines), 0, "new lines"},
|
||||
{"new_data", T_OBJECT, offsetof(Hunk, new_data), 0, "new data"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static PyTypeObject HunkType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"pygit2.Hunk", /* tp_name */
|
||||
sizeof(Hunk), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)Hunk_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 */
|
||||
"Hunk object", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
Hunk_members, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
(initproc)Hunk_init, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
0, /* tp_new */
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
Diff_dealloc(Diff *self)
|
||||
{
|
||||
Py_XDECREF(self->repo);
|
||||
Py_XDECREF(self->t0);
|
||||
Py_XDECREF(self->t1);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
|
||||
static PyGetSetDef Diff_getseters[] = {
|
||||
{"changes", (getter)Diff_changes, NULL, "raw changes", NULL},
|
||||
{"patch", (getter)Diff_patch, NULL, "patch", NULL},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
||||
static PyTypeObject DiffType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"pygit2.Diff", /* tp_name */
|
||||
sizeof(Diff), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)Diff_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 */
|
||||
"Diff objects", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
Diff_getseters, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
0, /* tp_new */
|
||||
};
|
||||
|
||||
static PyObject *
|
||||
Tree_diff_tree(Tree *self, PyObject *args)
|
||||
{
|
||||
Diff *py_diff;
|
||||
Tree *py_tree;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O!", &TreeType, &py_tree)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
py_diff = PyObject_New(Diff, &DiffType);
|
||||
if (py_diff) {
|
||||
Py_INCREF(py_diff);
|
||||
Py_INCREF(self->repo);
|
||||
Py_INCREF(py_tree);
|
||||
Py_INCREF(self);
|
||||
|
||||
py_diff->repo = self->repo;
|
||||
py_diff->t0 = self;
|
||||
py_diff->t1 = py_tree;
|
||||
}
|
||||
|
||||
return (PyObject*) py_diff;
|
||||
}
|
||||
|
||||
static PySequenceMethods Tree_as_sequence = {
|
||||
0, /* sq_length */
|
||||
0, /* sq_concat */
|
||||
@ -1714,6 +2065,12 @@ static PyMappingMethods Tree_as_mapping = {
|
||||
0, /* mp_ass_subscript */
|
||||
};
|
||||
|
||||
static PyMethodDef Tree_methods[] = {
|
||||
{"diff", (PyCFunction)Tree_diff_tree, METH_VARARGS,
|
||||
"Diff two trees."},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static PyTypeObject TreeType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"pygit2.Tree", /* tp_name */
|
||||
@ -1742,7 +2099,7 @@ static PyTypeObject TreeType = {
|
||||
0, /* tp_weaklistoffset */
|
||||
(getiterfunc)Tree_iter, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
Tree_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
@ -3223,6 +3580,12 @@ moduleinit(PyObject* m)
|
||||
if (PyType_Ready(&TagType) < 0)
|
||||
return NULL;
|
||||
|
||||
DiffType.tp_base = &ObjectType;
|
||||
if (PyType_Ready(&DiffType) < 0)
|
||||
return NULL;
|
||||
HunkType.tp_base = &ObjectType;
|
||||
if (PyType_Ready(&HunkType) < 0)
|
||||
return NULL;
|
||||
TreeEntryType.tp_new = PyType_GenericNew;
|
||||
if (PyType_Ready(&TreeEntryType) < 0)
|
||||
return NULL;
|
||||
@ -3260,6 +3623,12 @@ moduleinit(PyObject* m)
|
||||
Py_INCREF(&TreeEntryType);
|
||||
PyModule_AddObject(m, "TreeEntry", (PyObject *)&TreeEntryType);
|
||||
|
||||
Py_INCREF(&DiffType);
|
||||
PyModule_AddObject(m, "Diff", (PyObject *)&DiffType);
|
||||
|
||||
Py_INCREF(&HunkType);
|
||||
PyModule_AddObject(m, "Hunk", (PyObject *)&HunkType);
|
||||
|
||||
Py_INCREF(&TreeType);
|
||||
PyModule_AddObject(m, "Tree", (PyObject *)&TreeType);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user