Merge remote-tracking branch 'petr/blame'
This commit is contained in:
commit
ad05df422b
384
src/blame.c
Normal file
384
src/blame.c
Normal file
@ -0,0 +1,384 @@
|
||||
/*
|
||||
* Copyright 2010-2013 The pygit2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
#include <structmember.h>
|
||||
#include "error.h"
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
#include "signature.h"
|
||||
#include "blame.h"
|
||||
|
||||
extern PyObject *GitError;
|
||||
|
||||
extern PyTypeObject BlameType;
|
||||
extern PyTypeObject BlameIterType;
|
||||
extern PyTypeObject BlameHunkType;
|
||||
|
||||
PyObject*
|
||||
wrap_blame(git_blame *blame, Repository *repo)
|
||||
{
|
||||
Blame *py_blame;
|
||||
|
||||
py_blame = PyObject_New(Blame, &BlameType);
|
||||
if (py_blame) {
|
||||
Py_INCREF(repo);
|
||||
py_blame->repo = repo;
|
||||
py_blame->blame = blame;
|
||||
}
|
||||
|
||||
return (PyObject*) py_blame;
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
PyObject*
|
||||
wrap_blame_hunk(const git_blame_hunk *hunk, Blame *blame)
|
||||
{
|
||||
BlameHunk *py_hunk = NULL;
|
||||
|
||||
if (!hunk)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
py_hunk = PyObject_New(BlameHunk, &BlameHunkType);
|
||||
if (py_hunk != NULL) {
|
||||
py_hunk->lines_in_hunk = hunk->lines_in_hunk;
|
||||
py_hunk->final_commit_id = git_oid_allocfmt(&hunk->final_commit_id);
|
||||
py_hunk->final_start_line_number = hunk->final_start_line_number;
|
||||
py_hunk->final_signature = hunk->final_signature != NULL ?
|
||||
git_signature_dup(hunk->final_signature) : NULL;
|
||||
py_hunk->orig_commit_id = git_oid_allocfmt(&hunk->orig_commit_id);
|
||||
py_hunk->orig_path = hunk->orig_path != NULL ?
|
||||
strdup(hunk->orig_path) : NULL;
|
||||
py_hunk->orig_start_line_number = hunk->orig_start_line_number;
|
||||
py_hunk->orig_signature = hunk->orig_signature != NULL ?
|
||||
git_signature_dup(hunk->orig_signature) : NULL;
|
||||
py_hunk->boundary = hunk->boundary;
|
||||
}
|
||||
|
||||
return (PyObject*) py_hunk;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(BlameHunk_final_committer__doc__, "Final committer.");
|
||||
|
||||
PyObject *
|
||||
BlameHunk_final_committer__get__(BlameHunk *self)
|
||||
{
|
||||
if (!self->final_signature)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
return build_signature((Object*) self, self->final_signature, "utf-8");
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(BlameHunk_orig_committer__doc__, "Origin committer.");
|
||||
|
||||
PyObject *
|
||||
BlameHunk_orig_committer__get__(BlameHunk *self)
|
||||
{
|
||||
if (!self->orig_signature)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
return build_signature((Object*) self, self->orig_signature, "utf-8");
|
||||
}
|
||||
|
||||
static int
|
||||
BlameHunk_init(BlameHunk *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
self->final_commit_id = NULL;
|
||||
self->final_signature = NULL;
|
||||
self->orig_commit_id = NULL;
|
||||
self->orig_path = NULL;
|
||||
self->orig_signature = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
BlameHunk_dealloc(BlameHunk *self)
|
||||
{
|
||||
free(self->final_commit_id);
|
||||
if (self->final_signature)
|
||||
git_signature_free(self->final_signature);
|
||||
free(self->orig_commit_id);
|
||||
if (self->orig_path)
|
||||
free(self->orig_path);
|
||||
if (self->orig_signature)
|
||||
git_signature_free(self->orig_signature);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
PyMemberDef BlameHunk_members[] = {
|
||||
MEMBER(BlameHunk, lines_in_hunk, T_UINT, "Number of lines."),
|
||||
MEMBER(BlameHunk, final_commit_id, T_STRING, "Last changed oid."),
|
||||
MEMBER(BlameHunk, final_start_line_number, T_UINT, "final start line no."),
|
||||
MEMBER(BlameHunk, orig_commit_id, T_STRING, "oid where hunk was found."),
|
||||
MEMBER(BlameHunk, orig_path, T_STRING, "Origin path."),
|
||||
MEMBER(BlameHunk, orig_start_line_number, T_UINT, "Origin start line no."),
|
||||
MEMBER(BlameHunk, boundary, T_BOOL, "Tracked to a boundary commit."),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyGetSetDef BlameHunk_getseters[] = {
|
||||
GETTER(BlameHunk, final_committer),
|
||||
GETTER(BlameHunk, orig_committer),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(BlameHunk__doc__, "Blame Hunk object.");
|
||||
|
||||
PyTypeObject BlameHunkType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_pygit2.BlameHunk", /* tp_name */
|
||||
sizeof(BlameHunk), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)BlameHunk_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 */
|
||||
BlameHunk__doc__, /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
BlameHunk_members, /* tp_members */
|
||||
BlameHunk_getseters, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
(initproc)BlameHunk_init, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
0, /* tp_new */
|
||||
};
|
||||
|
||||
|
||||
PyObject *
|
||||
BlameIter_iternext(BlameIter *self)
|
||||
{
|
||||
if (self->i < self->n)
|
||||
return wrap_blame_hunk(git_blame_get_hunk_byindex(
|
||||
self->blame->blame, self->i++), self->blame);
|
||||
|
||||
PyErr_SetNone(PyExc_StopIteration);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
BlameIter_dealloc(BlameIter *self)
|
||||
{
|
||||
Py_CLEAR(self->blame);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(BlameIter__doc__, "Blame iterator object.");
|
||||
|
||||
PyTypeObject BlameIterType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_pygit2.BlameIter", /* tp_name */
|
||||
sizeof(BlameIter), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)BlameIter_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 */
|
||||
BlameIter__doc__, /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
PyObject_SelfIter, /* tp_iter */
|
||||
(iternextfunc) BlameIter_iternext, /* tp_iternext */
|
||||
};
|
||||
|
||||
|
||||
PyObject *
|
||||
Blame_iter(Blame *self)
|
||||
{
|
||||
BlameIter *iter;
|
||||
|
||||
iter = PyObject_New(BlameIter, &BlameIterType);
|
||||
if (iter != NULL) {
|
||||
Py_INCREF(self);
|
||||
iter->blame = self;
|
||||
iter->i = 0;
|
||||
iter->n = git_blame_get_hunk_count(self->blame);
|
||||
}
|
||||
return (PyObject*)iter;
|
||||
}
|
||||
|
||||
Py_ssize_t
|
||||
Blame_len(Blame *self)
|
||||
{
|
||||
assert(self->blame);
|
||||
return (Py_ssize_t)git_blame_get_hunk_count(self->blame);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
Blame_getitem(Blame *self, PyObject *value)
|
||||
{
|
||||
size_t i;
|
||||
const git_blame_hunk *hunk;
|
||||
|
||||
if (PyLong_Check(value) < 0) {
|
||||
PyErr_SetObject(PyExc_IndexError, value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i = PyLong_AsUnsignedLong(value);
|
||||
if (PyErr_Occurred()) {
|
||||
PyErr_SetObject(PyExc_IndexError, value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hunk = git_blame_get_hunk_byindex(self->blame, i);
|
||||
if (!hunk) {
|
||||
PyErr_SetObject(PyExc_IndexError, value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return wrap_blame_hunk(hunk, self);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Blame_for_line__doc__,
|
||||
"for_line(line_no) -> hunk\n"
|
||||
"\n"
|
||||
"Returns the blame hunk data for the given \"line_no\" in blame.\n"
|
||||
"\n"
|
||||
"Arguments:\n"
|
||||
"\n"
|
||||
"line_no\n"
|
||||
" Line number, countings starts with 1.");
|
||||
|
||||
PyObject *
|
||||
Blame_for_line(Blame *self, PyObject *args)
|
||||
{
|
||||
size_t line_no;
|
||||
const git_blame_hunk *hunk;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "I", &line_no))
|
||||
return NULL;
|
||||
|
||||
hunk = git_blame_get_hunk_byline(self->blame, line_no);
|
||||
if (!hunk) {
|
||||
PyErr_SetObject(PyExc_IndexError, args);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return wrap_blame_hunk(hunk, self);
|
||||
}
|
||||
|
||||
static void
|
||||
Blame_dealloc(Blame *self)
|
||||
{
|
||||
git_blame_free(self->blame);
|
||||
Py_CLEAR(self->repo);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
PyMappingMethods Blame_as_mapping = {
|
||||
(lenfunc)Blame_len, /* mp_length */
|
||||
(binaryfunc)Blame_getitem, /* mp_subscript */
|
||||
0, /* mp_ass_subscript */
|
||||
};
|
||||
|
||||
static PyMethodDef Blame_methods[] = {
|
||||
METHOD(Blame, for_line, METH_VARARGS),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
||||
PyDoc_STRVAR(Blame__doc__, "Blame objects.");
|
||||
|
||||
PyTypeObject BlameType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_pygit2.Blame", /* tp_name */
|
||||
sizeof(Blame), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)Blame_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 */
|
||||
&Blame_as_mapping, /* 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 */
|
||||
Blame__doc__, /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
(getiterfunc)Blame_iter, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
Blame_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* 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 */
|
||||
};
|
38
src/blame.h
Normal file
38
src/blame.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2013 The pygit2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_pygit2_blame_h
|
||||
#define INCLUDE_pygit2_blame_h
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
#include <git2.h>
|
||||
#include "types.h"
|
||||
|
||||
PyObject* wrap_blame(git_blame *blame, Repository *repo);
|
||||
|
||||
#endif
|
15
src/pygit2.c
15
src/pygit2.c
@ -64,6 +64,9 @@ extern PyTypeObject SignatureType;
|
||||
extern PyTypeObject RemoteType;
|
||||
extern PyTypeObject NoteType;
|
||||
extern PyTypeObject NoteIterType;
|
||||
extern PyTypeObject BlameType;
|
||||
extern PyTypeObject BlameIterType;
|
||||
extern PyTypeObject BlameHunkType;
|
||||
|
||||
|
||||
|
||||
@ -405,6 +408,18 @@ moduleinit(PyObject* m)
|
||||
INIT_TYPE(RemoteType, NULL, NULL)
|
||||
ADD_TYPE(m, Remote)
|
||||
|
||||
/* Blame */
|
||||
INIT_TYPE(BlameType, NULL, NULL)
|
||||
INIT_TYPE(BlameIterType, NULL, NULL)
|
||||
INIT_TYPE(BlameHunkType, NULL, NULL)
|
||||
ADD_TYPE(m, Blame)
|
||||
ADD_TYPE(m, BlameHunk)
|
||||
ADD_CONSTANT_INT(m, GIT_BLAME_NORMAL)
|
||||
ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_SAME_FILE)
|
||||
ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES)
|
||||
ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES)
|
||||
ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES)
|
||||
|
||||
/* Global initialization of libgit2 */
|
||||
git_threads_init();
|
||||
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "repository.h"
|
||||
#include "remote.h"
|
||||
#include "branch.h"
|
||||
#include "blame.h"
|
||||
#include <git2/odb_backend.h>
|
||||
|
||||
extern PyObject *GitError;
|
||||
@ -1430,6 +1431,71 @@ Repository_lookup_note(Repository *self, PyObject* args)
|
||||
return (PyObject*) wrap_note(self, &annotated_id, ref);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Repository_blame__doc__,
|
||||
"blame(path, [flags, min_match_characters, newest_commit, oldest_commit,\n"
|
||||
" min_line, max_line]) -> blame\n"
|
||||
"\n"
|
||||
"Get the blame for a single file.\n"
|
||||
"\n"
|
||||
"Arguments:\n"
|
||||
"\n"
|
||||
"path\n"
|
||||
" A path to file to consider.\n"
|
||||
"flags\n"
|
||||
" A GIT_BLAME_* constant.\n"
|
||||
"min_match_characters\n"
|
||||
" The number of alphanum chars that must be detected as moving/copying\n"
|
||||
" within a file for it to associate those lines with the parent commit.\n"
|
||||
"newest_commit\n"
|
||||
" The id of the newest commit to consider.\n"
|
||||
"oldest_commit\n"
|
||||
" The id of the oldest commit to consider.\n"
|
||||
"min_line\n"
|
||||
" The first line in the file to blame.\n"
|
||||
"max_line\n"
|
||||
" The last line in the file to blame.\n"
|
||||
"\n"
|
||||
"Examples::\n"
|
||||
"\n"
|
||||
" repo.blame('foo.c', flags=GIT_BLAME_TRACK_COPIES_SAME_FILE)");
|
||||
|
||||
PyObject* Repository_blame(Repository *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
|
||||
git_blame *blame;
|
||||
char *path;
|
||||
PyObject *value1 = NULL;
|
||||
PyObject *value2 = NULL;
|
||||
int err;
|
||||
char *keywords[] = {"flags", "min_match_characters", "newest_commit",
|
||||
"oldest_commit", "min_line", "max_line", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|IHOOII", keywords,
|
||||
&path, &opts.flags,
|
||||
&opts.min_match_characters,
|
||||
&value1, &value2,
|
||||
&opts.min_line, &opts.max_line))
|
||||
return NULL;
|
||||
|
||||
if (value1) {
|
||||
err = py_oid_to_git_oid_expand(self->repo, value1, &opts.newest_commit);
|
||||
if (err < 0)
|
||||
return NULL;
|
||||
}
|
||||
if (value2) {
|
||||
err = py_oid_to_git_oid_expand(self->repo, value2, &opts.oldest_commit);
|
||||
if (err < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = git_blame_file(&blame, self->repo, path, NULL);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
return wrap_blame(blame, self);
|
||||
}
|
||||
|
||||
|
||||
PyMethodDef Repository_methods[] = {
|
||||
METHOD(Repository, create_blob, METH_VARARGS),
|
||||
METHOD(Repository, create_blob_fromworkdir, METH_VARARGS),
|
||||
@ -1459,6 +1525,7 @@ PyMethodDef Repository_methods[] = {
|
||||
METHOD(Repository, lookup_branch, METH_VARARGS),
|
||||
METHOD(Repository, listall_branches, METH_VARARGS),
|
||||
METHOD(Repository, create_branch, METH_VARARGS),
|
||||
METHOD(Repository, blame, METH_VARARGS | METH_KEYWORDS),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
@ -67,4 +67,6 @@ PyObject* Repository_status(Repository *self, PyObject *args);
|
||||
PyObject* Repository_status_file(Repository *self, PyObject *value);
|
||||
PyObject* Repository_TreeBuilder(Repository *self, PyObject *args);
|
||||
|
||||
PyObject* Repository_blame(Repository *self, PyObject *args, PyObject *kwds);
|
||||
|
||||
#endif
|
||||
|
24
src/types.h
24
src/types.h
@ -194,4 +194,28 @@ typedef struct {
|
||||
SIMPLE_TYPE(Remote, git_remote, remote)
|
||||
|
||||
|
||||
/* git_blame */
|
||||
SIMPLE_TYPE(Blame, git_blame, blame)
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
Blame* blame;
|
||||
size_t i;
|
||||
size_t n;
|
||||
} BlameIter;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
unsigned lines_in_hunk;
|
||||
char* final_commit_id;
|
||||
unsigned final_start_line_number;
|
||||
git_signature* final_signature;
|
||||
char* orig_commit_id;
|
||||
char* orig_path;
|
||||
unsigned orig_start_line_number;
|
||||
git_signature* orig_signature;
|
||||
char boundary;
|
||||
} BlameHunk;
|
||||
|
||||
|
||||
#endif
|
||||
|
111
test/test_blame.py
Normal file
111
test/test_blame.py
Normal file
@ -0,0 +1,111 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2013 The pygit2 contributors
|
||||
#
|
||||
# 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 Blame objects."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
import pygit2
|
||||
from pygit2 import Signature
|
||||
from pygit2 import GIT_DIFF_INCLUDE_UNMODIFIED
|
||||
from pygit2 import GIT_DIFF_IGNORE_WHITESPACE, GIT_DIFF_IGNORE_WHITESPACE_EOL
|
||||
from . import utils
|
||||
from itertools import chain
|
||||
from datetime import datetime
|
||||
|
||||
PATH = 'hello.txt'
|
||||
|
||||
HUNKS = [
|
||||
('acecd5ea2924a4b900e7e149496e1f4b57976e51', 1,
|
||||
Signature('J. David Ibañez', 'jdavid@itaapy.com',
|
||||
1297179898, 60, encoding='utf-8'), True),
|
||||
('6aaa262e655dd54252e5813c8e5acd7780ed097d', 2,
|
||||
Signature('J. David Ibañez', 'jdavid@itaapy.com',
|
||||
1297696877, 60, encoding='utf-8'), False),
|
||||
('4ec4389a8068641da2d6578db0419484972284c8', 3,
|
||||
Signature('J. David Ibañez', 'jdavid@itaapy.com',
|
||||
1297696908, 60, encoding='utf-8'), False)
|
||||
]
|
||||
|
||||
class BlameTest(utils.RepoTestCase):
|
||||
|
||||
def test_blame_index(self):
|
||||
repo = self.repo
|
||||
blame = repo.blame(PATH)
|
||||
|
||||
self.assertEqual(len(blame), 3)
|
||||
|
||||
for i, hunk in enumerate(blame):
|
||||
self.assertEqual(hunk.lines_in_hunk, 1)
|
||||
self.assertEqual(HUNKS[i][0], hunk.final_commit_id)
|
||||
self.assertEqual(HUNKS[i][1], hunk.final_start_line_number)
|
||||
self.assertEqualSignature(HUNKS[i][2], hunk.final_committer)
|
||||
self.assertEqual(hunk.orig_commit_id,
|
||||
'0000000000000000000000000000000000000000')
|
||||
self.assertEqual(hunk.orig_path, PATH)
|
||||
self.assertEqual(HUNKS[i][1], hunk.orig_start_line_number)
|
||||
self.assertIsNone(hunk.orig_committer)
|
||||
self.assertEqual(HUNKS[i][3], hunk.boundary)
|
||||
|
||||
def test_blame_with_invalid_index(self):
|
||||
repo = self.repo
|
||||
blame = repo.blame(PATH)
|
||||
|
||||
with self.assertRaises(IndexError):
|
||||
blame[100000]
|
||||
blame[-1]
|
||||
|
||||
def test_blame_for_line(self):
|
||||
repo = self.repo
|
||||
blame = repo.blame(PATH)
|
||||
|
||||
for i, line in zip(range(0, 2), range(1, 3)):
|
||||
hunk = blame.for_line(line)
|
||||
|
||||
self.assertEqual(hunk.lines_in_hunk, 1)
|
||||
self.assertEqual(HUNKS[i][0], hunk.final_commit_id)
|
||||
self.assertEqual(HUNKS[i][1], hunk.final_start_line_number)
|
||||
self.assertEqualSignature(HUNKS[i][2], hunk.final_committer)
|
||||
self.assertEqual(hunk.orig_commit_id,
|
||||
'0000000000000000000000000000000000000000')
|
||||
self.assertEqual(hunk.orig_path, PATH)
|
||||
self.assertEqual(HUNKS[i][1], hunk.orig_start_line_number)
|
||||
self.assertIsNone(hunk.orig_committer)
|
||||
self.assertEqual(HUNKS[i][3], hunk.boundary)
|
||||
|
||||
def test_blame_with_invalid_line(self):
|
||||
repo = self.repo
|
||||
blame = repo.blame(PATH)
|
||||
|
||||
with self.assertRaises(IndexError):
|
||||
blame.for_line(0)
|
||||
blame.for_line(100000)
|
||||
blame.for_line(-1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue
Block a user