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:
@@ -6,6 +6,11 @@
|
|||||||
#include <git2.h>
|
#include <git2.h>
|
||||||
#include <pygit2/types.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_changes(Diff *self);
|
||||||
PyObject* Diff_patch(Diff *self);
|
PyObject* Diff_patch(Diff *self);
|
||||||
|
|
||||||
|
@@ -38,8 +38,8 @@ typedef struct {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
Tree *t0;
|
PyObject *a;
|
||||||
Tree *t1;
|
PyObject *b;
|
||||||
} Diff;
|
} Diff;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@@ -6,9 +6,60 @@
|
|||||||
#include <pygit2/utils.h>
|
#include <pygit2/utils.h>
|
||||||
#include <pygit2/diff.h>
|
#include <pygit2/diff.h>
|
||||||
|
|
||||||
|
extern PyObject *GitError;
|
||||||
|
|
||||||
|
extern PyTypeObject TreeType;
|
||||||
|
extern PyTypeObject IndexType;
|
||||||
extern PyTypeObject DiffType;
|
extern PyTypeObject DiffType;
|
||||||
extern PyTypeObject HunkType;
|
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(
|
static int diff_data_cb(
|
||||||
void *cb_data,
|
void *cb_data,
|
||||||
git_diff_delta *delta,
|
git_diff_delta *delta,
|
||||||
@@ -67,15 +118,23 @@ static int diff_hunk_cb(
|
|||||||
int len;
|
int len;
|
||||||
char* old_path, *new_path;
|
char* old_path, *new_path;
|
||||||
|
|
||||||
|
if(delta->old_file.path != NULL) {
|
||||||
len = strlen(delta->old_file.path) + 1;
|
len = strlen(delta->old_file.path) + 1;
|
||||||
old_path = malloc(sizeof(char) * len);
|
old_path = malloc(sizeof(char) * len);
|
||||||
memcpy(old_path, delta->old_file.path, len);
|
memcpy(old_path, delta->old_file.path, len);
|
||||||
hunk->old_file = old_path;
|
hunk->old_file = old_path;
|
||||||
|
} else {
|
||||||
|
hunk->old_file = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(delta->new_file.path != NULL) {
|
||||||
len = strlen(delta->new_file.path) + 1;
|
len = strlen(delta->new_file.path) + 1;
|
||||||
new_path = malloc(sizeof(char) * len);
|
new_path = malloc(sizeof(char) * len);
|
||||||
memcpy(new_path, delta->new_file.path, len);
|
memcpy(new_path, delta->new_file.path, len);
|
||||||
hunk->new_file = new_path;
|
hunk->new_file = new_path;
|
||||||
|
} else {
|
||||||
|
hunk->new_file = "";
|
||||||
|
}
|
||||||
|
|
||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
hunk->old_data = Py_BuildValue("y", "");
|
hunk->old_data = Py_BuildValue("y", "");
|
||||||
@@ -94,7 +153,9 @@ static int diff_file_cb(void *cb_data, git_diff_delta *delta, float progress)
|
|||||||
{
|
{
|
||||||
PyObject *files, *file;
|
PyObject *files, *file;
|
||||||
|
|
||||||
|
if(delta->old_file.path != NULL && delta->new_file.path != NULL) {
|
||||||
files = PyDict_GetItemString(cb_data, "files");
|
files = PyDict_GetItemString(cb_data, "files");
|
||||||
|
|
||||||
if(files == NULL) {
|
if(files == NULL) {
|
||||||
files = PyList_New(0);
|
files = PyList_New(0);
|
||||||
PyDict_SetItemString(cb_data, "files", files);
|
PyDict_SetItemString(cb_data, "files", files);
|
||||||
@@ -107,6 +168,7 @@ static int diff_file_cb(void *cb_data, git_diff_delta *delta, float progress)
|
|||||||
);
|
);
|
||||||
|
|
||||||
PyList_Append(files, file);
|
PyList_Append(files, file);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -116,21 +178,16 @@ Diff_changes(Diff *self)
|
|||||||
{
|
{
|
||||||
git_diff_options opts = {0};
|
git_diff_options opts = {0};
|
||||||
git_diff_list *changes;
|
git_diff_list *changes;
|
||||||
|
PyObject *payload;
|
||||||
int err;
|
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) {
|
if(err < 0) {
|
||||||
Error_set(err);
|
Error_set(err);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *payload;
|
|
||||||
payload = PyDict_New();
|
payload = PyDict_New();
|
||||||
|
|
||||||
git_diff_foreach(
|
git_diff_foreach(
|
||||||
@@ -140,6 +197,7 @@ Diff_changes(Diff *self)
|
|||||||
&diff_hunk_cb,
|
&diff_hunk_cb,
|
||||||
&diff_data_cb
|
&diff_data_cb
|
||||||
);
|
);
|
||||||
|
|
||||||
git_diff_list_free(changes);
|
git_diff_list_free(changes);
|
||||||
|
|
||||||
return payload;
|
return payload;
|
||||||
@@ -167,13 +225,7 @@ Diff_patch(Diff *self)
|
|||||||
git_diff_list *changes;
|
git_diff_list *changes;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = git_diff_tree_to_tree(
|
err = diff_get_list(self, &opts, &changes);
|
||||||
self->t0->repo->repo,
|
|
||||||
&opts,
|
|
||||||
self->t0->tree,
|
|
||||||
self->t1->tree,
|
|
||||||
&changes);
|
|
||||||
|
|
||||||
if(err < 0) {
|
if(err < 0) {
|
||||||
Error_set(err);
|
Error_set(err);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -197,13 +249,13 @@ Hunk_init(Hunk *self, PyObject *args, PyObject *kwds)
|
|||||||
|
|
||||||
self->old_data = PyString_FromString("");
|
self->old_data = PyString_FromString("");
|
||||||
if (self->old_data == NULL) {
|
if (self->old_data == NULL) {
|
||||||
Py_DECREF(self);
|
Py_XDECREF(self);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
self->new_data = PyString_FromString("");
|
self->new_data = PyString_FromString("");
|
||||||
if (self->new_data == NULL) {
|
if (self->new_data == NULL) {
|
||||||
Py_DECREF(self);
|
Py_XDECREF(self);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,8 +327,8 @@ PyTypeObject HunkType = {
|
|||||||
static void
|
static void
|
||||||
Diff_dealloc(Diff *self)
|
Diff_dealloc(Diff *self)
|
||||||
{
|
{
|
||||||
Py_XDECREF(self->t0);
|
Py_XDECREF(self->a);
|
||||||
Py_XDECREF(self->t1);
|
Py_XDECREF(self->b);
|
||||||
PyObject_Del(self);
|
PyObject_Del(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
extern PyTypeObject TreeType;
|
extern PyTypeObject TreeType;
|
||||||
extern PyTypeObject DiffType;
|
extern PyTypeObject DiffType;
|
||||||
extern PyTypeObject TreeIterType;
|
extern PyTypeObject TreeIterType;
|
||||||
|
extern PyTypeObject IndexType;
|
||||||
|
|
||||||
void
|
void
|
||||||
TreeEntry_dealloc(TreeEntry *self)
|
TreeEntry_dealloc(TreeEntry *self)
|
||||||
@@ -232,20 +233,26 @@ PyObject *
|
|||||||
Tree_diff_tree(Tree *self, PyObject *args)
|
Tree_diff_tree(Tree *self, PyObject *args)
|
||||||
{
|
{
|
||||||
Diff *py_diff;
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
py_diff = PyObject_New(Diff, &DiffType);
|
py_diff = PyObject_New(Diff, &DiffType);
|
||||||
if (py_diff) {
|
if (py_diff) {
|
||||||
Py_INCREF(py_diff);
|
Py_INCREF(py_diff);
|
||||||
Py_INCREF(py_tree);
|
|
||||||
Py_INCREF(self);
|
Py_INCREF(self);
|
||||||
|
Py_XINCREF(py_obj);
|
||||||
|
|
||||||
py_diff->t0 = self;
|
py_diff->a = (PyObject*) self;
|
||||||
py_diff->t1 = py_tree;
|
py_diff->b = (PyObject*) py_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (PyObject*) py_diff;
|
return (PyObject*) py_diff;
|
||||||
@@ -269,8 +276,14 @@ PyMappingMethods Tree_as_mapping = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
PyMethodDef Tree_methods[] = {
|
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}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -39,6 +39,7 @@ __author__ = 'Nico.Geyso@FU-Berlin.de (Nico von Geyso)'
|
|||||||
|
|
||||||
COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10'
|
COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10'
|
||||||
COMMIT_SHA1_2 = 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c'
|
COMMIT_SHA1_2 = 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c'
|
||||||
|
|
||||||
PATCH = b"""diff --git a/a b/a
|
PATCH = b"""diff --git a/a b/a
|
||||||
index 7f129fd..af431f2 100644
|
index 7f129fd..af431f2 100644
|
||||||
--- a/a
|
--- a/a
|
||||||
@@ -55,6 +56,45 @@ index 297efb8..0000000
|
|||||||
-c/d contents
|
-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):
|
class DiffTest(utils.BareRepoTestCase):
|
||||||
|
|
||||||
@@ -63,6 +103,14 @@ class DiffTest(utils.BareRepoTestCase):
|
|||||||
commit_b = self.repo[COMMIT_SHA1_2]
|
commit_b = self.repo[COMMIT_SHA1_2]
|
||||||
self.assertRaises(TypeError, commit_a.tree.diff, commit_b)
|
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):
|
def test_diff_tree(self):
|
||||||
commit_a = self.repo[COMMIT_SHA1_1]
|
commit_a = self.repo[COMMIT_SHA1_1]
|
||||||
commit_b = self.repo[COMMIT_SHA1_2]
|
commit_b = self.repo[COMMIT_SHA1_2]
|
||||||
|
Reference in New Issue
Block a user