libgit-thin: pygit: Document commit methods
[git/libgit-gsoc.git] / libgit-thin / pygit / pygit.c
blob7fe5bcc5b949daa81707b3f25874f4e5f7c3af76
1 #include <Python.h>
2 #include "structmember.h"
4 #include <libgit-thin.h>
6 static PyObject *PyGitError;
8 PyMODINIT_FUNC initpygit(void);
11 * Useful helpers for all the types
14 #define UNUSED(x) (x = x)
16 static PyObject *
17 pygit_error(void)
19 return PyErr_Format(PyGitError, "[Errno: %d] %s", errno,
20 strerror(errno));
23 static int
24 pyarg_to_sha1(PyObject *args, unsigned char *sha1)
26 const char *hex;
28 if (!PyArg_ParseTuple(args, "s", &hex))
29 return -1;
31 if (git_hex_to_sha1(hex, sha1)) {
32 pygit_error();
33 return -1;
36 return 0;
39 static PyObject *
40 sha1_to_pystr(unsigned char *sha1)
42 char hex[GIT_HEX_LENGTH + 1];
44 if (git_sha1_to_hex(sha1, hex))
45 return pygit_error();
47 return PyString_FromString(hex);
51 * Commit Type
54 typedef struct {
55 PyObject_HEAD
56 struct git_commit *commit;
57 } GitCommitObject;
59 static PyObject *
60 pygit_call_commit_op(struct git_commit *commit,
61 const char *(*commit_op)(struct git_commit *))
63 const char *ret;
65 ret = commit_op(commit);
66 if (!ret)
67 return pygit_error();
69 return PyString_FromString(ret);
72 PyDoc_STRVAR(cmessage_doc,
73 "commit.message() -> string\n\
74 \n\
75 Return the commit's message.");
77 static PyObject *
78 commit_message(GitCommitObject *self, PyObject *args)
80 UNUSED(args);
81 return pygit_call_commit_op(self->commit, git_commit_message);
84 PyDoc_STRVAR(ccommitter_email_doc,
85 "commit.committer_email() -> string\n\
86 \n\
87 Return the commit's committer e-mail.");
89 static PyObject *
90 commit_committer_email(GitCommitObject *self, PyObject *args)
92 UNUSED(args);
93 return pygit_call_commit_op(self->commit, git_commit_committer_email);
96 PyDoc_STRVAR(ccommitter_name_doc,
97 "commit.committer_name() -> string\n\
98 \n\
99 Return the commit's committer name.");
101 static PyObject *
102 commit_committer_name(GitCommitObject *self, PyObject *args)
104 UNUSED(args);
105 return pygit_call_commit_op(self->commit, git_commit_committer_name);
108 PyDoc_STRVAR(cauthor_email_doc,
109 "commit.author_email() -> string\n\
111 Return the commit's author e-mail.");
113 static PyObject *
114 commit_author_email(GitCommitObject *self, PyObject *args)
116 UNUSED(args);
117 return pygit_call_commit_op(self->commit, git_commit_author_email);
120 PyDoc_STRVAR(cauthor_name_doc,
121 "commit.author_name() -> string\n\
123 Return the commit's author name.");
125 static PyObject *
126 commit_author_name(GitCommitObject *self, PyObject *args)
128 UNUSED(args);
129 return pygit_call_commit_op(self->commit, git_commit_author_name);
132 PyDoc_STRVAR(commit_raw_doc,
133 "commit.buffer() -> string\n\
135 Return the commit's raw buffer.");
137 static PyObject *
138 commit_raw(GitCommitObject *self, PyObject *args)
140 UNUSED(args);
141 return pygit_call_commit_op(self->commit, git_commit_raw);
144 static PyObject *
145 pygit_call_commit_op_sha1(struct git_commit *commit,
146 int (*commit_op)(struct git_commit *,
147 unsigned char *sha1))
149 int err;
150 unsigned char sha1[GIT_SHA1_SIZE];
152 err = commit_op(commit, sha1);
153 if (err)
154 return pygit_error();
156 return sha1_to_pystr(sha1);
159 PyDoc_STRVAR(commit_id_doc,
160 "commit.id() -> string\n\
162 Return the commit's SHA1 hex representation.");
164 static PyObject *
165 commit_id(GitCommitObject *self, PyObject *args)
167 UNUSED(args);
168 return pygit_call_commit_op_sha1(self->commit, git_commit_id);
171 PyDoc_STRVAR(commit_tree_doc,
172 "commit.tree() -> string\n\
174 Return the commit's tree SHA1 hex representation.");
176 static PyObject *
177 commit_tree(GitCommitObject *self, PyObject *args)
179 UNUSED(args);
180 return pygit_call_commit_op_sha1(self->commit, git_commit_tree);
183 static PyMethodDef commit_methods[] = {
184 {"message", (PyCFunction) commit_message, METH_VARARGS, cmessage_doc},
185 {"id", (PyCFunction) commit_id, METH_VARARGS, commit_id_doc},
186 {"tree", (PyCFunction) commit_tree, METH_VARARGS, commit_tree_doc},
187 {"buffer", (PyCFunction) commit_raw, METH_VARARGS, commit_raw_doc},
188 {"committer_email", (PyCFunction) commit_committer_email,
189 METH_VARARGS, ccommitter_email_doc},
190 {"committer_name", (PyCFunction) commit_committer_name, METH_VARARGS,
191 ccommitter_name_doc},
192 {"author_email", (PyCFunction) commit_author_email, METH_VARARGS,
193 cauthor_email_doc},
194 {"author_name", (PyCFunction) commit_author_name, METH_VARARGS,
195 cauthor_name_doc},
196 {NULL, NULL, 0, NULL}
199 static void
200 commit_dealloc(GitCommitObject *self)
202 git_commit_free(self->commit);
203 self->ob_type->tp_free((PyObject*) self);
206 static int
207 commit_compare(GitCommitObject *a, GitCommitObject *b)
209 int ret, a_tz, b_tz;
210 time_t a_time, b_time;
212 if (git_commit_equal(a->commit, b->commit))
213 return 0;
215 ret = git_commit_committer_date(a->commit, &a_time, &a_tz);
216 if (ret) {
217 pygit_error();
218 return -1;
221 ret = git_commit_committer_date(b->commit, &b_time, &b_tz);
222 if (ret) {
223 pygit_error();
224 return -1;
228 * FIXME: We should convert to UTC and then compare, but
229 * this is incomplete for now...
231 if (a_tz != b_tz) {
232 PyErr_SetString(PyGitError, "time zone must be equal");
233 return -1;
236 ret = a_time < b_time ? -1 : 1;
237 return ret;
240 static PyTypeObject Git_Commit_Type = {
241 PyObject_HEAD_INIT(NULL)
242 0, /* ob_size */
243 "pygit.comit", /* tp_name */
244 sizeof(GitCommitObject), /* tp_basicsize */
245 0, /* tp_itemsize */
246 (destructor)commit_dealloc, /* tp_dealloc */
247 0, /* tp_print */
248 0, /* tp_getattr */
249 0, /* tp_setattr */
250 (cmpfunc)commit_compare, /* tp_compare */
251 0, /* tp_repr */
252 0, /* tp_as_number */
253 0, /* tp_as_sequence */
254 0, /* tp_as_mapping */
255 0, /* tp_hash */
256 0, /* tp_call */
257 0, /* tp_str */
258 0, /* tp_getattro */
259 0, /* tp_setattro */
260 0, /* tp_as_buffer */
261 Py_TPFLAGS_DEFAULT, /* tp_flags */
262 0, /* tp_doc */
263 0, /* tp_traverse */
264 0, /* tp_clear */
265 0, /* tp_richcompare */
266 0, /* tp_weaklistoffset */
267 0, /* tp_iter */
268 0, /* tp_iternext */
269 commit_methods, /* tp_methods */
270 0, /* tp_members */
271 0, /* tp_getset */
272 0, /* tp_base */
273 0, /* tp_dict */
274 0, /* tp_descr_get */
275 0, /* tp_descr_set */
276 0, /* tp_dictoffset */
277 0, /* tp_init */
278 0, /* tp_alloc */
279 0, /* tp_new */
283 * Revision list type
286 typedef struct {
287 PyObject_HEAD
288 struct git_revlist_opt *opt;
289 } GitRevListObject;
291 static PyObject *
292 revlist_add_commit(GitRevListObject *self, PyObject *args, int exclude)
294 int err;
295 unsigned char sha1[GIT_SHA1_SIZE];
297 err = pyarg_to_sha1(args, sha1);
298 if (err)
299 return pygit_error();
301 if (exclude)
302 err = git_revlist_exclude(self->opt, sha1);
303 else
304 err = git_revlist_include(self->opt, sha1);
306 if (err)
307 return pygit_error();
309 Py_RETURN_NONE;
312 static PyObject *
313 revlist_include(GitRevListObject *self, PyObject *args)
315 return revlist_add_commit(self, args, 0);
318 static PyObject *
319 revlist_exclude(GitRevListObject *self, PyObject *args)
321 return revlist_add_commit(self, args, 1);
324 static PyObject *
325 revlist_reverse(GitRevListObject *self, PyObject *args)
327 int err;
329 UNUSED(args);
331 err = git_revlist_reverse(self->opt);
332 if (err)
333 return pygit_error();
335 Py_RETURN_NONE;
338 static PyObject *
339 revlist_show_merges(GitRevListObject *self, PyObject *args)
341 int err;
343 UNUSED(args);
345 err = git_revlist_show_merges(self->opt);
346 if (err)
347 return pygit_error();
349 Py_RETURN_NONE;
352 static PyObject *
353 revlist_max_count(GitRevListObject *self, PyObject *args)
355 int err;
356 unsigned long count;
358 if (!PyArg_ParseTuple(args, "k", &count))
359 return NULL;
361 err = git_revlist_max_count(self->opt, (size_t) count);
362 if (err)
363 return pygit_error();
365 Py_RETURN_NONE;
368 static PyMethodDef revlist_methods[] = {
369 {"include", (PyCFunction) revlist_include, METH_VARARGS, NULL},
370 {"exclude", (PyCFunction) revlist_exclude, METH_VARARGS, NULL},
371 {"reverse", (PyCFunction) revlist_reverse, METH_NOARGS, NULL},
372 {"show_merges", (PyCFunction) revlist_show_merges, METH_NOARGS, NULL},
373 {"max_count", (PyCFunction) revlist_max_count,METH_VARARGS,NULL},
374 {NULL, NULL, 0, NULL}
377 static void
378 revlist_dealloc(GitRevListObject *self)
380 git_revlist_free(self->opt);
381 self->ob_type->tp_free((PyObject*) self);
384 static PyObject *
385 revlist_iter(GitRevListObject *self)
387 Py_INCREF(self);
388 return (PyObject *) self;
391 static PyObject *
392 revlist_iternext(GitRevListObject *self)
394 int ret;
395 GitCommitObject *m;
396 struct git_commit *commit;
398 m = PyObject_New(GitCommitObject, &Git_Commit_Type);
399 if (!m)
400 return NULL;
402 ret = PyType_Ready(&Git_Commit_Type);
403 if (ret) {
404 PyObject_DEL(m);
405 return NULL;
408 commit = git_commit_init();
409 if (!commit) {
410 PyObject_DEL(m);
411 return pygit_error();
414 ret = git_revlist_next(self->opt, commit);
415 if (ret != 1) {
416 git_commit_free(commit);
417 PyObject_DEL(m);
418 return NULL;
421 m->commit = commit;
422 return (PyObject *) m;
425 static PyTypeObject Git_RevList_Type = {
426 PyObject_HEAD_INIT(NULL)
427 0, /* ob_size */
428 "pygit.revlist", /* tp_name */
429 sizeof(GitRevListObject), /* tp_basicsize */
430 0, /* tp_itemsize */
431 (destructor)revlist_dealloc, /* tp_dealloc */
432 0, /* tp_print */
433 0, /* tp_getattr */
434 0, /* tp_setattr */
435 0, /* tp_compare */
436 0, /* tp_repr */
437 0, /* tp_as_number */
438 0, /* tp_as_sequence */
439 0, /* tp_as_mapping */
440 0, /* tp_hash */
441 0, /* tp_call */
442 0, /* tp_str */
443 0, /* tp_getattro */
444 0, /* tp_setattro */
445 0, /* tp_as_buffer */
446 Py_TPFLAGS_DEFAULT, /* tp_flags */
447 0, /* tp_doc */
448 0, /* tp_traverse */
449 0, /* tp_clear */
450 0, /* tp_richcompare */
451 0, /* tp_weaklistoffset */
452 (getiterfunc)revlist_iter, /* tp_iter */
453 (iternextfunc)revlist_iternext, /* tp_iternext */
454 revlist_methods, /* tp_methods */
455 0, /* tp_members */
456 0, /* tp_getset */
457 0, /* tp_base */
458 0, /* tp_dict */
459 0, /* tp_descr_get */
460 0, /* tp_descr_set */
461 0, /* tp_dictoffset */
462 0, /* tp_init */
463 0, /* tp_alloc */
464 0, /* tp_new */
468 * Repository type
471 typedef struct {
472 PyObject_HEAD
473 PyObject *path;
474 } GitRepoObject;
476 static PyObject *
477 repo_read_obj(PyObject *args,
478 int (*read_obj)(unsigned char *sha1, void **buf, size_t *len))
480 int err;
481 void *buf;
482 PyObject *ret;
483 unsigned char sha1[GIT_SHA1_SIZE];
485 err = pyarg_to_sha1(args, sha1);
486 if (err)
487 return NULL;
489 err = read_obj(sha1, &buf, NULL);
490 if (err)
491 return pygit_error();
493 ret = PyString_FromString((char *) buf);
494 free(buf);
496 return ret;
499 static PyObject *
500 repo_head_commit(GitRepoObject *self, PyObject *args)
502 int err;
503 unsigned char sha1[GIT_SHA1_SIZE];
505 UNUSED(self);
506 UNUSED(args);
508 err = git_repo_head(sha1);
509 if (err)
510 return pygit_error();
512 return sha1_to_pystr(sha1);
515 static PyObject *
516 repo_read_commit(GitRepoObject *self, PyObject *args)
518 UNUSED(self);
519 return repo_read_obj(args, git_repo_commit_read);
522 static PyObject *
523 repo_read_blob(GitRepoObject *self, PyObject *args)
525 UNUSED(self);
526 return repo_read_obj(args, git_repo_blob_read);
529 static PyObject *
530 repo_lookup_commit(GitRepoObject *self, PyObject *args)
532 int err;
533 GitCommitObject *m;
534 struct git_commit *commit;
535 unsigned char sha1[GIT_SHA1_SIZE];
537 UNUSED(self);
539 err = pyarg_to_sha1(args, sha1);
540 if (err)
541 return NULL;
543 m = PyObject_New(GitCommitObject, &Git_Commit_Type);
544 if (!m)
545 return NULL;
547 err = PyType_Ready(&Git_Commit_Type);
548 if (err) {
549 PyObject_DEL(m);
550 return NULL;
553 commit = git_commit_lookup(sha1);
554 if (!commit) {
555 PyObject_DEL(m);
556 return pygit_error();
558 m->commit = commit;
560 return (PyObject *) m;
563 static PyObject *
564 repo_translate_ref(GitRepoObject *self, PyObject *args)
566 int err;
567 const char *ref;
568 unsigned char sha1[GIT_SHA1_SIZE];
570 UNUSED(self);
572 if (!PyArg_ParseTuple(args, "s", &ref))
573 return NULL;
575 err = git_repo_translate_ref(ref, sha1);
576 if (err)
577 return pygit_error();
579 return sha1_to_pystr(sha1);
582 static PyObject *
583 repo_revlist(GitRepoObject *self, PyObject *args)
585 int err;
586 GitRevListObject *m;
588 UNUSED(self);
589 UNUSED(args);
591 m = PyObject_New(GitRevListObject, &Git_RevList_Type);
592 if (!m)
593 return NULL;
595 err = PyType_Ready(&Git_RevList_Type);
596 if (err) {
597 PyObject_DEL(m);
598 return NULL;
601 m->opt = git_revlist_init();
602 if (!m->opt) {
603 PyObject_DEL(m);
604 return pygit_error();
607 return (PyObject *) m;
610 static PyMethodDef repo_methods[] = {
611 {"read_commit", (PyCFunction) repo_read_commit, METH_VARARGS, NULL},
612 {"read_blob", (PyCFunction) repo_read_blob, METH_VARARGS, NULL},
613 {"lookup_commit", (PyCFunction) repo_lookup_commit, METH_VARARGS,NULL},
614 {"head_commit", (PyCFunction) repo_head_commit, METH_NOARGS, NULL},
615 {"translate_ref", (PyCFunction) repo_translate_ref,METH_VARARGS, NULL},
616 {"revlist", (PyCFunction) repo_revlist, METH_VARARGS, NULL},
617 {NULL, NULL, 0, NULL}
620 static PyMemberDef repo_members[] = {
621 {"path", T_OBJECT_EX, offsetof(GitRepoObject, path), 0,
622 "repository's path"},
623 {NULL, 0, 0, 0, NULL}
626 static void
627 repo_dealloc(GitRepoObject *self)
629 Py_DECREF(self->path);
630 self->ob_type->tp_free((PyObject*) self);
633 static PyTypeObject Git_Repo_Type = {
634 PyObject_HEAD_INIT(NULL)
635 0, /* ob_size */
636 "pygit.repo", /* tp_name */
637 sizeof(GitRepoObject), /* tp_basicsize */
638 0, /* tp_itemsize */
639 (destructor)repo_dealloc, /* tp_dealloc */
640 0, /* tp_print */
641 0, /* tp_getattr */
642 0, /* tp_setattr */
643 0, /* tp_compare */
644 0, /* tp_repr */
645 0, /* tp_as_number */
646 0, /* tp_as_sequence */
647 0, /* tp_as_mapping */
648 0, /* tp_hash */
649 0, /* tp_call */
650 0, /* tp_str */
651 0, /* tp_getattro */
652 0, /* tp_setattro */
653 0, /* tp_as_buffer */
654 Py_TPFLAGS_DEFAULT, /* tp_flags */
655 0, /* tp_doc */
656 0, /* tp_traverse */
657 0, /* tp_clear */
658 0, /* tp_richcompare */
659 0, /* tp_weaklistoffset */
660 0, /* tp_iter */
661 0, /* tp_iternext */
662 repo_methods, /* tp_methods */
663 repo_members, /* tp_members */
664 0, /* tp_getset */
665 0, /* tp_base */
666 0, /* tp_dict */
667 0, /* tp_descr_get */
668 0, /* tp_descr_set */
669 0, /* tp_dictoffset */
670 0, /* tp_init */
671 0, /* tp_alloc */
672 0, /* tp_new */
676 * Module's stuff
679 static PyObject *
680 pygit_open(PyObject *self, PyObject *args)
682 int err;
683 const char *dir;
684 GitRepoObject *m;
686 UNUSED(self);
688 if (!PyArg_ParseTuple(args, "s", &dir))
689 return NULL;
691 err = git_repo_open(dir);
692 if (err)
693 return pygit_error();
695 m = PyObject_New(GitRepoObject, &Git_Repo_Type);
696 if (!m)
697 return NULL;
699 err = PyType_Ready(&Git_Repo_Type);
700 if (err) {
701 PyObject_DEL(m);
702 return NULL;
705 m->path = PyString_FromString(dir);
706 if (!m->path) {
707 PyObject_DEL(m);
708 return NULL;
711 return (PyObject *) m;
714 static PyMethodDef pygit_methods[] = {
715 { "open", pygit_open, METH_VARARGS, "Open a GIT repository" },
716 { NULL, NULL, 0, NULL}
719 PyMODINIT_FUNC
720 initpygit(void)
722 PyObject *m;
724 m = Py_InitModule("pygit", pygit_methods);
725 if (!m)
726 return;
728 PyGitError = PyErr_NewException("pygit.error", NULL, NULL);
729 if (!PyGitError)
730 return ;
732 Py_INCREF(PyGitError);
733 PyModule_AddObject(m, "error", PyGitError);