diff --git a/include/pygit2/commit.h b/include/pygit2/commit.h index f586305..7930122 100644 --- a/include/pygit2/commit.h +++ b/include/pygit2/commit.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_pygit2_commit_h #define INCLUDE_pygit2_commit_h +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> diff --git a/include/pygit2/diff.h b/include/pygit2/diff.h new file mode 100644 index 0000000..3060234 --- /dev/null +++ b/include/pygit2/diff.h @@ -0,0 +1,12 @@ +#ifndef INCLUDE_pygit2_diff_h +#define INCLUDE_pygit2_diff_h + +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include <git2.h> +#include <pygit2/types.h> + +PyObject* Diff_changes(Diff *self); +PyObject* Diff_patch(Diff *self); + +#endif diff --git a/include/pygit2/error.h b/include/pygit2/error.h index cd336db..587eb89 100644 --- a/include/pygit2/error.h +++ b/include/pygit2/error.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_pygit2_error_h #define INCLUDE_pygit2_error_h +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> diff --git a/include/pygit2/index.h b/include/pygit2/index.h index 0f07236..6f30d4f 100644 --- a/include/pygit2/index.h +++ b/include/pygit2/index.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_pygit2_index_h #define INCLUDE_pygit2_index_h +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> diff --git a/include/pygit2/object.h b/include/pygit2/object.h index 513679e..aeba9d3 100644 --- a/include/pygit2/object.h +++ b/include/pygit2/object.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_pygit2_object_h #define INCLUDE_pygit2_object_h +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> #include <pygit2/types.h> diff --git a/include/pygit2/oid.h b/include/pygit2/oid.h index d9a758b..9d68459 100644 --- a/include/pygit2/oid.h +++ b/include/pygit2/oid.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_pygit2_oid_h #define INCLUDE_pygit2_oid_h +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> diff --git a/include/pygit2/reference.h b/include/pygit2/reference.h index 7b3d179..a0d6925 100644 --- a/include/pygit2/reference.h +++ b/include/pygit2/reference.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_pygit2_reference_h #define INCLUDE_pygit2_reference_h +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> diff --git a/include/pygit2/repository.h b/include/pygit2/repository.h index 8cec910..35cf7c0 100644 --- a/include/pygit2/repository.h +++ b/include/pygit2/repository.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_pygit2_repository_h #define INCLUDE_pygit2_repository_h +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> #include <pygit2/types.h> diff --git a/include/pygit2/signature.h b/include/pygit2/signature.h index 942e67d..67bf003 100644 --- a/include/pygit2/signature.h +++ b/include/pygit2/signature.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_pygit2_signature_h #define INCLUDE_pygit2_signature_h +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> #include <pygit2/types.h> diff --git a/include/pygit2/tag.h b/include/pygit2/tag.h index b0395a4..8c13903 100644 --- a/include/pygit2/tag.h +++ b/include/pygit2/tag.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_pygit2_tag_h #define INCLUDE_pygit2_tag_h +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> #include <pygit2/types.h> diff --git a/include/pygit2/tree.h b/include/pygit2/tree.h index 97a42ef..d4af195 100644 --- a/include/pygit2/tree.h +++ b/include/pygit2/tree.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_pygit2_tree_h #define INCLUDE_pygit2_tree_h +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> #include <pygit2/types.h> @@ -13,6 +14,7 @@ PyObject* TreeEntry_to_object(TreeEntry *self); TreeEntry* Tree_getitem_by_index(Tree *self, PyObject *py_index); TreeEntry* Tree_getitem(Tree *self, PyObject *value); +PyObject* Tree_diff_tree(Tree *self, PyObject *args); PyObject* TreeBuilder_insert(TreeBuilder *self, PyObject *args); PyObject* TreeBuilder_write(TreeBuilder *self); diff --git a/include/pygit2/types.h b/include/pygit2/types.h index bccb5c9..81f302f 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_pygit2_objects_h #define INCLUDE_pygit2_objects_h +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> @@ -35,6 +36,24 @@ typedef struct { const git_tree_entry *entry; } TreeEntry; +typedef struct { + PyObject_HEAD + Tree *t0; + Tree *t1; +} Diff; + +typedef struct { + PyObject_HEAD + int old_start; + int old_lines; + char* old_file; + int new_start; + int new_lines; + char* new_file; + PyObject *old_data; + PyObject *new_data; +} Hunk; + typedef struct { PyObject_HEAD Tree *owner; diff --git a/include/pygit2/utils.h b/include/pygit2/utils.h index 23c53f0..ee6e7b0 100644 --- a/include/pygit2/utils.h +++ b/include/pygit2/utils.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_pygit2_utils_h #define INCLUDE_pygit2_utils_h +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> #include <pygit2/types.h> diff --git a/include/pygit2/walker.h b/include/pygit2/walker.h index 4d6cc66..0c68373 100644 --- a/include/pygit2/walker.h +++ b/include/pygit2/walker.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_pygit2_walker_h #define INCLUDE_pygit2_walker_h +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> #include <pygit2/types.h> diff --git a/src/pygit2.c b/src/pygit2.c index fe46316..7e1ad75 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -40,6 +40,8 @@ extern PyObject *GitError; PyTypeObject RepositoryType; PyTypeObject ObjectType; PyTypeObject CommitType; +PyTypeObject DiffType; +PyTypeObject HunkType; PyTypeObject TreeType; PyTypeObject TreeBuilderType; PyTypeObject TreeEntryType; @@ -129,6 +131,12 @@ moduleinit(PyObject* m) CommitType.tp_base = &ObjectType; if (PyType_Ready(&CommitType) < 0) return NULL; + DiffType.tp_base = &ObjectType; + if (PyType_Ready(&DiffType) < 0) + return NULL; + HunkType.tp_base = &ObjectType; + if (PyType_Ready(&HunkType) < 0) + return NULL; TreeType.tp_base = &ObjectType; if (PyType_Ready(&TreeType) < 0) return NULL; diff --git a/src/pygit2/blob.c b/src/pygit2/blob.c index 4b78199..2a9d123 100644 --- a/src/pygit2/blob.c +++ b/src/pygit2/blob.c @@ -1,3 +1,4 @@ +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <pygit2/object.h> diff --git a/src/pygit2/commit.c b/src/pygit2/commit.c index d555189..15d9ae6 100644 --- a/src/pygit2/commit.c +++ b/src/pygit2/commit.c @@ -1,10 +1,9 @@ +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <pygit2/error.h> #include <pygit2/utils.h> #include <pygit2/signature.h> #include <pygit2/commit.h> -#include <pygit2/oid.h> -#include <pygit2/repository.h> extern PyTypeObject TreeType; diff --git a/src/pygit2/diff.c b/src/pygit2/diff.c new file mode 100644 index 0000000..c957b9c --- /dev/null +++ b/src/pygit2/diff.c @@ -0,0 +1,330 @@ +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include <structmember.h> +#include <pygit2/error.h> +#include <pygit2/types.h> +#include <pygit2/utils.h> +#include <pygit2/diff.h> + +extern PyTypeObject DiffType; +extern PyTypeObject HunkType; + +static int diff_data_cb( + void *cb_data, + git_diff_delta *delta, + git_diff_range *range, + char usage, + const char *line, + size_t line_len) +{ + PyObject *hunks, *tmp; + Hunk *hunk; + Py_ssize_t size; + + hunks = PyDict_GetItemString(cb_data, "hunks"); + if(hunks == NULL) + return -1; + + size = PyList_Size(hunks); + hunk = (Hunk*) PyList_GetItem(hunks, size-1); + if(hunk == NULL) + return -1; + + tmp = PyBytes_FromStringAndSize(line, line_len); + + if(usage != GIT_DIFF_LINE_DELETION) + PyBytes_Concat(&hunk->new_data, tmp); + + if(usage != GIT_DIFF_LINE_ADDITION) + PyBytes_Concat(&hunk->old_data, tmp); + + return 0; +} + +static int diff_hunk_cb( + void *cb_data, + git_diff_delta *delta, + git_diff_range *range, + const char *header, + size_t header_len) +{ + PyObject *hunks; + Hunk *hunk; + + hunks = PyDict_GetItemString(cb_data, "hunks"); + if(hunks == NULL) { + hunks = PyList_New(0); + PyDict_SetItemString(cb_data, "hunks", hunks); + } + + hunk = (Hunk*) PyType_GenericNew(&HunkType, NULL, NULL); + + hunk->old_start = range->old_start; + hunk->old_lines = range->old_lines; + hunk->new_start = range->new_start; + hunk->new_lines = range->new_lines; + + int len; + char* old_path, *new_path; + + len = strlen(delta->old_file.path) + 1; + old_path = malloc(sizeof(char) * len); + memcpy(old_path, delta->old_file.path, len); + hunk->old_file = old_path; + + len = strlen(delta->new_file.path) + 1; + new_path = malloc(sizeof(char) * len); + memcpy(new_path, delta->new_file.path, len); + hunk->new_file = new_path; + +#if PY_MAJOR_VERSION >= 3 + hunk->old_data = Py_BuildValue("y", ""); + hunk->new_data = Py_BuildValue("y", ""); +#else + hunk->old_data = Py_BuildValue("s", ""); + hunk->new_data = Py_BuildValue("s", ""); +#endif + + PyList_Append(hunks, (PyObject*) hunk); + + return 0; +}; + +static int diff_file_cb(void *cb_data, git_diff_delta *delta, float progress) +{ + PyObject *files, *file; + + files = PyDict_GetItemString(cb_data, "files"); + if(files == NULL) { + files = PyList_New(0); + PyDict_SetItemString(cb_data, "files", files); + } + + file = Py_BuildValue("(s,s,i)", + delta->old_file.path, + delta->new_file.path, + delta->status + ); + + PyList_Append(files, file); + + return 0; +} + +PyObject * +Diff_changes(Diff *self) +{ + git_diff_options opts = {0}; + git_diff_list *changes; + int err; + + err = git_diff_tree_to_tree( + self->t0->repo->repo, + &opts, + self->t0->tree, + self->t1->tree, + &changes); + + if(err < 0) { + Error_set(err); + return NULL; + } + + PyObject *payload; + payload = PyDict_New(); + + git_diff_foreach( + changes, + payload, + &diff_file_cb, + &diff_hunk_cb, + &diff_data_cb + ); + git_diff_list_free(changes); + + return payload; +} + +static int diff_print_cb( + void *cb_data, + git_diff_delta *delta, + git_diff_range *range, + char usage, + const char *line, + size_t line_len) +{ + PyObject *data = PyBytes_FromStringAndSize(line, line_len); + PyBytes_ConcatAndDel((PyObject**) cb_data, data); + + return 0; +} + + +PyObject * +Diff_patch(Diff *self) +{ + git_diff_options opts = {0}; + git_diff_list *changes; + int err; + + err = git_diff_tree_to_tree( + self->t0->repo->repo, + &opts, + self->t0->tree, + self->t1->tree, + &changes); + + if(err < 0) { + Error_set(err); + return NULL; + } + + PyObject *patch = PyBytes_FromString(""); + + git_diff_print_patch(changes, &patch, &diff_print_cb); + + return patch; +} + +static int +Hunk_init(Hunk *self, PyObject *args, PyObject *kwds) +{ + self->old_start = 0; + self->old_lines = 0; + + self->new_start = 0; + self->new_lines = 0; + + self->old_data = PyString_FromString(""); + if (self->old_data == NULL) { + Py_DECREF(self); + return -1; + } + + self->new_data = PyString_FromString(""); + if (self->new_data == NULL) { + Py_DECREF(self); + return -1; + } + + return 0; +} + +static void +Hunk_dealloc(Hunk *self) +{ + Py_XDECREF(self->old_data); + Py_XDECREF(self->new_data); + PyObject_Del(self); +} + +PyMemberDef Hunk_members[] = { + {"old_start", T_INT, offsetof(Hunk, old_start), 0, "old start"}, + {"old_lines", T_INT, offsetof(Hunk, old_lines), 0, "old lines"}, + {"old_file", T_STRING, offsetof(Hunk, old_file), 0, "old file"}, + {"old_data", T_OBJECT, offsetof(Hunk, old_data), 0, "old data"}, + {"new_start", T_INT, offsetof(Hunk, new_start), 0, "new start"}, + {"new_lines", T_INT, offsetof(Hunk, new_lines), 0, "new lines"}, + {"new_file", T_STRING, offsetof(Hunk, new_file), 0, "old file"}, + {"new_data", T_OBJECT, offsetof(Hunk, new_data), 0, "new data"}, + {NULL} +}; + +PyTypeObject HunkType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Hunk", /* tp_name */ + sizeof(Hunk), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Hunk_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 */ + "Hunk object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + Hunk_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Hunk_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +static void +Diff_dealloc(Diff *self) +{ + Py_XDECREF(self->t0); + Py_XDECREF(self->t1); + PyObject_Del(self); +} + + +PyGetSetDef Diff_getseters[] = { + {"changes", (getter)Diff_changes, NULL, "raw changes", NULL}, + {"patch", (getter)Diff_patch, NULL, "patch", NULL}, + {NULL} +}; + + +PyTypeObject DiffType = { + PyVarObject_HEAD_INIT(NULL, 0) + "pygit2.Diff", /* tp_name */ + sizeof(Diff), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Diff_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 */ + "Diff objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + Diff_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 */ +}; diff --git a/src/pygit2/index.c b/src/pygit2/index.c index 475d04c..2237b66 100644 --- a/src/pygit2/index.c +++ b/src/pygit2/index.c @@ -1,3 +1,4 @@ +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <pygit2/error.h> #include <pygit2/types.h> diff --git a/src/pygit2/object.c b/src/pygit2/object.c index 62a5182..cf48483 100644 --- a/src/pygit2/object.c +++ b/src/pygit2/object.c @@ -1,3 +1,4 @@ +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <pygit2/error.h> #include <pygit2/types.h> diff --git a/src/pygit2/oid.c b/src/pygit2/oid.c index fd6e135..6ac86aa 100644 --- a/src/pygit2/oid.c +++ b/src/pygit2/oid.c @@ -1,3 +1,4 @@ +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <git2.h> #include <pygit2/utils.h> diff --git a/src/pygit2/reference.c b/src/pygit2/reference.c index f1a076d..37f24c4 100644 --- a/src/pygit2/reference.c +++ b/src/pygit2/reference.c @@ -1,3 +1,4 @@ +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <pygit2/error.h> #include <pygit2/types.h> diff --git a/src/pygit2/repository.c b/src/pygit2/repository.c index 763fa48..00de0d9 100644 --- a/src/pygit2/repository.c +++ b/src/pygit2/repository.c @@ -1,5 +1,3 @@ -// with the following define PY_SSIZE_T_CLEAN -// the length of PyArg_ParseTuple will be Py_ssize_t rather than int #define PY_SSIZE_T_CLEAN #include <Python.h> #include <pygit2/error.h> diff --git a/src/pygit2/signature.c b/src/pygit2/signature.c index cc4f8c2..666dd3b 100644 --- a/src/pygit2/signature.c +++ b/src/pygit2/signature.c @@ -1,3 +1,4 @@ +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <pygit2/error.h> #include <pygit2/types.h> diff --git a/src/pygit2/tag.c b/src/pygit2/tag.c index ab4a2b5..fb739db 100644 --- a/src/pygit2/tag.c +++ b/src/pygit2/tag.c @@ -1,3 +1,4 @@ +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <pygit2/error.h> #include <pygit2/types.h> diff --git a/src/pygit2/tree.c b/src/pygit2/tree.c index 436b3f9..77c77d1 100644 --- a/src/pygit2/tree.c +++ b/src/pygit2/tree.c @@ -1,3 +1,4 @@ +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <pygit2/error.h> #include <pygit2/utils.h> @@ -6,6 +7,7 @@ #include <pygit2/tree.h> extern PyTypeObject TreeType; +extern PyTypeObject DiffType; extern PyTypeObject TreeIterType; void @@ -226,6 +228,29 @@ Tree_getitem(Tree *self, PyObject *value) return wrap_tree_entry(entry, self); } +PyObject * +Tree_diff_tree(Tree *self, PyObject *args) +{ + Diff *py_diff; + Tree *py_tree; + + if (!PyArg_ParseTuple(args, "O!", &TreeType, &py_tree)) { + return NULL; + } + + py_diff = PyObject_New(Diff, &DiffType); + if (py_diff) { + Py_INCREF(py_diff); + Py_INCREF(py_tree); + Py_INCREF(self); + + py_diff->t0 = self; + py_diff->t1 = py_tree; + } + + return (PyObject*) py_diff; +} + PySequenceMethods Tree_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ @@ -243,6 +268,12 @@ PyMappingMethods Tree_as_mapping = { 0, /* mp_ass_subscript */ }; +PyMethodDef Tree_methods[] = { + {"diff", (PyCFunction)Tree_diff_tree, METH_VARARGS, + "Diff two trees."}, + {NULL} +}; + PyTypeObject TreeType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Tree", /* tp_name */ @@ -271,7 +302,7 @@ PyTypeObject TreeType = { 0, /* tp_weaklistoffset */ (getiterfunc)Tree_iter, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + Tree_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ diff --git a/src/pygit2/utils.c b/src/pygit2/utils.c index 0bd3aac..d4dd399 100644 --- a/src/pygit2/utils.c +++ b/src/pygit2/utils.c @@ -1,3 +1,5 @@ +#define PY_SSIZE_T_CLEAN +#include <Python.h> #include <pygit2/error.h> #include <pygit2/utils.h> diff --git a/src/pygit2/walker.c b/src/pygit2/walker.c index 7b785da..6a0bb2b 100644 --- a/src/pygit2/walker.c +++ b/src/pygit2/walker.c @@ -1,3 +1,4 @@ +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <pygit2/error.h> #include <pygit2/utils.h> diff --git a/test/__init__.py b/test/__init__.py index 62823ae..fcb82c5 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -36,7 +36,7 @@ import unittest names = ['blob', 'commit', 'index', 'refs', 'repository', 'revwalk', 'tag', - 'tree', 'signature', 'status', 'treebuilder'] + 'tree', 'signature', 'status', 'treebuilder', 'diff'] def test_suite(): modules = ['test.test_%s' % n for n in names] return unittest.defaultTestLoader.loadTestsFromNames(modules) diff --git a/test/test_diff.py b/test/test_diff.py new file mode 100644 index 0000000..fadaff2 --- /dev/null +++ b/test/test_diff.py @@ -0,0 +1,97 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2012 Nico von Geyso +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2, +# as published by the Free Software Foundation. +# +# In addition to the permissions in the GNU General Public License, +# the authors give you unlimited permission to link the compiled +# version of this file into combinations with other programs, +# and to distribute those combinations without any restriction +# coming from the use of this file. (The General Public License +# restrictions do apply in other respects; for example, they cover +# modification of the file, and distribution when not linked into +# a combined executable.) +# +# This file is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +"""Tests for Diff objects.""" + +from __future__ import absolute_import +from __future__ import unicode_literals +import unittest + +import pygit2 +from . import utils + +__author__ = 'Nico.Geyso@FU-Berlin.de (Nico von Geyso)' + + +COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10' +COMMIT_SHA1_2 = 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c' +PATCH = b"""diff --git a/a b/a +index 7f129fd..af431f2 100644 +--- a/a ++++ b/a +@@ -1 +1 @@ +-a contents 2 ++a contents +diff --git a/c/d b/c/d +deleted file mode 100644 +index 297efb8..0000000 +--- a/c/d ++++ /dev/null +@@ -1 +0,0 @@ +-c/d contents +""" + + +class DiffTest(utils.BareRepoTestCase): + + def test_diff_invalid(self): + commit_a = self.repo[COMMIT_SHA1_1] + commit_b = self.repo[COMMIT_SHA1_2] + self.assertRaises(TypeError, commit_a.tree.diff, commit_b) + + def test_diff_tree(self): + commit_a = self.repo[COMMIT_SHA1_1] + commit_b = self.repo[COMMIT_SHA1_2] + + diff = commit_a.tree.diff(commit_b.tree) + + self.assertIsNotNone(diff) + self.assertIn(('a','a', 3), diff.changes['files']) + self.assertEqual(2, len(diff.changes['hunks'])) + + hunk = diff.changes['hunks'][0] + self.assertEqual(hunk.old_start, 1) + self.assertEqual(hunk.old_lines, 0) + self.assertEqual(hunk.new_start, 1) + self.assertEqual(hunk.new_lines, 0) + + self.assertEqual(hunk.old_file, 'a') + self.assertEqual(hunk.new_file, 'a') + + self.assertEqual(hunk.old_data, b'a contents 2\n') + self.assertEqual(hunk.new_data, b'a contents\n') + + def test_diff_patch(self): + commit_a = self.repo[COMMIT_SHA1_1] + commit_b = self.repo[COMMIT_SHA1_2] + + diff = commit_a.tree.diff(commit_b.tree) + self.assertEqual(diff.patch, PATCH) + + +if __name__ == '__main__': + unittest.main()