|
| 1 | +/* |
| 2 | + * Copyright 2010-2013 The pygit2 contributors |
| 3 | + * |
| 4 | + * This file is free software; you can redistribute it and/or modify |
| 5 | + * it under the terms of the GNU General Public License, version 2, |
| 6 | + * as published by the Free Software Foundation. |
| 7 | + * |
| 8 | + * In addition to the permissions in the GNU General Public License, |
| 9 | + * the authors give you unlimited permission to link the compiled |
| 10 | + * version of this file into combinations with other programs, |
| 11 | + * and to distribute those combinations without any restriction |
| 12 | + * coming from the use of this file. (The General Public License |
| 13 | + * restrictions do apply in other respects; for example, they cover |
| 14 | + * modification of the file, and distribution when not linked into |
| 15 | + * a combined executable.) |
| 16 | + * |
| 17 | + * This file is distributed in the hope that it will be useful, but |
| 18 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 19 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 20 | + * General Public License for more details. |
| 21 | + * |
| 22 | + * You should have received a copy of the GNU General Public License |
| 23 | + * along with this program; see the file COPYING. If not, write to |
| 24 | + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, |
| 25 | + * Boston, MA 02110-1301, USA. |
| 26 | + */ |
| 27 | + |
| 28 | +#define PY_SSIZE_T_CLEAN |
| 29 | +#include <Python.h> |
| 30 | +#include <structmember.h> |
| 31 | +#include "error.h" |
| 32 | +#include "types.h" |
| 33 | +#include "utils.h" |
| 34 | +#include "signature.h" |
| 35 | +#include "blame.h" |
| 36 | + |
| 37 | +extern PyObject *GitError; |
| 38 | + |
| 39 | +extern PyTypeObject BlameType; |
| 40 | +extern PyTypeObject BlameIterType; |
| 41 | +extern PyTypeObject BlameHunkType; |
| 42 | + |
| 43 | +PyObject* |
| 44 | +wrap_blame(git_blame *blame, Repository *repo) |
| 45 | +{ |
| 46 | + Blame *py_blame; |
| 47 | + |
| 48 | + py_blame = PyObject_New(Blame, &BlameType); |
| 49 | + if (py_blame) { |
| 50 | + Py_INCREF(repo); |
| 51 | + py_blame->repo = repo; |
| 52 | + py_blame->blame = blame; |
| 53 | + } |
| 54 | + |
| 55 | + return (PyObject*) py_blame; |
| 56 | +} |
| 57 | + |
| 58 | +#include <stdio.h> |
| 59 | +PyObject* |
| 60 | +wrap_blame_hunk(const git_blame_hunk *hunk, Blame *blame) |
| 61 | +{ |
| 62 | + BlameHunk *py_hunk = NULL; |
| 63 | + |
| 64 | + if (!hunk) |
| 65 | + Py_RETURN_NONE; |
| 66 | + |
| 67 | + py_hunk = PyObject_New(BlameHunk, &BlameHunkType); |
| 68 | + if (py_hunk != NULL) { |
| 69 | + py_hunk->lines_in_hunk = hunk->lines_in_hunk; |
| 70 | + py_hunk->final_commit_id = git_oid_allocfmt(&hunk->final_commit_id); |
| 71 | + py_hunk->final_start_line_number = hunk->final_start_line_number; |
| 72 | + py_hunk->final_signature = hunk->final_signature != NULL ? |
| 73 | + git_signature_dup(hunk->final_signature) : NULL; |
| 74 | + py_hunk->orig_commit_id = git_oid_allocfmt(&hunk->orig_commit_id); |
| 75 | + py_hunk->orig_path = hunk->orig_path != NULL ? |
| 76 | + strdup(hunk->orig_path) : NULL; |
| 77 | + py_hunk->orig_start_line_number = hunk->orig_start_line_number; |
| 78 | + py_hunk->orig_signature = hunk->orig_signature != NULL ? |
| 79 | + git_signature_dup(hunk->orig_signature) : NULL; |
| 80 | + py_hunk->boundary = hunk->boundary; |
| 81 | + } |
| 82 | + |
| 83 | + return (PyObject*) py_hunk; |
| 84 | +} |
| 85 | + |
| 86 | +PyDoc_STRVAR(BlameHunk_final_committer__doc__, "Final committer."); |
| 87 | + |
| 88 | +PyObject * |
| 89 | +BlameHunk_final_committer__get__(BlameHunk *self) |
| 90 | +{ |
| 91 | + if (!self->final_signature) |
| 92 | + Py_RETURN_NONE; |
| 93 | + |
| 94 | + return build_signature((Object*) self, self->final_signature, "utf-8"); |
| 95 | +} |
| 96 | + |
| 97 | +PyDoc_STRVAR(BlameHunk_orig_committer__doc__, "Origin committer."); |
| 98 | + |
| 99 | +PyObject * |
| 100 | +BlameHunk_orig_committer__get__(BlameHunk *self) |
| 101 | +{ |
| 102 | + if (!self->orig_signature) |
| 103 | + Py_RETURN_NONE; |
| 104 | + |
| 105 | + return build_signature((Object*) self, self->orig_signature, "utf-8"); |
| 106 | +} |
| 107 | + |
| 108 | +static int |
| 109 | +BlameHunk_init(BlameHunk *self, PyObject *args, PyObject *kwds) |
| 110 | +{ |
| 111 | + self->final_commit_id = NULL; |
| 112 | + self->final_signature = NULL; |
| 113 | + self->orig_commit_id = NULL; |
| 114 | + self->orig_path = NULL; |
| 115 | + self->orig_signature = NULL; |
| 116 | + |
| 117 | + return 0; |
| 118 | +} |
| 119 | + |
| 120 | +static void |
| 121 | +BlameHunk_dealloc(BlameHunk *self) |
| 122 | +{ |
| 123 | + free(self->final_commit_id); |
| 124 | + if (self->final_signature) |
| 125 | + git_signature_free(self->final_signature); |
| 126 | + free(self->orig_commit_id); |
| 127 | + if (self->orig_path) |
| 128 | + free(self->orig_path); |
| 129 | + if (self->orig_signature) |
| 130 | + git_signature_free(self->orig_signature); |
| 131 | + PyObject_Del(self); |
| 132 | +} |
| 133 | + |
| 134 | +PyMemberDef BlameHunk_members[] = { |
| 135 | + MEMBER(BlameHunk, lines_in_hunk, T_UINT, "Number of lines."), |
| 136 | + MEMBER(BlameHunk, final_commit_id, T_STRING, "Last changed oid."), |
| 137 | + MEMBER(BlameHunk, final_start_line_number, T_UINT, "final start line no."), |
| 138 | + MEMBER(BlameHunk, orig_commit_id, T_STRING, "oid where hunk was found."), |
| 139 | + MEMBER(BlameHunk, orig_path, T_STRING, "Origin path."), |
| 140 | + MEMBER(BlameHunk, orig_start_line_number, T_UINT, "Origin start line no."), |
| 141 | + MEMBER(BlameHunk, boundary, T_BOOL, "Tracked to a boundary commit."), |
| 142 | + {NULL} |
| 143 | +}; |
| 144 | + |
| 145 | +PyGetSetDef BlameHunk_getseters[] = { |
| 146 | + GETTER(BlameHunk, final_committer), |
| 147 | + GETTER(BlameHunk, orig_committer), |
| 148 | + {NULL} |
| 149 | +}; |
| 150 | + |
| 151 | +PyDoc_STRVAR(BlameHunk__doc__, "Blame Hunk object."); |
| 152 | + |
| 153 | +PyTypeObject BlameHunkType = { |
| 154 | + PyVarObject_HEAD_INIT(NULL, 0) |
| 155 | + "_pygit2.BlameHunk", /* tp_name */ |
| 156 | + sizeof(BlameHunk), /* tp_basicsize */ |
| 157 | + 0, /* tp_itemsize */ |
| 158 | + (destructor)BlameHunk_dealloc, /* tp_dealloc */ |
| 159 | + 0, /* tp_print */ |
| 160 | + 0, /* tp_getattr */ |
| 161 | + 0, /* tp_setattr */ |
| 162 | + 0, /* tp_compare */ |
| 163 | + 0, /* tp_repr */ |
| 164 | + 0, /* tp_as_number */ |
| 165 | + 0, /* tp_as_sequence */ |
| 166 | + 0, /* tp_as_mapping */ |
| 167 | + 0, /* tp_hash */ |
| 168 | + 0, /* tp_call */ |
| 169 | + 0, /* tp_str */ |
| 170 | + 0, /* tp_getattro */ |
| 171 | + 0, /* tp_setattro */ |
| 172 | + 0, /* tp_as_buffer */ |
| 173 | + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ |
| 174 | + BlameHunk__doc__, /* tp_doc */ |
| 175 | + 0, /* tp_traverse */ |
| 176 | + 0, /* tp_clear */ |
| 177 | + 0, /* tp_richcompare */ |
| 178 | + 0, /* tp_weaklistoffset */ |
| 179 | + 0, /* tp_iter */ |
| 180 | + 0, /* tp_iternext */ |
| 181 | + 0, /* tp_methods */ |
| 182 | + BlameHunk_members, /* tp_members */ |
| 183 | + BlameHunk_getseters, /* tp_getset */ |
| 184 | + 0, /* tp_base */ |
| 185 | + 0, /* tp_dict */ |
| 186 | + 0, /* tp_descr_get */ |
| 187 | + 0, /* tp_descr_set */ |
| 188 | + 0, /* tp_dictoffset */ |
| 189 | + (initproc)BlameHunk_init, /* tp_init */ |
| 190 | + 0, /* tp_alloc */ |
| 191 | + 0, /* tp_new */ |
| 192 | +}; |
| 193 | + |
| 194 | + |
| 195 | +PyObject * |
| 196 | +BlameIter_iternext(BlameIter *self) |
| 197 | +{ |
| 198 | + if (self->i < self->n) |
| 199 | + return wrap_blame_hunk(git_blame_get_hunk_byindex( |
| 200 | + self->blame->blame, self->i++), self->blame); |
| 201 | + |
| 202 | + PyErr_SetNone(PyExc_StopIteration); |
| 203 | + return NULL; |
| 204 | +} |
| 205 | + |
| 206 | +static void |
| 207 | +BlameIter_dealloc(BlameIter *self) |
| 208 | +{ |
| 209 | + Py_CLEAR(self->blame); |
| 210 | + PyObject_Del(self); |
| 211 | +} |
| 212 | + |
| 213 | + |
| 214 | +PyDoc_STRVAR(BlameIter__doc__, "Blame iterator object."); |
| 215 | + |
| 216 | +PyTypeObject BlameIterType = { |
| 217 | + PyVarObject_HEAD_INIT(NULL, 0) |
| 218 | + "_pygit2.BlameIter", /* tp_name */ |
| 219 | + sizeof(BlameIter), /* tp_basicsize */ |
| 220 | + 0, /* tp_itemsize */ |
| 221 | + (destructor)BlameIter_dealloc, /* tp_dealloc */ |
| 222 | + 0, /* tp_print */ |
| 223 | + 0, /* tp_getattr */ |
| 224 | + 0, /* tp_setattr */ |
| 225 | + 0, /* tp_compare */ |
| 226 | + 0, /* tp_repr */ |
| 227 | + 0, /* tp_as_number */ |
| 228 | + 0, /* tp_as_sequence */ |
| 229 | + 0, /* tp_as_mapping */ |
| 230 | + 0, /* tp_hash */ |
| 231 | + 0, /* tp_call */ |
| 232 | + 0, /* tp_str */ |
| 233 | + 0, /* tp_getattro */ |
| 234 | + 0, /* tp_setattro */ |
| 235 | + 0, /* tp_as_buffer */ |
| 236 | + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ |
| 237 | + BlameIter__doc__, /* tp_doc */ |
| 238 | + 0, /* tp_traverse */ |
| 239 | + 0, /* tp_clear */ |
| 240 | + 0, /* tp_richcompare */ |
| 241 | + 0, /* tp_weaklistoffset */ |
| 242 | + PyObject_SelfIter, /* tp_iter */ |
| 243 | + (iternextfunc) BlameIter_iternext, /* tp_iternext */ |
| 244 | +}; |
| 245 | + |
| 246 | + |
| 247 | +PyObject * |
| 248 | +Blame_iter(Blame *self) |
| 249 | +{ |
| 250 | + BlameIter *iter; |
| 251 | + |
| 252 | + iter = PyObject_New(BlameIter, &BlameIterType); |
| 253 | + if (iter != NULL) { |
| 254 | + Py_INCREF(self); |
| 255 | + iter->blame = self; |
| 256 | + iter->i = 0; |
| 257 | + iter->n = git_blame_get_hunk_count(self->blame); |
| 258 | + } |
| 259 | + return (PyObject*)iter; |
| 260 | +} |
| 261 | + |
| 262 | +Py_ssize_t |
| 263 | +Blame_len(Blame *self) |
| 264 | +{ |
| 265 | + assert(self->blame); |
| 266 | + return (Py_ssize_t)git_blame_get_hunk_count(self->blame); |
| 267 | +} |
| 268 | + |
| 269 | +PyObject * |
| 270 | +Blame_getitem(Blame *self, PyObject *value) |
| 271 | +{ |
| 272 | + size_t i; |
| 273 | + const git_blame_hunk *hunk; |
| 274 | + |
| 275 | + if (PyLong_Check(value) < 0) { |
| 276 | + PyErr_SetObject(PyExc_IndexError, value); |
| 277 | + return NULL; |
| 278 | + } |
| 279 | + |
| 280 | + i = PyLong_AsUnsignedLong(value); |
| 281 | + if (PyErr_Occurred()) { |
| 282 | + PyErr_SetObject(PyExc_IndexError, value); |
| 283 | + return NULL; |
| 284 | + } |
| 285 | + |
| 286 | + hunk = git_blame_get_hunk_byindex(self->blame, i); |
| 287 | + if (!hunk) { |
| 288 | + PyErr_SetObject(PyExc_IndexError, value); |
| 289 | + return NULL; |
| 290 | + } |
| 291 | + |
| 292 | + return wrap_blame_hunk(hunk, self); |
| 293 | +} |
| 294 | + |
| 295 | +PyDoc_STRVAR(Blame_for_line__doc__, |
| 296 | + "for_line(line_no) -> hunk\n" |
| 297 | + "\n" |
| 298 | + "Returns the blame hunk data for the given \"line_no\" in blame.\n" |
| 299 | + "\n" |
| 300 | + "Arguments:\n" |
| 301 | + "\n" |
| 302 | + "line_no\n" |
| 303 | + " Line number, countings starts with 1."); |
| 304 | + |
| 305 | +PyObject * |
| 306 | +Blame_for_line(Blame *self, PyObject *args) |
| 307 | +{ |
| 308 | + size_t line_no; |
| 309 | + const git_blame_hunk *hunk; |
| 310 | + |
| 311 | + if (!PyArg_ParseTuple(args, "I", &line_no)) |
| 312 | + return NULL; |
| 313 | + |
| 314 | + hunk = git_blame_get_hunk_byline(self->blame, line_no); |
| 315 | + if (!hunk) { |
| 316 | + PyErr_SetObject(PyExc_IndexError, args); |
| 317 | + return NULL; |
| 318 | + } |
| 319 | + |
| 320 | + return wrap_blame_hunk(hunk, self); |
| 321 | +} |
| 322 | + |
| 323 | +static void |
| 324 | +Blame_dealloc(Blame *self) |
| 325 | +{ |
| 326 | + git_blame_free(self->blame); |
| 327 | + Py_CLEAR(self->repo); |
| 328 | + PyObject_Del(self); |
| 329 | +} |
| 330 | + |
| 331 | +PyMappingMethods Blame_as_mapping = { |
| 332 | + (lenfunc)Blame_len, /* mp_length */ |
| 333 | + (binaryfunc)Blame_getitem, /* mp_subscript */ |
| 334 | + 0, /* mp_ass_subscript */ |
| 335 | +}; |
| 336 | + |
| 337 | +static PyMethodDef Blame_methods[] = { |
| 338 | + METHOD(Blame, for_line, METH_VARARGS), |
| 339 | + {NULL} |
| 340 | +}; |
| 341 | + |
| 342 | + |
| 343 | +PyDoc_STRVAR(Blame__doc__, "Blame objects."); |
| 344 | + |
| 345 | +PyTypeObject BlameType = { |
| 346 | + PyVarObject_HEAD_INIT(NULL, 0) |
| 347 | + "_pygit2.Blame", /* tp_name */ |
| 348 | + sizeof(Blame), /* tp_basicsize */ |
| 349 | + 0, /* tp_itemsize */ |
| 350 | + (destructor)Blame_dealloc, /* tp_dealloc */ |
| 351 | + 0, /* tp_print */ |
| 352 | + 0, /* tp_getattr */ |
| 353 | + 0, /* tp_setattr */ |
| 354 | + 0, /* tp_compare */ |
| 355 | + 0, /* tp_repr */ |
| 356 | + 0, /* tp_as_number */ |
| 357 | + 0, /* tp_as_sequence */ |
| 358 | + &Blame_as_mapping, /* tp_as_mapping */ |
| 359 | + 0, /* tp_hash */ |
| 360 | + 0, /* tp_call */ |
| 361 | + 0, /* tp_str */ |
| 362 | + 0, /* tp_getattro */ |
| 363 | + 0, /* tp_setattro */ |
| 364 | + 0, /* tp_as_buffer */ |
| 365 | + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ |
| 366 | + Blame__doc__, /* tp_doc */ |
| 367 | + 0, /* tp_traverse */ |
| 368 | + 0, /* tp_clear */ |
| 369 | + 0, /* tp_richcompare */ |
| 370 | + 0, /* tp_weaklistoffset */ |
| 371 | + (getiterfunc)Blame_iter, /* tp_iter */ |
| 372 | + 0, /* tp_iternext */ |
| 373 | + Blame_methods, /* tp_methods */ |
| 374 | + 0, /* tp_members */ |
| 375 | + 0, /* tp_getset */ |
| 376 | + 0, /* tp_base */ |
| 377 | + 0, /* tp_dict */ |
| 378 | + 0, /* tp_descr_get */ |
| 379 | + 0, /* tp_descr_set */ |
| 380 | + 0, /* tp_dictoffset */ |
| 381 | + 0, /* tp_init */ |
| 382 | + 0, /* tp_alloc */ |
| 383 | + 0, /* tp_new */ |
| 384 | +}; |
0 commit comments