support for diff against workdir or index

Added missing support for a diff against workdir or an index.
The latter one differs from `git diff HEAD` because git itself
does a merge of workdir and index (see docs of git_diff_workdir_to_tree).
This commit is contained in:
Nico von Geyso 2012-05-30 07:50:33 +02:00
parent d14393c1df
commit ade2e5812c
5 changed files with 195 additions and 77 deletions
include/pygit2
src/pygit2
test

@ -6,6 +6,11 @@
#include <git2.h>
#include <pygit2/types.h>
#define DIFF_CHECK_TYPES(_x, _y, _type_x, _type_y) \
PyObject_TypeCheck(_x, _type_x) && \
PyObject_TypeCheck(_y, _type_y)
PyObject* Diff_changes(Diff *self);
PyObject* Diff_patch(Diff *self);

@ -38,8 +38,8 @@ typedef struct {
typedef struct {
PyObject_HEAD
Tree *t0;
Tree *t1;
PyObject *a;
PyObject *b;
} Diff;
typedef struct {

@ -6,9 +6,60 @@
#include <pygit2/utils.h>
#include <pygit2/diff.h>
extern PyObject *GitError;
extern PyTypeObject TreeType;
extern PyTypeObject IndexType;
extern PyTypeObject DiffType;
extern PyTypeObject HunkType;
int diff_get_list(Diff *diff, git_diff_options* opts, git_diff_list** list)
{
int err = -1;
if(diff->b == NULL) {
if(PyObject_TypeCheck(diff->a, &TreeType)) {
err = git_diff_workdir_to_tree(
((Tree*) diff->a)->repo->repo,
opts,
((Tree*) diff->a)->tree,
list
);
} else {
err = git_diff_workdir_to_index(
((Index*) diff->a)->repo->repo,
opts,
list
);
}
} else if(DIFF_CHECK_TYPES(diff->a, diff->b, &TreeType, &TreeType)) {
err = git_diff_tree_to_tree(
((Tree*) diff->a)->repo->repo,
opts,
((Tree*) diff->a)->tree,
((Tree*) diff->b)->tree,
list
);
} else if(DIFF_CHECK_TYPES(diff->a, diff->b, &IndexType, &TreeType)) {
err = git_diff_index_to_tree(
((Tree*) diff->b)->repo->repo,
opts,
((Tree*) diff->b)->tree,
list
);
} else if(DIFF_CHECK_TYPES(diff->a, diff->b, &TreeType, &IndexType)) {
err = git_diff_index_to_tree(
((Tree*) diff->a)->repo->repo,
opts,
((Tree*) diff->a)->tree,
list
);
}
return err;
}
static int diff_data_cb(
void *cb_data,
git_diff_delta *delta,
@ -23,12 +74,12 @@ static int diff_data_cb(
hunks = PyDict_GetItemString(cb_data, "hunks");
if(hunks == NULL)
return -1;
return -1;
size = PyList_Size(hunks);
hunk = (Hunk*) PyList_GetItem(hunks, size-1);
if(hunk == NULL)
return -1;
return -1;
tmp = PyBytes_FromStringAndSize(line, line_len);
@ -38,7 +89,7 @@ static int diff_data_cb(
if(usage != GIT_DIFF_LINE_ADDITION)
PyBytes_Concat(&hunk->old_data, tmp);
return 0;
return 0;
}
static int diff_hunk_cb(
@ -48,66 +99,77 @@ static int diff_hunk_cb(
const char *header,
size_t header_len)
{
PyObject *hunks;
Hunk *hunk;
PyObject *hunks;
Hunk *hunk;
hunks = PyDict_GetItemString(cb_data, "hunks");
if(hunks == NULL) {
hunks = PyList_New(0);
PyDict_SetItemString(cb_data, "hunks", hunks);
}
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 = (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;
hunk->old_start = range->old_start;
hunk->old_lines = range->old_lines;
hunk->new_start = range->new_start;
hunk->new_lines = range->new_lines;
int len;
char* old_path, *new_path;
int len;
char* old_path, *new_path;
len = strlen(delta->old_file.path) + 1;
old_path = malloc(sizeof(char) * len);
memcpy(old_path, delta->old_file.path, len);
hunk->old_file = old_path;
if(delta->old_file.path != NULL) {
len = strlen(delta->old_file.path) + 1;
old_path = malloc(sizeof(char) * len);
memcpy(old_path, delta->old_file.path, len);
hunk->old_file = old_path;
} else {
hunk->old_file = "";
}
len = strlen(delta->new_file.path) + 1;
new_path = malloc(sizeof(char) * len);
memcpy(new_path, delta->new_file.path, len);
hunk->new_file = new_path;
if(delta->new_file.path != NULL) {
len = strlen(delta->new_file.path) + 1;
new_path = malloc(sizeof(char) * len);
memcpy(new_path, delta->new_file.path, len);
hunk->new_file = new_path;
} else {
hunk->new_file = "";
}
#if PY_MAJOR_VERSION >= 3
hunk->old_data = Py_BuildValue("y", "");
hunk->new_data = Py_BuildValue("y", "");
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", "");
hunk->old_data = Py_BuildValue("s", "");
hunk->new_data = Py_BuildValue("s", "");
#endif
PyList_Append(hunks, (PyObject*) hunk);
PyList_Append(hunks, (PyObject*) hunk);
return 0;
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);
if(delta->old_file.path != NULL && delta->new_file.path != NULL) {
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);
}
file = Py_BuildValue("(s,s,i)",
delta->old_file.path,
delta->new_file.path,
delta->status
);
PyList_Append(files, file);
return 0;
}
@ -116,23 +178,18 @@ Diff_changes(Diff *self)
{
git_diff_options opts = {0};
git_diff_list *changes;
PyObject *payload;
int err;
err = git_diff_tree_to_tree(
self->t0->repo->repo,
&opts,
self->t0->tree,
self->t1->tree,
&changes);
err = diff_get_list(self, &opts, &changes);
if(err < 0) {
Error_set(err);
return NULL;
}
PyObject *payload;
payload = PyDict_New();
payload = PyDict_New();
git_diff_foreach(
changes,
payload,
@ -140,6 +197,7 @@ Diff_changes(Diff *self)
&diff_hunk_cb,
&diff_data_cb
);
git_diff_list_free(changes);
return payload;
@ -153,10 +211,10 @@ static int diff_print_cb(
const char *line,
size_t line_len)
{
PyObject *data = PyBytes_FromStringAndSize(line, line_len);
PyBytes_ConcatAndDel((PyObject**) cb_data, data);
PyObject *data = PyBytes_FromStringAndSize(line, line_len);
PyBytes_ConcatAndDel((PyObject**) cb_data, data);
return 0;
return 0;
}
@ -167,13 +225,7 @@ Diff_patch(Diff *self)
git_diff_list *changes;
int err;
err = git_diff_tree_to_tree(
self->t0->repo->repo,
&opts,
self->t0->tree,
self->t1->tree,
&changes);
err = diff_get_list(self, &opts, &changes);
if(err < 0) {
Error_set(err);
return NULL;
@ -197,14 +249,14 @@ Hunk_init(Hunk *self, PyObject *args, PyObject *kwds)
self->old_data = PyString_FromString("");
if (self->old_data == NULL) {
Py_DECREF(self);
return -1;
Py_XDECREF(self);
return -1;
}
self->new_data = PyString_FromString("");
if (self->new_data == NULL) {
Py_DECREF(self);
return -1;
Py_XDECREF(self);
return -1;
}
return 0;
@ -275,8 +327,8 @@ PyTypeObject HunkType = {
static void
Diff_dealloc(Diff *self)
{
Py_XDECREF(self->t0);
Py_XDECREF(self->t1);
Py_XDECREF(self->a);
Py_XDECREF(self->b);
PyObject_Del(self);
}

@ -9,6 +9,7 @@
extern PyTypeObject TreeType;
extern PyTypeObject DiffType;
extern PyTypeObject TreeIterType;
extern PyTypeObject IndexType;
void
TreeEntry_dealloc(TreeEntry *self)
@ -232,20 +233,26 @@ PyObject *
Tree_diff_tree(Tree *self, PyObject *args)
{
Diff *py_diff;
Tree *py_tree;
PyObject* py_obj = NULL;
if (!PyArg_ParseTuple(args, "O!", &TreeType, &py_tree)) {
if (!PyArg_ParseTuple(args, "|O", &py_obj))
return NULL;
if (py_obj != NULL && !PyObject_TypeCheck(py_obj, &TreeType) &&
!PyObject_TypeCheck(py_obj, &IndexType)) {
PyErr_SetObject(PyExc_TypeError, py_obj);
return NULL;
}
py_diff = PyObject_New(Diff, &DiffType);
if (py_diff) {
Py_INCREF(py_diff);
Py_INCREF(py_tree);
Py_INCREF(self);
Py_XINCREF(py_obj);
py_diff->t0 = self;
py_diff->t1 = py_tree;
py_diff->a = (PyObject*) self;
py_diff->b = (PyObject*) py_obj;
}
return (PyObject*) py_diff;
@ -269,8 +276,14 @@ PyMappingMethods Tree_as_mapping = {
};
PyMethodDef Tree_methods[] = {
{"diff", (PyCFunction)Tree_diff_tree, METH_VARARGS,
"Diff two trees."},
{
"diff", (PyCFunction)Tree_diff_tree, METH_VARARGS,
"Get changes between current tree instance with another tree, an "
"index or the working dir.\n\n"
"@param obj : if not given compare diff against working dir. "
"Possible valid arguments are instances of Tree or Index.\n"
"@returns Diff instance"
},
{NULL}
};

@ -39,6 +39,7 @@ __author__ = 'Nico.Geyso@FU-Berlin.de (Nico von Geyso)'
COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10'
COMMIT_SHA1_2 = 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c'
PATCH = b"""diff --git a/a b/a
index 7f129fd..af431f2 100644
--- a/a
@ -55,6 +56,45 @@ index 297efb8..0000000
-c/d contents
"""
DIFF_INDEX_EXPECTED = [
"staged_changes",
"staged_changes_file_deleted",
"staged_changes_file_modified",
"staged_delete",
"staged_delete_file_modified",
"staged_new",
"staged_new_file_deleted",
"staged_new_file_modified"
]
DIFF_WORKDIR_EXPECTED = [
'file_deleted',
'modified_file',
'staged_changes',
'staged_changes_file_deleted',
'staged_changes_file_modified',
'staged_delete',
'staged_delete_file_modified',
'subdir/deleted_file',
'subdir/modified_file'
]
class DiffDirtyTest(utils.DirtyRepoTestCase):
def test_diff_empty_index(self):
repo = self.repo
head = repo[repo.lookup_reference('HEAD').resolve().oid]
diff = head.tree.diff(repo.index)
files = [x[1] for x in diff.changes['files']]
self.assertEqual(DIFF_INDEX_EXPECTED, files)
def test_workdir_to_tree(self):
repo = self.repo
head = repo[repo.lookup_reference('HEAD').resolve().oid]
diff = head.tree.diff()
files = [x[1] for x in diff.changes['files']]
self.assertEqual(DIFF_WORKDIR_EXPECTED, files)
class DiffTest(utils.BareRepoTestCase):
@ -63,6 +103,14 @@ class DiffTest(utils.BareRepoTestCase):
commit_b = self.repo[COMMIT_SHA1_2]
self.assertRaises(TypeError, commit_a.tree.diff, commit_b)
def test_diff_empty_index(self):
repo = self.repo
head = repo[repo.lookup_reference('HEAD').resolve().oid]
diff = head.tree.diff(repo.index)
files = [x[0].split('/')[0] for x in diff.changes['files']]
self.assertEqual([x.name for x in head.tree], files)
def test_diff_tree(self):
commit_a = self.repo[COMMIT_SHA1_1]
commit_b = self.repo[COMMIT_SHA1_2]