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:
@@ -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
|
||||
====================
|
||||
|
@@ -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
|
||||
------------
|
||||
|
@@ -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
|
||||
|
@@ -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")
|
||||
|
38
src/diff.c
38
src/diff.c
@@ -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."),
|
||||
|
@@ -41,4 +41,6 @@
|
||||
PyObject* Diff_changes(Diff *self);
|
||||
PyObject* Diff_patch(Diff *self);
|
||||
|
||||
PyObject* wrap_diff(git_diff_list *diff, Repository *repo);
|
||||
|
||||
#endif
|
||||
|
99
src/index.c
99
src/index.c
@@ -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),
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
}
|
||||
|
161
src/tree.c
161
src/tree.c
@@ -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}
|
||||
};
|
||||
|
||||
|
@@ -118,7 +118,6 @@ typedef struct {
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PyObject* lines;
|
||||
char origin;
|
||||
int old_start;
|
||||
int old_lines;
|
||||
int new_start;
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user