New DiffDelta and DiffFile

Comes from PR #346
This commit is contained in:
J. David Ibáñez
2015-01-29 18:34:47 +01:00
parent 8881b75aaa
commit 909e03d8fc
7 changed files with 258 additions and 67 deletions

View File

@@ -31,8 +31,20 @@ Changelog
- Make pygit work in a frozen environment
`#453 <https://github.com/libgit2/pygit2/pull/453>`_
- New ``pygit2.DiffDelta`` and ``pygit2.DiffFile``
- Rename ``pygit2.Hunk`` to ``pygit2.DiffHunk``
API changes::
Patch.old_file_path => Patch.delta.old_file.path
Patch.new_file_path => Patch.delta.new_file.path
Patch.old_id => Patch.delta.old_file.id
Patch.new_id => Patch.delta.new_file.id
Patch.status => Patch.delta.status
Patch.similarity => Patch.delta.similarity
Patch.is_binary => Patch.delta.is_binary
0.22.0 (2015-01-16)
-------------------

View File

@@ -40,6 +40,8 @@ extern PyObject *GitError;
extern PyTypeObject TreeType;
extern PyTypeObject IndexType;
extern PyTypeObject DiffType;
extern PyTypeObject DiffDeltaType;
extern PyTypeObject DiffFileType;
extern PyTypeObject DiffHunkType;
extern PyTypeObject RepositoryType;
@@ -58,6 +60,180 @@ wrap_diff(git_diff *diff, Repository *repo)
return (PyObject*) py_diff;
}
PyObject *
wrap_diff_file(const git_diff_file *file)
{
DiffFile *py_file;
if (!file)
Py_RETURN_NONE;
py_file = PyObject_New(DiffFile, &DiffFileType);
if (py_file) {
py_file->id = git_oid_to_python(&file->id);
py_file->path = file->path != NULL ? strdup(file->path) : NULL;
}
return (PyObject *) py_file;
}
PyObject *
wrap_diff_delta(const git_diff_delta *delta)
{
DiffDelta *py_delta;
if (!delta)
Py_RETURN_NONE;
py_delta = PyObject_New(DiffDelta, &DiffDeltaType);
if (py_delta) {
py_delta->status = git_diff_status_char(delta->status);
py_delta->flags = delta->flags;
py_delta->similarity = delta->similarity;
py_delta->nfiles = delta->nfiles;
py_delta->old_file = wrap_diff_file(&delta->old_file);
py_delta->new_file = wrap_diff_file(&delta->new_file);
}
return (PyObject *) py_delta;
}
static void
DiffFile_dealloc(DiffFile *self)
{
Py_CLEAR(self->id);
if (self->path)
free(self->path);
PyObject_Del(self);
}
PyMemberDef DiffFile_members[] = {
MEMBER(DiffFile, id, T_OBJECT, "Oid of the item."),
MEMBER(DiffFile, path, T_STRING, "Path to the entry."),
{NULL}
};
PyDoc_STRVAR(DiffFile__doc__, "DiffFile object.");
PyTypeObject DiffFileType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.DiffFile", /* tp_name */
sizeof(DiffFile), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)DiffFile_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, /* tp_flags */
DiffFile__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
DiffFile_members, /* tp_members */
0, /* 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 */
};
PyDoc_STRVAR(DiffDelta_is_binary__doc__, "True if binary data, False if not.");
PyObject *
DiffDelta_is_binary__get__(DiffDelta *self)
{
if (!(self->flags & GIT_DIFF_FLAG_NOT_BINARY) &&
(self->flags & GIT_DIFF_FLAG_BINARY))
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
static void
DiffDelta_dealloc(DiffDelta *self)
{
Py_CLEAR(self->old_file);
Py_CLEAR(self->new_file);
PyObject_Del(self);
}
PyMemberDef DiffDelta_members[] = {
MEMBER(DiffDelta, status, T_CHAR, "A GIT_DELTA_* constant."),
MEMBER(DiffDelta, flags, T_UINT, "Combination of GIT_DIFF_FLAG_* flags."),
MEMBER(DiffDelta, similarity, T_USHORT, "For renamed and copied."),
MEMBER(DiffDelta, nfiles, T_USHORT, "Number of files in the delta."),
MEMBER(DiffDelta, old_file, T_OBJECT, "\"from\" side of the diff."),
MEMBER(DiffDelta, new_file, T_OBJECT, "\"to\" side of the diff."),
{NULL}
};
PyGetSetDef DiffDelta_getseters[] = {
GETTER(DiffDelta, is_binary),
{NULL}
};
PyDoc_STRVAR(DiffDelta__doc__, "DiffDelta object.");
PyTypeObject DiffDeltaType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.DiffDelta", /* tp_name */
sizeof(DiffDelta), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)DiffDelta_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, /* tp_flags */
DiffDelta__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
DiffDelta_members, /* tp_members */
DiffDelta_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 */
};
PyObject *
diff_get_patch_byindex(git_diff *diff, size_t idx)
{

View File

@@ -37,5 +37,7 @@ PyObject* Diff_changes(Diff *self);
PyObject* Diff_patch(Diff *self);
PyObject* wrap_diff(git_diff *diff, Repository *repo);
PyObject* wrap_diff_file(const git_diff_file *file);
PyObject* wrap_diff_delta(const git_diff_delta *delta);
#endif

View File

@@ -28,6 +28,7 @@
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include "diff.h"
#include "error.h"
#include "oid.h"
#include "types.h"
@@ -53,15 +54,9 @@ wrap_patch(git_patch *patch)
const git_diff_line *line;
int err;
delta = git_patch_get_delta(patch);
py_patch->patch = patch;
py_patch->old_file_path = strdup(delta->old_file.path);
py_patch->new_file_path = strdup(delta->new_file.path);
py_patch->status = git_diff_status_char(delta->status);
py_patch->similarity = delta->similarity;
py_patch->flags = delta->flags;
py_patch->old_id = git_oid_to_python(&delta->old_file.id);
py_patch->new_id = git_oid_to_python(&delta->new_file.id);
delta = git_patch_get_delta(patch);
git_patch_line_stats(NULL, &additions, &deletions, patch);
py_patch->additions = additions;
@@ -107,7 +102,6 @@ wrap_patch(git_patch *patch)
}
}
}
git_patch_free(patch);
return (PyObject*) py_patch;
}
@@ -116,39 +110,30 @@ static void
Patch_dealloc(Patch *self)
{
Py_CLEAR(self->hunks);
Py_CLEAR(self->old_id);
Py_CLEAR(self->new_id);
free(self->old_file_path);
free(self->new_file_path);
git_patch_free(self->patch);
PyObject_Del(self);
}
PyDoc_STRVAR(Patch_delta__doc__, "Get the delta associated with a patch.");
PyObject *
Patch_delta__get__(Patch *self)
{
if (!self->patch)
Py_RETURN_NONE;
return wrap_diff_delta(git_patch_get_delta(self->patch));
}
PyMemberDef Patch_members[] = {
MEMBER(Patch, old_file_path, T_STRING, "old file path"),
MEMBER(Patch, new_file_path, T_STRING, "new file path"),
MEMBER(Patch, old_id, T_OBJECT, "old oid"),
MEMBER(Patch, new_id, T_OBJECT, "new oid"),
MEMBER(Patch, status, T_CHAR, "status"),
MEMBER(Patch, similarity, T_INT, "similarity"),
MEMBER(Patch, hunks, T_OBJECT, "hunks"),
MEMBER(Patch, additions, T_INT, "additions"),
MEMBER(Patch, deletions, T_INT, "deletions"),
{NULL}
};
PyDoc_STRVAR(Patch_is_binary__doc__, "True if binary data, False if not.");
PyObject *
Patch_is_binary__get__(Patch *self)
{
if (!(self->flags & GIT_DIFF_FLAG_NOT_BINARY) &&
(self->flags & GIT_DIFF_FLAG_BINARY))
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
PyGetSetDef Patch_getseters[] = {
GETTER(Patch, is_binary),
GETTER(Patch, delta),
{NULL}
};

View File

@@ -44,8 +44,10 @@ extern PyTypeObject ObjectType;
extern PyTypeObject CommitType;
extern PyTypeObject DiffType;
extern PyTypeObject DiffIterType;
extern PyTypeObject PatchType;
extern PyTypeObject DiffDeltaType;
extern PyTypeObject DiffFileType;
extern PyTypeObject DiffHunkType;
extern PyTypeObject PatchType;
extern PyTypeObject TreeType;
extern PyTypeObject TreeBuilderType;
extern PyTypeObject TreeEntryType;
@@ -287,11 +289,15 @@ moduleinit(PyObject* m)
*/
INIT_TYPE(DiffType, NULL, NULL)
INIT_TYPE(DiffIterType, NULL, NULL)
INIT_TYPE(PatchType, NULL, NULL)
INIT_TYPE(DiffDeltaType, NULL, NULL)
INIT_TYPE(DiffFileType, NULL, NULL)
INIT_TYPE(DiffHunkType, NULL, NULL)
INIT_TYPE(PatchType, NULL, NULL)
ADD_TYPE(m, Diff)
ADD_TYPE(m, Patch)
ADD_TYPE(m, DiffDelta)
ADD_TYPE(m, DiffFile)
ADD_TYPE(m, DiffHunk)
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)

View File

@@ -90,6 +90,14 @@ typedef struct {
char* ref;
} NoteIter;
/* git_patch */
typedef struct {
PyObject_HEAD
git_patch *patch;
PyObject* hunks;
unsigned additions;
unsigned deletions;
} Patch;
/* git_diff */
SIMPLE_TYPE(Diff, git_diff, diff)
@@ -103,17 +111,19 @@ typedef struct {
typedef struct {
PyObject_HEAD
PyObject* hunks;
char * old_file_path;
char * new_file_path;
PyObject* old_id;
PyObject* new_id;
char status;
unsigned similarity;
unsigned additions;
unsigned deletions;
unsigned flags;
} Patch;
PyObject *id;
char *path;
} DiffFile;
typedef struct {
PyObject_HEAD
git_delta_t status;
uint32_t flags;
uint16_t similarity;
uint16_t nfiles;
PyObject *old_file;
PyObject *new_file;
} DiffDelta;
typedef struct {
PyObject_HEAD

View File

@@ -107,11 +107,11 @@ class DiffDirtyTest(utils.DirtyRepoTestCase):
head = repo[repo.lookup_reference('HEAD').resolve().target]
diff = head.tree.diff_to_index(repo.index)
files = [patch.new_file_path for patch in diff]
files = [patch.delta.new_file.path for patch in diff]
self.assertEqual(DIFF_HEAD_TO_INDEX_EXPECTED, files)
diff = repo.diff('HEAD', cached=True)
files = [patch.new_file_path for patch in diff]
files = [patch.delta.new_file.path for patch in diff]
self.assertEqual(DIFF_HEAD_TO_INDEX_EXPECTED, files)
def test_workdir_to_tree(self):
@@ -119,16 +119,16 @@ class DiffDirtyTest(utils.DirtyRepoTestCase):
head = repo[repo.lookup_reference('HEAD').resolve().target]
diff = head.tree.diff_to_workdir()
files = [patch.new_file_path for patch in diff]
files = [patch.delta.new_file.path for patch in diff]
self.assertEqual(DIFF_HEAD_TO_WORKDIR_EXPECTED, files)
diff = repo.diff('HEAD')
files = [patch.new_file_path for patch in diff]
files = [patch.delta.new_file.path for patch in diff]
self.assertEqual(DIFF_HEAD_TO_WORKDIR_EXPECTED, files)
def test_index_to_workdir(self):
diff = self.repo.diff()
files = [patch.new_file_path for patch in diff]
files = [patch.delta.new_file.path for patch in diff]
self.assertEqual(DIFF_INDEX_TO_WORK_EXPECTED, files)
@@ -145,15 +145,15 @@ class DiffTest(utils.BareRepoTestCase):
head = repo[repo.lookup_reference('HEAD').resolve().target]
diff = self.repo.index.diff_to_tree(head.tree)
files = [patch.new_file_path.split('/')[0] for patch in diff]
files = [patch.delta.new_file.path.split('/')[0] for patch in diff]
self.assertEqual([x.name for x in head.tree], files)
diff = head.tree.diff_to_index(repo.index)
files = [patch.new_file_path.split('/')[0] for patch in diff]
files = [patch.delta.new_file.path.split('/')[0] for patch in diff]
self.assertEqual([x.name for x in head.tree], files)
diff = repo.diff('HEAD', cached=True)
files = [patch.new_file_path.split('/')[0] for patch in diff]
files = [patch.delta.new_file.path.split('/')[0] for patch in diff]
self.assertEqual([x.name for x in head.tree], files)
def test_diff_tree(self):
@@ -173,9 +173,9 @@ class DiffTest(utils.BareRepoTestCase):
self.assertEqual(hunk.new_start, 1)
self.assertEqual(hunk.new_lines, 1)
self.assertEqual(patch.old_file_path, 'a')
self.assertEqual(patch.new_file_path, 'a')
self.assertEqual(patch.is_binary, False)
self.assertEqual(patch.delta.old_file.path, 'a')
self.assertEqual(patch.delta.new_file.path, 'a')
self.assertEqual(patch.delta.is_binary, False)
_test(commit_a.tree.diff_to_tree(commit_b.tree))
_test(self.repo.diff(COMMIT_SHA1_1, COMMIT_SHA1_2))
@@ -190,12 +190,12 @@ class DiffTest(utils.BareRepoTestCase):
lines = chain(*map(lambda x: x.lines, hunks))
return map(lambda x: x[0], lines)
entries = [p.new_file_path for p in diff]
entries = [p.delta.new_file.path for p 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.new_file_path for p in diff_swaped]
entries = [p.delta.new_file.path for p in diff_swaped]
self.assertAll(lambda x: commit_a.tree[x], entries)
self.assertAll(lambda x: '+' == x, get_context_for_lines(diff_swaped))
@@ -231,13 +231,13 @@ class DiffTest(utils.BareRepoTestCase):
self.assertTrue(diff_c is not None)
# assertIn / assertNotIn are 2.7 only
self.assertFalse('b' in [patch.new_file_path for patch in diff_b])
self.assertTrue('b' in [patch.new_file_path for patch in diff_c])
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])
diff_b.merge(diff_c)
# assertIn is 2.7 only
self.assertTrue('b' in [patch.new_file_path for patch in diff_b])
self.assertTrue('b' in [patch.delta.new_file.path for patch in diff_b])
patch = diff_b[0]
hunk = patch.hunks[0]
@@ -246,8 +246,8 @@ class DiffTest(utils.BareRepoTestCase):
self.assertEqual(hunk.new_start, 1)
self.assertEqual(hunk.new_lines, 1)
self.assertEqual(patch.old_file_path, 'a')
self.assertEqual(patch.new_file_path, 'a')
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]
@@ -261,9 +261,9 @@ class DiffTest(utils.BareRepoTestCase):
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.old_id.hex,
self.assertEqual(patch.delta.old_file.id.hex,
'7f129fd57e31e935c6d60a0c794efe4e6927664b')
self.assertEqual(patch.new_id.hex,
self.assertEqual(patch.delta.new_file.id.hex,
'af431f20fc541ed6d5afede3e2dc7160f6f01f16')
def test_hunk_content(self):
@@ -282,9 +282,9 @@ 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.status != 'R', diff)
self.assertAll(lambda x: x.delta.status != 'R', diff)
diff.find_similar()
self.assertAny(lambda x: x.status == 'R', diff)
self.assertAny(lambda x: x.delta.status == 'R', diff)
if __name__ == '__main__':
unittest.main()