Add DiffStats
This wraps git_diff_stats and can be retrieved through a Diff. It includes a formatting method.
This commit is contained in:
parent
3091c7aa87
commit
42d81e33ec
@ -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
|
||||
|
157
src/diff.c
157
src/diff.c
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user