Merge pull request #229 from cholin/features/diff_refactoring

refactoring Tree.diff(), Index.diff() into seperate methods and added a Repository.diff() method.
This commit is contained in:
Nico von Geyso
2013-05-22 07:04:53 -07:00
12 changed files with 428 additions and 142 deletions

View File

@@ -2,27 +2,34 @@
Diff
**********************************************************************
A diff shows the changes between trees, an index or the working dir::
.. contents::
>>> head = repo.revparse_single('HEAD')
A diff shows the changes between trees, an index or the working dir.
# Diff two trees
>>> t0 = head.tree
>>> t1 = head.parents[0].tree
>>> diff = t1.diff(t0)
.. automethod:: pygit2.Repository.diff
# Diff a tree with the index
>>> diff = head.tree.diff(repo.index)
Examples
# Diff a tree with the current working dir
>>> diff = head.tree.diff()
.. code-block:: python
# Changes between commits
>>> t0 = revparse_single('HEAD')
>>> t1 = revparse_single('HEAD^')
>>> repo.diff(t0, t1)
>>> t0.diff(t1) # equivalent
>>> repo.diff('HEAD', 'HEAD^') # equivalent
# Get all patches for a diff
>>> t0 = repo.revparse_single('HEAD^').tree
>>> t1 = repo.revparse_single('HEAD~3').tree
>>> diff = t0.diff(t1)
>>> diff = repo.diff('HEAD^', 'HEAD~3')
>>> patches = [p for p in diff]
# Diffing the empty tree
>>> tree = revparse_single('HEAD').tree
>>> tree.diff_to_tree()
# Diff empty tree to a tree
>>> tree = revparse_single('HEAD').tree
>>> tree.diff_to_tree(swap=True)
The Diff type
====================

View File

@@ -159,7 +159,9 @@ interfaces.
Return an iterator over the entries of the tree.
.. automethod:: pygit2.Tree.diff
.. automethod:: pygit2.Tree.diff_to_tree
.. automethod:: pygit2.Tree.diff_to_workdir
.. automethod:: pygit2.Tree.diff_to_index
Tree entries
------------

View File

@@ -33,7 +33,8 @@ The Index type
.. automethod:: pygit2.Index.write
.. automethod:: pygit2.Index.read_tree
.. automethod:: pygit2.Index.write_tree
.. automethod:: pygit2.Index.diff
.. automethod:: pygit2.Index.diff_to_tree
.. automethod:: pygit2.Index.diff_to_workdir
The IndexEntry type

View File

@@ -31,8 +31,8 @@ from string import hexdigits
# Import from pygit2
from _pygit2 import Repository as _Repository
from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN
from _pygit2 import GIT_CHECKOUT_SAFE_CREATE
from _pygit2 import Reference
from _pygit2 import GIT_CHECKOUT_SAFE_CREATE, GIT_DIFF_NORMAL
from _pygit2 import Reference, Tree, Commit, Blob
class Repository(_Repository):
@@ -128,3 +128,90 @@ class Repository(_Repository):
treeish = self[oid]
self.checkout_tree(treeish, strategy)
self.head = refname
#
# Diff
#
def diff(self, a=None, b=None, cached=False, flags=GIT_DIFF_NORMAL,
context_lines=3, interhunk_lines=0):
"""
Show changes between the working tree and the index or a tree,
changes between the index and a tree, changes between two trees, or
changes between two blobs.
Keyword arguments:
cached
use staged changes instead of workdir
flag
a GIT_DIFF_* constant
context_lines
the number of unchanged lines that define the boundary
of a hunk (and to display before and after)\n"
interhunk_lines
the maximum number of unchanged lines between hunk
boundaries before the hunks will be merged into a one
Examples::
# Changes in the working tree not yet staged for the next commit
>>> diff()
# Changes between the index and your last commit
>>> diff(cached=True)
# Changes in the working tree since your last commit
>>> diff('HEAD')
# Changes between commits
>>> t0 = revparse_single('HEAD')
>>> t1 = revparse_single('HEAD^')
>>> diff(t0, t1)
>>> diff('HEAD', 'HEAD^') # equivalent
If you want to diff a tree against an empty tree, use the low level
API (Tree.diff_to_tree()) directly.
"""
def treeish_to_tree(obj):
try:
obj = self.revparse_single(obj)
except:
pass
if isinstance(obj, Commit):
return obj.tree
elif isinstance(obj, Reference):
oid = obj.resolve().target
return self[oid]
a = treeish_to_tree(a) or a
b = treeish_to_tree(b) or b
opt_keys = ['flags', 'context_lines', 'interhunk_lines']
opt_values = [flags, context_lines, interhunk_lines]
# Case 1: Diff tree to tree
if isinstance(a, Tree) and isinstance(b, Tree):
return a.diff_to_tree(b, **dict(zip(opt_keys, opt_values)))
# Case 2: Index to workdir
elif a is None and b is None:
return self.index.diff_to_workdir(*opt_values)
# Case 3: Diff tree to index or workdir
elif isinstance(a, Tree) and b is None:
if cached:
return a.diff_to_index(self.index, *opt_values)
else:
return a.diff_to_workdir(*opt_values)
# Case 4: Diff blob to blob
if isinstance(a, Blob) and isinstance(b, Blob):
raise NotImplementedError('git_diff_blob_to_blob()')
raise ValueError("Only blobs and treeish can be diffed")

View File

@@ -42,6 +42,21 @@ extern PyTypeObject HunkType;
PyTypeObject PatchType;
PyObject*
wrap_diff(git_diff_list *diff, Repository *repo)
{
Diff *py_diff;
py_diff = PyObject_New(Diff, &DiffType);
if (py_diff) {
Py_INCREF(repo);
py_diff->repo = repo;
py_diff->list = diff;
}
return (PyObject*) py_diff;
}
PyObject*
diff_get_patch_byindex(git_diff_list* list, size_t idx)
{
@@ -50,9 +65,11 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx)
git_diff_patch* patch = NULL;
size_t i, j, hunk_amounts, lines_in_hunk, line_len, header_len;
const char* line, *header;
char line_origin;
int err;
Hunk *py_hunk = NULL;
Patch *py_patch = NULL;
PyObject *py_line_origin=NULL, *py_line=NULL;
err = git_diff_get_patch(&patch, &delta, list, idx);
if (err < 0)
@@ -84,18 +101,24 @@ diff_get_patch_byindex(git_diff_list* list, size_t idx)
py_hunk->new_start = range->new_start;
py_hunk->new_lines = range->new_lines;
py_hunk->lines = PyList_New(lines_in_hunk + 1);
PyList_SetItem(py_hunk->lines, 0,
to_unicode_n(header, header_len, NULL, NULL));
for (j=1; j < lines_in_hunk + 1; ++j) {
err = git_diff_patch_get_line_in_hunk(&py_hunk->origin,
&line, &line_len, NULL, NULL, patch, i, j - 1);
py_hunk->lines = PyList_New(lines_in_hunk);
for (j=0; j < lines_in_hunk; ++j) {
err = git_diff_patch_get_line_in_hunk(&line_origin,
&line, &line_len, NULL, NULL, patch, i, j);
if (err < 0)
goto cleanup;
py_line_origin = to_unicode_n(&line_origin, 1, NULL, NULL);
py_line = to_unicode_n(line, line_len, NULL, NULL);
PyList_SetItem(py_hunk->lines, j,
to_unicode_n(line, line_len, NULL, NULL));
Py_BuildValue("OO",
py_line_origin,
py_line
)
);
Py_DECREF(py_line_origin);
Py_DECREF(py_line);
}
PyList_SetItem((PyObject*) py_patch->hunks, i,
@@ -279,7 +302,6 @@ Hunk_dealloc(Hunk *self)
}
PyMemberDef Hunk_members[] = {
MEMBER(Hunk, origin, T_CHAR, "origin."),
MEMBER(Hunk, old_start, T_INT, "Old start."),
MEMBER(Hunk, old_lines, T_INT, "Old lines."),
MEMBER(Hunk, new_start, T_INT, "New start."),

View File

@@ -41,4 +41,6 @@
PyObject* Diff_changes(Diff *self);
PyObject* Diff_patch(Diff *self);
PyObject* wrap_diff(git_diff_list *diff, Repository *repo);
#endif

View File

@@ -31,6 +31,7 @@
#include "types.h"
#include "utils.h"
#include "oid.h"
#include "diff.h"
#include "index.h"
extern PyTypeObject IndexType;
@@ -114,54 +115,83 @@ Index_clear(Index *self)
}
PyDoc_STRVAR(Index_diff__doc__,
"diff([tree]) -> Diff\n"
PyDoc_STRVAR(Index_diff_to_workdir__doc__,
"diff_to_workdir([flag, context_lines, interhunk_lines]) -> Diff\n"
"\n"
"Return a :py:class:`~pygit2.Diff` object with the differences between the\n"
"index and the working copy. If a :py:class:`~pygit2.Tree` object is\n"
"passed, return the diferences between the index and the given tree.");
"index and the working copy.\n"
"\n"
"Arguments:\n"
"\n"
"flag: a GIT_DIFF_* constant.\n"
"\n"
"context_lines: the number of unchanged lines that define the boundary\n"
" of a hunk (and to display before and after)\n"
"\n"
"interhunk_lines: the maximum number of unchanged lines between hunk\n"
" boundaries before the hunks will be merged into a one.\n");
PyObject *
Index_diff(Index *self, PyObject *args)
Index_diff_to_workdir(Index *self, PyObject *args)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff_list *diff;
int err;
Diff *py_diff;
PyObject *py_obj = NULL;
if (!PyArg_ParseTuple(args, "|O", &py_obj))
if (!PyArg_ParseTuple(args, "|IHH", &opts.flags, &opts.context_lines,
&opts.interhunk_lines))
return NULL;
if (py_obj == NULL) {
err = git_diff_index_to_workdir(
&diff,
self->repo->repo,
self->index,
&opts);
} else if (PyObject_TypeCheck(py_obj, &TreeType)) {
err = git_diff_tree_to_index(
&diff,
self->repo->repo,
((Tree *)py_obj)->tree,
self->index,
&opts);
} else {
PyErr_SetObject(PyExc_TypeError, py_obj);
return NULL;
}
err = git_diff_index_to_workdir(
&diff,
self->repo->repo,
self->index,
&opts);
if (err < 0)
return Error_set(err);
py_diff = PyObject_New(Diff, &DiffType);
if (py_diff) {
Py_INCREF(self->repo);
py_diff->repo = self->repo;
py_diff->list = diff;
}
return wrap_diff(diff, self->repo);
}
return (PyObject*)py_diff;
PyDoc_STRVAR(Index_diff_to_tree__doc__,
"diff_to_tree(tree [, flag, context_lines, interhunk_lines]) -> Diff\n"
"\n"
"Return a :py:class:`~pygit2.Diff` object with the differences between the\n"
"index and the given tree.\n"
"\n"
"Arguments:\n"
"\n"
"tree: the tree to diff.\n"
"\n"
"flag: a GIT_DIFF_* constant.\n"
"\n"
"context_lines: the number of unchanged lines that define the boundary\n"
" of a hunk (and to display before and after)\n"
"\n"
"interhunk_lines: the maximum number of unchanged lines between hunk\n"
" boundaries before the hunks will be merged into a one.\n");
PyObject *
Index_diff_to_tree(Index *self, PyObject *args)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff_list *diff;
git_repository* repo;
int err;
Tree *py_tree = NULL;
if (!PyArg_ParseTuple(args, "O!|IHH", &TreeType, &py_tree, &opts.flags,
&opts.context_lines, &opts.interhunk_lines))
return NULL;
repo = git_tree_owner(py_tree->tree);
err = git_diff_tree_to_index(&diff, repo, py_tree->tree, self->index, &opts);
if (err < 0)
return Error_set(err);
return wrap_diff(diff, self->repo);
}
@@ -393,7 +423,8 @@ PyMethodDef Index_methods[] = {
METHOD(Index, add, METH_VARARGS),
METHOD(Index, remove, METH_VARARGS),
METHOD(Index, clear, METH_NOARGS),
METHOD(Index, diff, METH_VARARGS),
METHOD(Index, diff_to_workdir, METH_VARARGS),
METHOD(Index, diff_to_tree, METH_VARARGS),
METHOD(Index, _find, METH_O),
METHOD(Index, read, METH_NOARGS),
METHOD(Index, write, METH_NOARGS),

View File

@@ -383,6 +383,14 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNTRACKED)
ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNMODIFIED)
ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_UNTRACKED_DIRS)
ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_UNTRACKED_DIRS)
ADD_CONSTANT_INT(m, GIT_DIFF_DISABLE_PATHSPEC_MATCH)
ADD_CONSTANT_INT(m, GIT_DIFF_DELTAS_ARE_ICASE)
ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNTRACKED_CONTENT)
ADD_CONSTANT_INT(m, GIT_DIFF_SKIP_BINARY_CHECK)
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)
/* Flags for diff find similar */
/* --find-renames */
ADD_CONSTANT_INT(m, GIT_DIFF_FIND_RENAMES)

View File

@@ -497,6 +497,9 @@ Repository_config__get__(Repository *self)
py_config->config = config;
self->config = (PyObject*)py_config;
// We need 2 refs here.
// One is returned, one is keep internally.
Py_INCREF(self->config);
} else {
Py_INCREF(self->config);
}

View File

@@ -33,6 +33,7 @@
#include "repository.h"
#include "oid.h"
#include "tree.h"
#include "diff.h"
extern PyTypeObject TreeType;
extern PyTypeObject DiffType;
@@ -270,70 +271,138 @@ Tree_getitem(Tree *self, PyObject *value)
}
PyDoc_STRVAR(Tree_diff__doc__,
"diff([obj, flags]) -> Diff\n"
PyDoc_STRVAR(Tree_diff_to_workdir__doc__,
"diff_to_workdir([flags, context_lines, interhunk_lines]) -> Diff\n"
"\n"
"Get changes between current tree instance with another tree, an index or\n"
"the working dir.\n"
"Show the changes between the :py:class:`~pygit2.Tree` and the workdir.\n"
"\n"
"Arguments:\n"
"\n"
"obj\n"
" If not given compare diff against working dir. Possible valid\n"
" arguments are instances of Tree or Index.\n"
"flag: a GIT_DIFF_* constant.\n"
"\n"
"flags\n"
" TODO");
"context_lines: the number of unchanged lines that define the boundary\n"
" of a hunk (and to display before and after)\n"
"\n"
"interhunk_lines: the maximum number of unchanged lines between hunk\n"
" boundaries before the hunks will be merged into a one.\n");
PyObject *
Tree_diff(Tree *self, PyObject *args, PyObject *kwds)
Tree_diff_to_workdir(Tree *self, PyObject *args)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff_list *diff;
git_tree* tree = NULL;
git_index* index;
git_repository *repo;
int err, empty_tree = 0;
char *keywords[] = {"obj", "flags", "empty_tree", NULL};
git_repository* repo;
int err;
Diff *py_diff;
PyObject *py_obj = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oii", keywords,
&py_obj, &opts.flags, &empty_tree))
if (!PyArg_ParseTuple(args, "|IHH", &opts.flags, &opts.context_lines,
&opts.interhunk_lines))
return NULL;
repo = git_tree_owner(self->tree);
if (py_obj == NULL) {
if (empty_tree > 0)
err = git_diff_tree_to_tree(&diff, repo, self->tree, NULL, &opts);
else
err = git_diff_tree_to_workdir(&diff, repo, self->tree, &opts);
} else if (PyObject_TypeCheck(py_obj, &TreeType)) {
tree = ((Tree *)py_obj)->tree;
err = git_diff_tree_to_tree(&diff, repo, self->tree, tree, &opts);
} else if (PyObject_TypeCheck(py_obj, &IndexType)) {
index = ((Index *)py_obj)->index;
err = git_diff_tree_to_index(&diff, repo, self->tree, index, &opts);
} else {
PyErr_SetObject(PyExc_TypeError, py_obj);
return NULL;
}
err = git_diff_tree_to_workdir(&diff, repo, self->tree, &opts);
if (err < 0)
return Error_set(err);
py_diff = PyObject_New(Diff, &DiffType);
if (py_diff) {
Py_INCREF(self->repo);
py_diff->repo = self->repo;
py_diff->list = diff;
return wrap_diff(diff, self->repo);
}
PyDoc_STRVAR(Tree_diff_to_index__doc__,
"diff_to_index(index, [flags, context_lines, interhunk_lines]) -> Diff\n"
"\n"
"Show the changes between the index and a given :py:class:`~pygit2.Tree`.\n"
"\n"
"Arguments:\n"
"\n"
"tree: the :py:class:`~pygit2.Tree` to diff.\n"
"\n"
"flag: a GIT_DIFF_* constant.\n"
"\n"
"context_lines: the number of unchanged lines that define the boundary\n"
" of a hunk (and to display before and after)\n"
"\n"
"interhunk_lines: the maximum number of unchanged lines between hunk\n"
" boundaries before the hunks will be merged into a one.\n");
PyObject *
Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff_list *diff;
git_repository* repo;
int err;
Index *py_idx = NULL;
if (!PyArg_ParseTuple(args, "O!|IHH", &IndexType, &py_idx, &opts.flags,
&opts.context_lines,
&opts.interhunk_lines))
return NULL;
repo = git_tree_owner(self->tree);
err = git_diff_tree_to_index(&diff, repo, self->tree, py_idx->index, &opts);
if (err < 0)
return Error_set(err);
return wrap_diff(diff, self->repo);
}
PyDoc_STRVAR(Tree_diff_to_tree__doc__,
"diff_to_tree([tree, flags, context_lines, interhunk_lines, swap]) -> Diff\n"
"\n"
"Show the changes between two trees\n"
"\n"
"Arguments:\n"
"\n"
"tree: the :py:class:`~pygit2.Tree` to diff. If no tree is given the empty\n"
" tree will be used instead.\n"
"\n"
"flag: a GIT_DIFF_* constant.\n"
"\n"
"context_lines: the number of unchanged lines that define the boundary\n"
" of a hunk (and to display before and after)\n"
"\n"
"interhunk_lines: the maximum number of unchanged lines between hunk\n"
" boundaries before the hunks will be merged into a one.\n"
"\n"
"swap: instead of diffing a to b. Diff b to a.\n");
PyObject *
Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff_list *diff;
git_tree *from, *to, *tmp;
git_repository* repo;
int err, swap = 0;
char *keywords[] = {"obj", "flags", "context_lines", "interhunk_lines",
"swap", NULL};
Tree *py_tree = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!IHHi", keywords,
&TreeType, &py_tree, &opts.flags,
&opts.context_lines,
&opts.interhunk_lines, &swap))
return NULL;
repo = git_tree_owner(self->tree);
to = (py_tree == NULL) ? NULL : py_tree->tree;
from = self->tree;
if (swap > 0) {
tmp = from;
from = to;
to = tmp;
}
return (PyObject*)py_diff;
err = git_diff_tree_to_tree(&diff, repo, from, to, &opts);
if (err < 0)
return Error_set(err);
return wrap_diff(diff, self->repo);
}
@@ -355,7 +424,9 @@ PyMappingMethods Tree_as_mapping = {
};
PyMethodDef Tree_methods[] = {
METHOD(Tree, diff, METH_VARARGS | METH_KEYWORDS),
METHOD(Tree, diff_to_tree, METH_VARARGS | METH_KEYWORDS),
METHOD(Tree, diff_to_workdir, METH_VARARGS),
METHOD(Tree, diff_to_index, METH_VARARGS | METH_KEYWORDS),
{NULL}
};

View File

@@ -118,7 +118,6 @@ typedef struct {
typedef struct {
PyObject_HEAD
PyObject* lines;
char origin;
int old_start;
int old_lines;
int new_start;

View File

@@ -33,6 +33,7 @@ import unittest
import pygit2
from pygit2 import GIT_DIFF_INCLUDE_UNMODIFIED
from . import utils
from itertools import chain
COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10'
@@ -60,7 +61,7 @@ index 297efb8..0000000
-c/d contents
"""
DIFF_INDEX_EXPECTED = [
DIFF_HEAD_TO_INDEX_EXPECTED = [
'staged_changes',
'staged_changes_file_deleted',
'staged_changes_file_modified',
@@ -71,7 +72,7 @@ DIFF_INDEX_EXPECTED = [
'staged_new_file_modified'
]
DIFF_WORKDIR_EXPECTED = [
DIFF_HEAD_TO_WORKDIR_EXPECTED = [
'file_deleted',
'modified_file',
'staged_changes',
@@ -83,27 +84,50 @@ DIFF_WORKDIR_EXPECTED = [
'subdir/modified_file'
]
HUNK_EXPECTED = """@@ -1 +1 @@
a contents 2
a contents
DIFF_INDEX_TO_WORK_EXPECTED = [
'file_deleted',
'modified_file',
'staged_changes_file_deleted',
'staged_changes_file_modified',
'staged_new_file_deleted',
'staged_new_file_modified',
'subdir/deleted_file',
'subdir/modified_file'
]
HUNK_EXPECTED = """- a contents 2
+ a contents
"""
class DiffDirtyTest(utils.DirtyRepoTestCase):
def test_diff_empty_index(self):
repo = self.repo
head = repo[repo.lookup_reference('HEAD').resolve().target]
diff = head.tree.diff(repo.index)
head = repo[repo.lookup_reference('HEAD').resolve().target]
diff = head.tree.diff_to_index(repo.index)
files = [patch.new_file_path for patch in diff]
self.assertEqual(DIFF_INDEX_EXPECTED, files)
self.assertEqual(DIFF_HEAD_TO_INDEX_EXPECTED, files)
diff = repo.diff('HEAD', cached=True)
files = [patch.new_file_path for patch in diff]
self.assertEqual(DIFF_HEAD_TO_INDEX_EXPECTED, files)
def test_workdir_to_tree(self):
repo = self.repo
head = repo[repo.lookup_reference('HEAD').resolve().target]
diff = head.tree.diff()
diff = head.tree.diff_to_workdir()
files = [patch.new_file_path for patch in diff]
self.assertEqual(DIFF_WORKDIR_EXPECTED, files)
self.assertEqual(DIFF_HEAD_TO_WORKDIR_EXPECTED, files)
diff = repo.diff('HEAD')
files = [patch.new_file_path for patch in diff]
self.assertEqual(DIFF_HEAD_TO_WORKDIR_EXPECTED, files)
def test_index_to_workdir(self):
diff = self.repo.diff()
files = [patch.new_file_path for patch in diff]
self.assertEqual(DIFF_INDEX_TO_WORK_EXPECTED, files)
class DiffTest(utils.BareRepoTestCase):
@@ -111,13 +135,22 @@ 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)
self.assertRaises(TypeError, commit_a.tree.diff_to_tree, commit_b)
self.assertRaises(TypeError, commit_a.tree.diff_to_index, commit_b)
def test_diff_empty_index(self):
repo = self.repo
head = repo[repo.lookup_reference('HEAD').resolve().target]
diff = head.tree.diff(repo.index)
diff = self.repo.index.diff_to_tree(head.tree)
files = [patch.new_file_path.split('/')[0] for patch in diff]
self.assertEqual([x.name for x in head.tree], files)
diff = head.tree.diff_to_index(repo.index)
files = [patch.new_file_path.split('/')[0] for patch in diff]
self.assertEqual([x.name for x in head.tree], files)
diff = repo.diff('HEAD', cached=True)
files = [patch.new_file_path.split('/')[0] for patch in diff]
self.assertEqual([x.name for x in head.tree], files)
@@ -125,41 +158,59 @@ class DiffTest(utils.BareRepoTestCase):
commit_a = self.repo[COMMIT_SHA1_1]
commit_b = self.repo[COMMIT_SHA1_2]
diff = commit_a.tree.diff(commit_b.tree)
def _test(diff):
# self.assertIsNotNone is 2.7 only
self.assertTrue(diff is not None)
# self.assertIn is 2.7 only
self.assertEqual(2, sum(map(lambda x: len(x.hunks), diff)))
# self.assertIsNotNone is 2.7 only
self.assertTrue(diff is not None)
# self.assertIn is 2.7 only
self.assertEqual(2, sum(map(lambda x: len(x.hunks), diff)))
patch = diff[0]
hunk = patch.hunks[0]
self.assertEqual(hunk.old_start, 1)
self.assertEqual(hunk.old_lines, 1)
self.assertEqual(hunk.new_start, 1)
self.assertEqual(hunk.new_lines, 1)
patch = diff[0]
hunk = patch.hunks[0]
self.assertEqual(hunk.origin, '+')
self.assertEqual(hunk.old_start, 1)
self.assertEqual(hunk.old_lines, 1)
self.assertEqual(hunk.new_start, 1)
self.assertEqual(hunk.new_lines, 1)
self.assertEqual(patch.old_file_path, 'a')
self.assertEqual(patch.new_file_path, 'a')
_test(commit_a.tree.diff_to_tree(commit_b.tree))
_test(self.repo.diff(COMMIT_SHA1_1, COMMIT_SHA1_2))
self.assertEqual(patch.old_file_path, 'a')
self.assertEqual(patch.new_file_path, 'a')
def test_diff_empty_tree(self):
commit_a = self.repo[COMMIT_SHA1_1]
diff = commit_a.tree.diff(empty_tree=True)
diff = commit_a.tree.diff_to_tree()
def get_context_for_lines(diff):
hunks = chain(*map(lambda x: x.hunks, [p for p in diff]))
lines = chain(*map(lambda x: x.lines, hunks))
return map(lambda x: x[0], lines)
entries = [p.new_file_path for p in diff]
self.assertAll(lambda x: commit_a.tree[x], entries)
self.assertAll(lambda x: '-' == x, get_context_for_lines(diff))
diff_swaped = commit_a.tree.diff_to_tree(swap=True)
entries = [p.new_file_path for p in diff_swaped]
self.assertAll(lambda x: commit_a.tree[x], entries)
self.assertAll(lambda x: '+' == x, get_context_for_lines(diff_swaped))
def test_diff_revparse(self):
diff = self.repo.diff('HEAD','HEAD~6')
self.assertEqual(type(diff), pygit2.Diff)
def test_diff_tree_opts(self):
commit_c = self.repo[COMMIT_SHA1_3]
commit_d = self.repo[COMMIT_SHA1_4]
for opt in [pygit2.GIT_DIFF_IGNORE_WHITESPACE,
for flag in [pygit2.GIT_DIFF_IGNORE_WHITESPACE,
pygit2.GIT_DIFF_IGNORE_WHITESPACE_EOL]:
diff = commit_c.tree.diff(commit_d.tree, opt)
diff = commit_c.tree.diff_to_tree(commit_d.tree, flag)
self.assertTrue(diff is not None)
self.assertEqual(0, len(diff[0].hunks))
diff = commit_c.tree.diff(commit_d.tree)
diff = commit_c.tree.diff_to_tree(commit_d.tree)
self.assertTrue(diff is not None)
self.assertEqual(1, len(diff[0].hunks))
@@ -168,11 +219,11 @@ class DiffTest(utils.BareRepoTestCase):
commit_b = self.repo[COMMIT_SHA1_2]
commit_c = self.repo[COMMIT_SHA1_3]
diff_b = commit_a.tree.diff(commit_b.tree)
diff_b = commit_a.tree.diff_to_tree(commit_b.tree)
# self.assertIsNotNone is 2.7 only
self.assertTrue(diff_b is not None)
diff_c = commit_b.tree.diff(commit_c.tree)
diff_c = commit_b.tree.diff_to_tree(commit_c.tree)
# self.assertIsNotNone is 2.7 only
self.assertTrue(diff_c is not None)
@@ -199,13 +250,13 @@ class DiffTest(utils.BareRepoTestCase):
commit_a = self.repo[COMMIT_SHA1_1]
commit_b = self.repo[COMMIT_SHA1_2]
diff = commit_a.tree.diff(commit_b.tree)
diff = commit_a.tree.diff_to_tree(commit_b.tree)
self.assertEqual(diff.patch, PATCH)
def test_diff_oids(self):
commit_a = self.repo[COMMIT_SHA1_1]
commit_b = self.repo[COMMIT_SHA1_2]
patch = commit_a.tree.diff(commit_b.tree)[0]
patch = commit_a.tree.diff_to_tree(commit_b.tree)[0]
self.assertEqual(patch.old_oid,
'7f129fd57e31e935c6d60a0c794efe4e6927664b')
self.assertEqual(patch.new_oid,
@@ -214,9 +265,10 @@ class DiffTest(utils.BareRepoTestCase):
def test_hunk_content(self):
commit_a = self.repo[COMMIT_SHA1_1]
commit_b = self.repo[COMMIT_SHA1_2]
patch = commit_a.tree.diff(commit_b.tree)[0]
patch = commit_a.tree.diff_to_tree(commit_b.tree)[0]
hunk = patch.hunks[0]
self.assertEqual(HUNK_EXPECTED, ''.join(hunk.lines))
lines = ('{0} {1}'.format(*x) for x in hunk.lines)
self.assertEqual(HUNK_EXPECTED, ''.join(lines))
def test_find_similar(self):
commit_a = self.repo[COMMIT_SHA1_6]
@@ -224,7 +276,8 @@ class DiffTest(utils.BareRepoTestCase):
#~ Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate
#~ --find-copies-harder during rename transformion...
diff = commit_a.tree.diff(commit_b.tree, GIT_DIFF_INCLUDE_UNMODIFIED)
diff = commit_a.tree.diff_to_tree(commit_b.tree,
GIT_DIFF_INCLUDE_UNMODIFIED)
self.assertAll(lambda x: x.status != 'R', diff)
diff.find_similar()
self.assertAny(lambda x: x.status == 'R', diff)