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()