Merge remote-tracking branch 'carlos/diff-stats'

This commit is contained in:
J. David Ibáñez 2015-04-29 10:47:21 +02:00
commit d63c2d4fd7
5 changed files with 198 additions and 0 deletions

@ -23,6 +23,10 @@ Examples
>>> diff = repo.diff('HEAD^', 'HEAD~3')
>>> patches = [p for p in diff]
# Get the stats for a diff
>>> diff = repo.diff('HEAD^', 'HEAD~3')
>>> diff.stats
# Diffing the empty tree
>>> tree = revparse_single('HEAD').tree
>>> tree.diff_to_tree()
@ -89,3 +93,11 @@ The DiffHunk type
.. autoattribute:: pygit2.DiffHunk.new_start
.. autoattribute:: pygit2.DiffHunk.new_lines
.. autoattribute:: pygit2.DiffHunk.lines
The DiffStats type
====================
.. autoattribute :: pygit2.DiffStats.insertions
.. autoattribute :: pygit2.DiffStats.deletions
.. autoattribute :: pygit2.DiffStats.files_changed
.. automethod :: pygit2.DiffStats.format

@ -44,6 +44,7 @@ extern PyTypeObject DiffDeltaType;
extern PyTypeObject DiffFileType;
extern PyTypeObject DiffHunkType;
extern PyTypeObject DiffLineType;
extern PyTypeObject DiffStatsType;
extern PyTypeObject RepositoryType;
PyObject *
@ -143,6 +144,28 @@ wrap_diff_hunk(git_patch *patch, size_t idx)
return (PyObject *) py_hunk;
}
PyObject *
wrap_diff_stats(git_diff *diff)
{
git_diff_stats *stats;
DiffStats *py_stats;
int err;
err = git_diff_get_stats(&stats, diff);
if (err < 0)
return Error_set(err);
py_stats = PyObject_New(DiffStats, &DiffStatsType);
if (!py_stats) {
git_diff_stats_free(stats);
return NULL;
}
py_stats->stats = stats;
return (PyObject *) py_stats;
}
PyObject *
wrap_diff_line(const git_diff_line *line)
{
@ -558,6 +581,132 @@ PyTypeObject DiffHunkType = {
0, /* tp_new */
};
PyDoc_STRVAR(DiffStats_insertions__doc__, "Total number of insertions");
PyObject *
DiffStats_insertions__get__(DiffStats *self)
{
return PyLong_FromSize_t(git_diff_stats_insertions(self->stats));
}
PyDoc_STRVAR(DiffStats_deletions__doc__, "Total number of deletions");
PyObject *
DiffStats_deletions__get__(DiffStats *self)
{
return PyLong_FromSize_t(git_diff_stats_deletions(self->stats));
}
PyDoc_STRVAR(DiffStats_files_changed__doc__, "Total number of files changed");
PyObject *
DiffStats_files_changed__get__(DiffStats *self)
{
return PyLong_FromSize_t(git_diff_stats_files_changed(self->stats));
}
PyDoc_STRVAR(DiffStats_format__doc__,
"format(format, width)-> str\n"
"\n"
"Format the stats as a string\n"
"\n"
"Arguments:\n"
"\n"
"format\n"
" The format to use. A pygit2.GIT_DIFF_STATS_* constant\n"
"\n"
"width\n"
" The width of the output. The output will be scaled to fit.");
PyObject *
DiffStats_format(DiffStats *self, PyObject *args, PyObject *kwds)
{
int err, format;
git_buf buf = { 0 };
Py_ssize_t width;
PyObject *str;
char *keywords[] = {"format", "width", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "in", keywords, &format, &width))
return NULL;
if (width <= 0) {
PyErr_SetString(PyExc_ValueError, "width must be positive");
return NULL;
}
err = git_diff_stats_to_buf(&buf, self->stats, format, width);
if (err < 0)
return Error_set(err);
str = to_unicode(buf.ptr, NULL, NULL);
git_buf_free(&buf);
return str;
}
static void
DiffStats_dealloc(DiffStats *self)
{
git_diff_stats_free(self->stats);
PyObject_Del(self);
}
PyMethodDef DiffStats_methods[] = {
METHOD(DiffStats, format, METH_VARARGS | METH_KEYWORDS),
{NULL}
};
PyGetSetDef DiffStats_getseters[] = {
GETTER(DiffStats, insertions),
GETTER(DiffStats, deletions),
GETTER(DiffStats, files_changed),
{NULL}
};
PyDoc_STRVAR(DiffStats__doc__, "DiffStats object.");
PyTypeObject DiffStatsType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.DiffStats", /* tp_name */
sizeof(DiffStats), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)DiffStats_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 */
DiffStats__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
DiffStats_methods, /* tp_methods */
0, /* tp_members */
DiffStats_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 */
};
PyDoc_STRVAR(Diff_from_c__doc__, "Method exposed for Index to hook into");
PyObject *
@ -662,6 +811,13 @@ Diff_getitem(Diff *self, PyObject *value)
return diff_get_patch_byindex(self->diff, i);
}
PyDoc_STRVAR(Diff_stats__doc__, "Accumulate diff statistics for all patches");
PyObject *
Diff_stats__get__(Diff *self)
{
return wrap_diff_stats(self->diff);
}
static void
Diff_dealloc(Diff *self)
@ -673,6 +829,7 @@ Diff_dealloc(Diff *self)
PyGetSetDef Diff_getseters[] = {
GETTER(Diff, patch),
GETTER(Diff, stats),
{NULL}
};

@ -48,6 +48,7 @@ extern PyTypeObject DiffDeltaType;
extern PyTypeObject DiffFileType;
extern PyTypeObject DiffHunkType;
extern PyTypeObject DiffLineType;
extern PyTypeObject DiffStatsType;
extern PyTypeObject PatchType;
extern PyTypeObject TreeType;
extern PyTypeObject TreeBuilderType;
@ -294,12 +295,14 @@ moduleinit(PyObject* m)
INIT_TYPE(DiffFileType, NULL, NULL)
INIT_TYPE(DiffHunkType, NULL, NULL)
INIT_TYPE(DiffLineType, NULL, NULL)
INIT_TYPE(DiffStatsType, 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, DiffStats)
ADD_TYPE(m, Patch)
ADD_CONSTANT_INT(m, GIT_DIFF_NORMAL)
ADD_CONSTANT_INT(m, GIT_DIFF_REVERSE)
@ -322,6 +325,11 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE)
ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE_TREES)
ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_IGNORED_DIRS)
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_NONE)
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_FULL)
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_SHORT)
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_NUMBER)
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_INCLUDE_SUMMARY)
/* Flags for diff find similar */
/* --find-renames */
ADD_CONSTANT_INT(m, GIT_DIFF_FIND_RENAMES)

@ -146,6 +146,8 @@ typedef struct {
PyObject *content;
} DiffLine;
SIMPLE_TYPE(DiffStats, git_diff_stats, stats);
/* git_tree_walk , git_treebuilder*/
SIMPLE_TYPE(TreeBuilder, git_treebuilder, bld)

@ -101,6 +101,11 @@ HUNK_EXPECTED = """- a contents 2
+ a contents
"""
STATS_EXPECTED = """ a | 2 +-
c/d | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
delete mode 100644 c/d
"""
class DiffDirtyTest(utils.DirtyRepoTestCase):
def test_diff_empty_index(self):
@ -289,5 +294,19 @@ class DiffTest(utils.BareRepoTestCase):
self.assertAny(lambda x: x.delta.status == GIT_DELTA_RENAMED, diff)
self.assertAny(lambda x: x.delta.status_char() == 'R', diff)
def test_diff_stats(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)
stats = diff.stats
self.assertEqual(1, stats.insertions)
self.assertEqual(2, stats.deletions)
self.assertEqual(2, stats.files_changed)
formatted = stats.format(format=pygit2.GIT_DIFF_STATS_FULL |
pygit2.GIT_DIFF_STATS_INCLUDE_SUMMARY,
width=80)
self.assertEqual(STATS_EXPECTED, formatted)
if __name__ == '__main__':
unittest.main()