Added new error codes. Improved error handling.
[libgit2.git] / src / commit.c
blob46c7d0de2393b0064ff2b909a34944f30a80d8c3
1 /*
2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
26 #include <time.h>
28 #include "common.h"
29 #include "commit.h"
30 #include "revwalk.h"
31 #include "git/odb.h"
33 #define COMMIT_PRINT(commit) {\
34 char oid[41]; oid[40] = 0;\
35 git_oid_fmt(oid, &commit->object.id);\
36 printf("Oid: %s | In degree: %d | Time: %u\n", oid, commit->in_degree, commit->commit_time);\
39 const git_oid *git_commit_id(git_commit *c)
41 return &c->object.id;
44 void git_commit__mark_uninteresting(git_commit *commit)
46 if (commit == NULL)
47 return;
49 git_commit_node *parents = commit->parents.head;
51 commit->uninteresting = 1;
53 while (parents) {
54 parents->commit->uninteresting = 1;
55 parents = parents->next;
59 git_commit *git_commit_parse(git_revpool *pool, const git_oid *id)
61 git_commit *commit = NULL;
63 if ((commit = git_commit_lookup(pool, id)) == NULL)
64 return NULL;
66 if (git_commit_parse_existing(commit) < 0)
67 goto error_cleanup;
69 return commit;
71 error_cleanup:
72 free(commit);
73 return NULL;
76 int git_commit_parse_existing(git_commit *commit)
78 git_obj commit_obj;
80 if (commit->parsed)
81 return 0;
83 if (git_odb_read(&commit_obj, commit->object.pool->db, &commit->object.id) < 0)
84 return GIT_ENOTFOUND;
86 if (commit_obj.type != GIT_OBJ_COMMIT)
87 goto error_cleanup;
89 if (git_commit__parse_buffer(commit, commit_obj.data, commit_obj.len) < 0)
90 goto error_cleanup;
92 git_obj_close(&commit_obj);
94 return 0;
96 error_cleanup:
97 git_obj_close(&commit_obj);
98 return -1;
101 git_commit *git_commit_lookup(git_revpool *pool, const git_oid *id)
103 git_commit *commit = NULL;
105 if (pool == NULL)
106 return NULL;
108 commit = (git_commit *)git_revpool_table_lookup(pool->commits, id);
109 if (commit != NULL)
110 return commit;
112 commit = git__malloc(sizeof(git_commit));
114 if (commit == NULL)
115 return NULL;
117 memset(commit, 0x0, sizeof(git_commit));
119 // Initialize parent object
120 git_oid_cpy(&commit->object.id, id);
121 commit->object.pool = pool;
123 git_revpool_table_insert(pool->commits, (git_revpool_object *)commit);
125 return commit;
128 int git_commit__parse_time(time_t *commit_time, char *buffer, const char *buffer_end)
130 if (memcmp(buffer, "author ", 7) != 0)
131 return GIT_EOBJCORRUPTED;
133 buffer = memchr(buffer, '\n', buffer_end - buffer);
134 if (buffer == 0 || ++buffer >= buffer_end)
135 return GIT_EOBJCORRUPTED;
137 if (memcmp(buffer, "committer ", 10) != 0)
138 return GIT_EOBJCORRUPTED;
140 buffer = memchr(buffer, '>', buffer_end - buffer);
141 if (buffer == 0 || ++buffer >= buffer_end)
142 return GIT_EOBJCORRUPTED;
144 *commit_time = strtol(buffer, &buffer, 10);
146 if (*commit_time == 0)
147 return GIT_EOBJCORRUPTED;
149 buffer = memchr(buffer, '\n', buffer_end - buffer);
150 if (buffer == 0 || ++buffer >= buffer_end)
151 return GIT_EOBJCORRUPTED;
153 return (buffer < buffer_end) ? 0 : -1;
156 int git_commit__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header)
158 const size_t sha_len = GIT_OID_HEXSZ;
159 const size_t header_len = strlen(header);
161 char *buffer = *buffer_out;
163 if (buffer + (header_len + sha_len + 1) > buffer_end)
164 return GIT_EOBJCORRUPTED;
166 if (memcmp(buffer, header, header_len) != 0)
167 return GIT_EOBJCORRUPTED;
169 if (buffer[header_len + sha_len] != '\n')
170 return GIT_EOBJCORRUPTED;
172 if (git_oid_mkstr(oid, buffer + header_len) < 0)
173 return GIT_EOBJCORRUPTED;
175 *buffer_out = buffer + (header_len + sha_len + 1);
177 return 0;
180 int git_commit__parse_buffer(git_commit *commit, void *data, size_t len)
182 char *buffer = (char *)data;
183 const char *buffer_end = (char *)data + len;
185 git_oid oid;
187 if (commit->parsed)
188 return 0;
190 if (git_commit__parse_oid(&oid, &buffer, buffer_end, "tree ") < 0)
191 return GIT_EOBJCORRUPTED;
194 * TODO: load tree into commit object
195 * TODO: commit grafts!
198 while (git_commit__parse_oid(&oid, &buffer, buffer_end, "parent ") == 0) {
199 git_commit *parent;
201 if ((parent = git_commit_lookup(commit->object.pool, &oid)) == NULL)
202 return GIT_ENOTFOUND;
204 // Inherit uninteresting flag
205 if (commit->uninteresting)
206 parent->uninteresting = 1;
208 git_commit_list_push_back(&commit->parents, parent);
211 if (git_commit__parse_time(&commit->commit_time, buffer, buffer_end) < 0)
212 return GIT_EOBJCORRUPTED;
214 commit->parsed = 1;
216 return 0;
219 int git_commit_list_push_back(git_commit_list *list, git_commit *commit)
221 git_commit_node *node = NULL;
223 node = git__malloc(sizeof(git_commit_list));
225 if (node == NULL)
226 return GIT_ENOMEM;
228 node->commit = commit;
229 node->next = NULL;
230 node->prev = list->tail;
232 if (list->tail == NULL) {
233 list->head = list->tail = node;
234 } else {
235 list->tail->next = node;
236 list->tail = node;
239 list->size++;
240 return 0;
243 int git_commit_list_push_front(git_commit_list *list, git_commit *commit)
245 git_commit_node *node = NULL;
247 node = git__malloc(sizeof(git_commit_list));
249 if (node == NULL)
250 return GIT_ENOMEM;
252 node->commit = commit;
253 node->next = list->head;
254 node->prev = NULL;
256 if (list->head == NULL) {
257 list->head = list->tail = node;
258 } else {
259 list->head->prev = node;
260 list->head = node;
263 list->size++;
264 return 0;
268 git_commit *git_commit_list_pop_back(git_commit_list *list)
270 git_commit_node *node;
271 git_commit *commit;
273 if (list->tail == NULL)
274 return NULL;
276 node = list->tail;
277 list->tail = list->tail->prev;
278 if (list->tail == NULL)
279 list->head = NULL;
281 commit = node->commit;
282 free(node);
284 list->size--;
286 return commit;
289 git_commit *git_commit_list_pop_front(git_commit_list *list)
291 git_commit_node *node;
292 git_commit *commit;
294 if (list->head == NULL)
295 return NULL;
297 node = list->head;
298 list->head = list->head->next;
299 if (list->head == NULL)
300 list->tail = NULL;
302 commit = node->commit;
303 free(node);
305 list->size--;
307 return commit;
310 void git_commit_list_clear(git_commit_list *list, int free_commits)
312 git_commit_node *node, *next_node;
314 node = list->head;
315 while (node) {
316 if (free_commits)
317 free(node->commit);
319 next_node = node->next;
320 free(node);
321 node = next_node;
324 list->head = list->tail = NULL;
325 list->size = 0;
328 void git_commit_list_timesort(git_commit_list *list)
330 git_commit_node *p, *q, *e;
331 int in_size, p_size, q_size, merge_count, i;
333 if (list->head == NULL)
334 return;
336 in_size = 1;
338 do {
339 p = list->head;
340 list->tail = NULL;
341 merge_count = 0;
343 while (p != NULL) {
344 merge_count++;
345 q = p;
346 p_size = 0;
347 q_size = in_size;
349 for (i = 0; i < in_size && q; ++i, q = q->next)
350 p_size++;
352 while (p_size > 0 || (q_size > 0 && q)) {
354 if (p_size == 0)
355 e = q, q = q->next, q_size--;
357 else if (q_size == 0 || q == NULL ||
358 p->commit->commit_time >= q->commit->commit_time)
359 e = p, p = p->next, p_size--;
361 else
362 e = q, q = q->next, q_size--;
364 if (list->tail != NULL)
365 list->tail->next = e;
366 else
367 list->head = e;
369 e->prev = list->tail;
370 list->tail = e;
373 p = q;
376 list->tail->next = NULL;
377 in_size *= 2;
379 } while (merge_count > 1);
382 void git_commit_list_toposort(git_commit_list *list)
384 git_commit *commit;
385 git_commit_list topo;
386 memset(&topo, 0x0, sizeof(git_commit_list));
388 while ((commit = git_commit_list_pop_back(list)) != NULL) {
389 git_commit_node *p;
391 if (commit->in_degree > 0) {
392 commit->topo_delay = 1;
393 continue;
396 for (p = commit->parents.head; p != NULL; p = p->next) {
397 p->commit->in_degree--;
399 if (p->commit->in_degree == 0 && p->commit->topo_delay) {
400 p->commit->topo_delay = 0;
401 git_commit_list_push_back(list, p->commit);
405 git_commit_list_push_back(&topo, commit);
408 list->head = topo.head;
409 list->tail = topo.tail;
410 list->size = topo.size;