libgit-thin: Introduces git_revlist_show_merges()
[git/libgit-gsoc.git] / libgit-thin / ltcommit.c
blob859c776420f4bd7af6db4f3e5d1d139d4ab97e6b
1 /**
2 * @file
3 *
4 * Commit handling functions.
6 * The functions implemented in this file retrieve all kinds of
7 * information from commits.
8 *
9 * Luiz Fernando N. Capitulino
10 * <lcapitulino@gmail.com>
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <time.h>
17 #include <cache.h>
18 #include <commit.h>
20 #include "ltcommit.h"
22 /**< Commit structure */
23 struct git_commit {
24 struct commit *commit;
25 char *author_name;
26 char *author_email;
27 int author_date;
28 int author_tz;
29 time_t author_time;
30 char *committer_name;
31 char *committer_email;
32 int committer_date;
33 int committer_tz;
34 time_t committer_time;
35 char *message;
36 int tree_id;
37 unsigned char tree_sha1[20];
40 static char *commit_info_alloc(const char *src, size_t len)
42 char *p;
44 p = malloc(len + 1);
45 if (!p)
46 return NULL;
48 memcpy(p, src, len);
49 p[len] = '\0';
51 return p;
54 static char *commit_strstr(const struct git_commit *commit,
55 const char *needle)
57 char *p, *endp;
59 if (!commit->commit)
60 return NULL;
62 p = strstr(commit->commit->buffer, needle);
63 if (!p)
64 return NULL;
66 endp = strstr(commit->commit->buffer, "\n\n");
67 if (!endp)
68 endp = commit->commit->buffer + strlen(commit->commit->buffer);
70 return (p < endp) ? p : NULL;
73 static char *email_line_strchr(char *p, int c)
75 while (*p && *p != c && *p != '\n')
76 p++;
78 return (*p != c) ? NULL : p;
81 static inline char *email_line_start(char *p)
83 return email_line_strchr(p, '<');
86 static inline char *email_line_end(char *p)
88 return email_line_strchr(p, '>');
91 static inline char *tz_start(char *p)
93 return email_line_strchr(p, ' ');
96 static inline int check_strtoul_ret(unsigned long ret)
98 if ((!ret || ret == ULONG_MAX) && errno != 0)
99 return -1;
100 return 0;
103 static inline int check_strtol_ret(long ret)
105 if ((ret == LONG_MIN || ret == LONG_MAX) && errno != 0)
106 return -1;
107 return 0;
111 * Get the commit's message.
113 * \param commit git_commit structure to get the message from
115 * \return A pointer to the commit's message on success,
116 * NULL otherwise.
118 const char *git_commit_message(struct git_commit *commit)
120 if (!commit)
121 goto out_err;
123 if (!commit->message) {
124 char *p;
126 if (!commit->commit)
127 goto out_err;
129 p = strstr(commit->commit->buffer, "\n\n");
130 if (!p) {
131 /* empty message (probably) */
132 errno = 0;
133 return NULL;
136 p += 2;
137 if (!p)
138 goto out_err;
140 commit->message = commit_info_alloc(p, strlen(p));
143 return commit->message;
145 out_err:
146 errno = EINVAL;
147 return NULL;
151 * Get the commit's committer email.
153 * \param commit git_commit structure to get the committer's email
154 * from
156 * \return A pointer to the commit's committer email on success,
157 * NULL otherwise.
159 const char *git_commit_committer_email(struct git_commit *commit)
161 if (!commit)
162 goto out_err;
164 if (!commit->committer_email) {
165 char *p, *endp;
167 p = commit_strstr(commit, "committer ");
168 if (!p)
169 goto out_err;
171 p = email_line_start(p);
172 if (!p)
173 goto out_err;
175 endp = email_line_end(p);
176 if (!endp)
177 goto out_err;
179 ++p;
180 commit->committer_email = commit_info_alloc(p, endp - p);
183 return commit->committer_email;
185 out_err:
186 errno = EINVAL;
187 return NULL;
191 * Get the commit's committer name.
193 * \param commit git_commit structure to get the committer's name
194 * from
196 * \return A pointer to the commit's committer name on success,
197 * NULL otherwise.
199 const char *git_commit_committer_name(struct git_commit *commit)
201 if (!commit)
202 goto out_err;
204 if (!commit->committer_name) {
205 char *p, *endp;
207 p = commit_strstr(commit, "committer ");
208 if (!p)
209 goto out_err;
211 endp = email_line_start(p);
212 if (!endp)
213 goto out_err;
215 --endp;
216 if (*endp != ' ')
217 goto out_err;
219 p += 10;
220 commit->committer_name = commit_info_alloc(p, endp - p);
223 return commit->committer_name;
225 out_err:
226 errno = EINVAL;
227 return NULL;
231 * Get the commit's (committer) date
233 * \param commit git_commit structure to get the (committer's) date from
234 * \param com_time return the date timestamp as seconds since the
235 * epoch, UTC (might be NULL)
236 * \param com_tz return the timezone information as the number of
237 * hours and minutes offset from UTC (might be NULL)
239 * \return 0 on succes, -1 otherwise
241 int git_commit_committer_date(struct git_commit *commit,
242 time_t *com_time, int *com_tz)
244 if (!commit)
245 goto out_err;
247 if (!com_time && !com_tz)
248 goto out_err;
250 if (!commit->committer_date) {
251 int err;
252 char *p;
254 p = commit_strstr(commit, "committer ");
255 if (!p)
256 goto out_err;
258 p = email_line_end(p);
259 if (!p)
260 goto out_err;
262 ++p;
263 if (*p != ' ')
264 goto out_err;
266 errno = 0;
267 commit->committer_time = (time_t) strtoul(++p, NULL, 10);
268 err = check_strtoul_ret(commit->committer_time);
269 if (err)
270 return -1;
272 p = tz_start(p);
273 if (!p)
274 goto out_err;
276 errno = 0;
277 commit->committer_tz = strtol(++p, NULL, 10);
278 err = check_strtol_ret(commit->committer_tz);
279 if (err)
280 return -1;
282 commit->committer_date = 1;
285 if (com_time)
286 *com_time = commit->committer_time;
288 if (com_tz)
289 *com_tz = commit->committer_tz;
291 return 0;
293 out_err:
294 errno = EINVAL;
295 return -1;
299 * Get the commit's author email.
301 * \param commit git_commit structure to get the author's email
302 * from
304 * \return A pointer to the commit's author's email on success,
305 * NULL otherwise.
307 const char *git_commit_author_email(struct git_commit *commit)
309 if (!commit)
310 goto out_err;
312 if (!commit->author_email) {
313 char *p, *endp;
315 p = commit_strstr(commit, "author ");
316 if (!p)
317 goto out_err;
319 p = email_line_start(p);
320 if (!p)
321 goto out_err;
323 endp = email_line_end(p);
324 if (!endp)
325 goto out_err;
327 ++p;
328 commit->author_email = commit_info_alloc(p, endp - p);
331 return commit->author_email;
333 out_err:
334 errno = EINVAL;
335 return NULL;
339 * Get the commit's (author) date
341 * \param commit git_commit structure to get the (author's) date from
342 * \param com_time return the date timestamp as seconds since the
343 * epoch, UTC (might be NULL)
344 * \param com_tz return the timezone information as the number of
345 * hours and minutes offset from UTC (might be NULL)
347 * \return 0 on succes, -1 otherwise
349 int git_commit_author_date(struct git_commit *commit,
350 time_t *com_time, int *com_tz)
352 if (!commit)
353 goto out_err;
355 if (!com_time && !com_tz)
356 goto out_err;
358 if (!commit->author_date) {
359 int err;
360 char *p;
362 p = commit_strstr(commit, "author ");
363 if (!p)
364 goto out_err;
366 p = email_line_end(p);
367 if (!p)
368 goto out_err;
370 ++p;
371 if (*p != ' ')
372 goto out_err;
374 errno = 0;
375 commit->author_time = (time_t) strtoul(++p, NULL, 10);
376 err = check_strtoul_ret(commit->author_time);
377 if (err)
378 return -1;
380 p = tz_start(p);
381 if (!p)
382 goto out_err;
384 errno = 0;
385 commit->author_tz = strtol(++p, NULL, 10);
386 err = check_strtol_ret(commit->author_tz);
387 if (err)
388 return -1;
390 commit->author_date = 1;
393 if (com_time)
394 *com_time = commit->author_time;
396 if (com_tz)
397 *com_tz = commit->author_tz;
399 return 0;
401 out_err:
402 errno = EINVAL;
403 return -1;
407 * Get the commit's author name.
409 * \param commit git_commit structure to get the author's name
410 * from
412 * \return A pointer to the commit's author's name on success,
413 * NULL otherwise.
415 const char *git_commit_author_name(struct git_commit *commit)
417 if (!commit)
418 goto out_err;
420 if (!commit->author_name) {
421 char *p, *endp;
423 p = commit_strstr(commit, "author ");
424 if (!p)
425 goto out_err;
427 endp = email_line_start(p);
428 if (!endp)
429 goto out_err;
431 --endp;
432 if (*endp != ' ')
433 goto out_err;
435 p += 7;
436 commit->author_name = commit_info_alloc(p, endp - p);
439 return commit->author_name;
441 out_err:
442 errno = EINVAL;
443 return NULL;
447 * Get the commit's SHA1.
449 * \param commit git_commit structure to get the commit's SHA1 from
450 * \param sha1 pointer to an 20-byte array to return the SHA1 into
452 * \return 0 on success, -1 otherwise
454 int git_commit_id(struct git_commit *commit, unsigned char *sha1)
456 if (!commit || !commit->commit || !sha1) {
457 errno = EINVAL;
458 return -1;
461 memcpy(sha1, commit->commit->object.sha1, 20);
462 return 0;
466 * Get the commit's tree SHA1.
468 * \param commit git_commit structure to get the tree SHA1 from
469 * \param sha1 pointer to an 20-byte array to return the SHA1 into
471 * \return 0 on success, -1 otherwise
473 int git_commit_tree(struct git_commit *commit, unsigned char *sha1)
475 if (!commit || !sha1)
476 goto out_err;
478 if (!commit->tree_id) {
479 char *p;
480 int err;
482 p = commit_strstr(commit, "tree ");
483 if (!p)
484 goto out_err;
486 p += 5;
487 if (!p)
488 goto out_err;
490 err = get_sha1_hex(p, commit->tree_sha1);
491 if (err)
492 return -1;
494 commit->tree_id = 1;
497 memcpy(sha1, commit->tree_sha1, 20);
498 return 0;
500 out_err:
501 errno = EINVAL;
502 return -1;
506 * Get the commit's buffer raw data.
508 * \param commit git_commit structure to get the raw buffer
509 * from
511 * \return A pointer to the commit's raw buffer (if any).
513 const char *git_commit_raw(struct git_commit *commit)
515 if (!commit || !commit->commit) {
516 errno = EINVAL;
517 return NULL;
520 return commit->commit->buffer;
524 * Free all the memory allocated by the git_commit structure's
525 * members.
527 * This is a low-level function, only use it if you know
528 * what you're doing.
530 * \param commit git_commit structure to have the contents
531 * freed
533 void __git_commit_free(struct git_commit *commit)
535 if (!commit->commit)
536 return;
538 free(commit->commit->buffer);
539 free_commit_list(commit->commit->parents);
541 free(commit->author_name);
542 free(commit->author_email);
543 free(commit->committer_name);
544 free(commit->committer_email);
545 free(commit->message);
549 * Free all the memory allocated by a git_commit structure.
551 * \param commit git_commit structure to be freed.
553 void git_commit_free(struct git_commit *commit)
555 if (commit) {
556 __git_commit_free(commit);
557 free(commit);
562 * Initialize a git_commit structure.
564 * This is a low-level function, only use it if you know
565 * what you're doing.
567 * \param commit git_commit structure to be initialized
569 void __git_commit_init(struct git_commit *commit)
571 commit->commit = NULL;
572 commit->author_name = NULL;
573 commit->author_email = NULL;
574 commit->author_date = 0;
575 commit->committer_name = NULL;
576 commit->committer_email = NULL;
577 commit->committer_date = 0;
578 commit->message = NULL;
579 commit->tree_id = 0;
583 * Allocate and initialize a git_commit structure.
585 * Should be called before using git_revlist_next().
587 * \return A pointer to an allocated git_commit structure
588 * on success, NULL otherwise.
590 struct git_commit *git_commit_init(void)
592 struct git_commit *commit;
594 commit = malloc(sizeof(*commit));
595 if (!commit)
596 return NULL;
598 __git_commit_init(commit);
599 return commit;
603 * Get the raw GIT commit object from a git_commit
604 * structure.
606 * This is a low-level function, only use it if you know
607 * what you're doing.
609 * \param commit git_commit structure to get the commit object
610 * from
612 * \return A pointer to the raw GIT commit object if any,
613 * NULL otherwise.
615 struct commit *__git_commit_obj(struct git_commit *commit)
617 return commit->commit;
621 * Set the git_commit structure's (raw) GIT commit object
622 * contents.
624 * This is a low-level function, only use it if you know
625 * what you're doing.
627 * \param commit git_commit structure to set the raw GIT
628 * commit object
629 * \param new The raw GIT commit object to set into the git_commit
630 * structure
632 void __git_commit_obj_set(struct git_commit *commit, struct commit *new)
634 commit->commit = new;
638 * Return a filled git_commit structure from a given commit's SHA1
640 * \param sha1 commit's SHA1 to be read
642 * \returns git_commit structure on success, NULL otherwise.
644 struct git_commit *git_commit_lookup(const unsigned char *sha1)
646 struct git_commit *commit;
648 if (!sha1) {
649 errno = EINVAL;
650 return NULL;
653 commit = git_commit_init();
654 if (!commit)
655 return NULL;
657 commit->commit = lookup_commit_reference_gently(sha1, 1);
658 if (!commit->commit) {
659 git_commit_free(commit);
660 return NULL;
663 return commit;