Merge remote-tracking branch 'carlos/diff-stats'
This commit is contained in:
@@ -23,6 +23,10 @@ Examples
|
|||||||
>>> diff = repo.diff('HEAD^', 'HEAD~3')
|
>>> diff = repo.diff('HEAD^', 'HEAD~3')
|
||||||
>>> patches = [p for p in diff]
|
>>> patches = [p for p in diff]
|
||||||
|
|
||||||
|
# Get the stats for a diff
|
||||||
|
>>> diff = repo.diff('HEAD^', 'HEAD~3')
|
||||||
|
>>> diff.stats
|
||||||
|
|
||||||
# Diffing the empty tree
|
# Diffing the empty tree
|
||||||
>>> tree = revparse_single('HEAD').tree
|
>>> tree = revparse_single('HEAD').tree
|
||||||
>>> tree.diff_to_tree()
|
>>> tree.diff_to_tree()
|
||||||
@@ -89,3 +93,11 @@ The DiffHunk type
|
|||||||
.. autoattribute:: pygit2.DiffHunk.new_start
|
.. autoattribute:: pygit2.DiffHunk.new_start
|
||||||
.. autoattribute:: pygit2.DiffHunk.new_lines
|
.. autoattribute:: pygit2.DiffHunk.new_lines
|
||||||
.. autoattribute:: pygit2.DiffHunk.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 DiffFileType;
|
||||||
extern PyTypeObject DiffHunkType;
|
extern PyTypeObject DiffHunkType;
|
||||||
extern PyTypeObject DiffLineType;
|
extern PyTypeObject DiffLineType;
|
||||||
|
extern PyTypeObject DiffStatsType;
|
||||||
extern PyTypeObject RepositoryType;
|
extern PyTypeObject RepositoryType;
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
@@ -143,6 +144,28 @@ wrap_diff_hunk(git_patch *patch, size_t idx)
|
|||||||
return (PyObject *) py_hunk;
|
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 *
|
PyObject *
|
||||||
wrap_diff_line(const git_diff_line *line)
|
wrap_diff_line(const git_diff_line *line)
|
||||||
{
|
{
|
||||||
@@ -558,6 +581,132 @@ PyTypeObject DiffHunkType = {
|
|||||||
0, /* tp_new */
|
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");
|
PyDoc_STRVAR(Diff_from_c__doc__, "Method exposed for Index to hook into");
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
@@ -662,6 +811,13 @@ Diff_getitem(Diff *self, PyObject *value)
|
|||||||
return diff_get_patch_byindex(self->diff, i);
|
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
|
static void
|
||||||
Diff_dealloc(Diff *self)
|
Diff_dealloc(Diff *self)
|
||||||
@@ -673,6 +829,7 @@ Diff_dealloc(Diff *self)
|
|||||||
|
|
||||||
PyGetSetDef Diff_getseters[] = {
|
PyGetSetDef Diff_getseters[] = {
|
||||||
GETTER(Diff, patch),
|
GETTER(Diff, patch),
|
||||||
|
GETTER(Diff, stats),
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -48,6 +48,7 @@ extern PyTypeObject DiffDeltaType;
|
|||||||
extern PyTypeObject DiffFileType;
|
extern PyTypeObject DiffFileType;
|
||||||
extern PyTypeObject DiffHunkType;
|
extern PyTypeObject DiffHunkType;
|
||||||
extern PyTypeObject DiffLineType;
|
extern PyTypeObject DiffLineType;
|
||||||
|
extern PyTypeObject DiffStatsType;
|
||||||
extern PyTypeObject PatchType;
|
extern PyTypeObject PatchType;
|
||||||
extern PyTypeObject TreeType;
|
extern PyTypeObject TreeType;
|
||||||
extern PyTypeObject TreeBuilderType;
|
extern PyTypeObject TreeBuilderType;
|
||||||
@@ -294,12 +295,14 @@ moduleinit(PyObject* m)
|
|||||||
INIT_TYPE(DiffFileType, NULL, NULL)
|
INIT_TYPE(DiffFileType, NULL, NULL)
|
||||||
INIT_TYPE(DiffHunkType, NULL, NULL)
|
INIT_TYPE(DiffHunkType, NULL, NULL)
|
||||||
INIT_TYPE(DiffLineType, NULL, NULL)
|
INIT_TYPE(DiffLineType, NULL, NULL)
|
||||||
|
INIT_TYPE(DiffStatsType, NULL, NULL)
|
||||||
INIT_TYPE(PatchType, NULL, NULL)
|
INIT_TYPE(PatchType, NULL, NULL)
|
||||||
ADD_TYPE(m, Diff)
|
ADD_TYPE(m, Diff)
|
||||||
ADD_TYPE(m, DiffDelta)
|
ADD_TYPE(m, DiffDelta)
|
||||||
ADD_TYPE(m, DiffFile)
|
ADD_TYPE(m, DiffFile)
|
||||||
ADD_TYPE(m, DiffHunk)
|
ADD_TYPE(m, DiffHunk)
|
||||||
ADD_TYPE(m, DiffLine)
|
ADD_TYPE(m, DiffLine)
|
||||||
|
ADD_TYPE(m, DiffStats)
|
||||||
ADD_TYPE(m, Patch)
|
ADD_TYPE(m, Patch)
|
||||||
ADD_CONSTANT_INT(m, GIT_DIFF_NORMAL)
|
ADD_CONSTANT_INT(m, GIT_DIFF_NORMAL)
|
||||||
ADD_CONSTANT_INT(m, GIT_DIFF_REVERSE)
|
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)
|
||||||
ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE_TREES)
|
ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE_TREES)
|
||||||
ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_IGNORED_DIRS)
|
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 */
|
/* Flags for diff find similar */
|
||||||
/* --find-renames */
|
/* --find-renames */
|
||||||
ADD_CONSTANT_INT(m, GIT_DIFF_FIND_RENAMES)
|
ADD_CONSTANT_INT(m, GIT_DIFF_FIND_RENAMES)
|
||||||
|
@@ -146,6 +146,8 @@ typedef struct {
|
|||||||
PyObject *content;
|
PyObject *content;
|
||||||
} DiffLine;
|
} DiffLine;
|
||||||
|
|
||||||
|
SIMPLE_TYPE(DiffStats, git_diff_stats, stats);
|
||||||
|
|
||||||
/* git_tree_walk , git_treebuilder*/
|
/* git_tree_walk , git_treebuilder*/
|
||||||
SIMPLE_TYPE(TreeBuilder, git_treebuilder, bld)
|
SIMPLE_TYPE(TreeBuilder, git_treebuilder, bld)
|
||||||
|
|
||||||
|
@@ -101,6 +101,11 @@ HUNK_EXPECTED = """- a contents 2
|
|||||||
+ a contents
|
+ 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):
|
class DiffDirtyTest(utils.DirtyRepoTestCase):
|
||||||
def test_diff_empty_index(self):
|
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 == GIT_DELTA_RENAMED, diff)
|
||||||
self.assertAny(lambda x: x.delta.status_char() == 'R', 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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Reference in New Issue
Block a user