Add 'git_revpool_object' and 'git_revpool_table' structures.
[libgit2.git] / src / commit.c
blob226eca421e206b16826b86f9aa9706aa31680240
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 const git_oid *git_commit_id(git_commit *c)
35 return &c->object.id;
38 void git_commit__mark_uninteresting(git_commit *commit)
40 if (commit == NULL)
41 return;
43 git_commit_node *parents = commit->parents.head;
45 commit->uninteresting = 1;
47 while (parents)
49 parents->commit->uninteresting = 1;
50 parents = parents->next;
54 git_commit *git_commit_parse(git_revpool *pool, const git_oid *id)
56 git_commit *commit = NULL;
58 if ((commit = git_commit_lookup(pool, id)) == NULL)
59 return NULL;
61 if (git_commit_parse_existing(commit) < 0)
62 goto error_cleanup;
64 return commit;
66 error_cleanup:
67 free(commit);
68 return NULL;
71 int git_commit_parse_existing(git_commit *commit)
73 git_obj commit_obj;
75 if (commit->parsed)
76 return 0;
78 if (git_odb_read(&commit_obj, commit->object.pool->db, &commit->object.id) < 0)
79 return -1;
81 if (commit_obj.type != GIT_OBJ_COMMIT)
82 goto error_cleanup;
84 if (git_commit__parse_buffer(commit, commit_obj.data, commit_obj.len) < 0)
85 goto error_cleanup;
87 git_obj_close(&commit_obj);
89 return 0;
91 error_cleanup:
92 git_obj_close(&commit_obj);
93 return -1;
96 git_commit *git_commit_lookup(git_revpool *pool, const git_oid *id)
98 git_commit *commit = NULL;
100 if (pool == NULL || pool->db == NULL)
101 return NULL;
103 commit = git__malloc(sizeof(git_commit));
105 if (commit == NULL)
106 return NULL;
108 memset(commit, 0x0, sizeof(git_commit));
110 // Initialize parent object
111 git_oid_cpy(&commit->object.id, id);
112 commit->object.pool = pool;
114 return commit;
117 int git_commit__parse_time(time_t *commit_time, char *buffer, const char *buffer_end)
119 if (memcmp(buffer, "author ", 7) != 0)
120 return -1;
122 buffer = memchr(buffer, '\n', buffer_end - buffer);
123 if (buffer == 0 || buffer >= buffer_end)
124 return -1;
126 if (memcmp(buffer, "committer ", 10) != 0)
127 return -1;
129 buffer = memchr(buffer, '\n', buffer_end - buffer);
130 if (buffer == 0 || buffer >= buffer_end)
131 return -1;
133 *commit_time = strtol(buffer, &buffer, 10);
135 return (buffer < buffer_end) ? 0 : -1;
138 int git_commit__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header)
140 const size_t sha_len = GIT_OID_HEXSZ;
141 const size_t header_len = strlen(header);
143 char *buffer = *buffer_out;
145 if (buffer + (header_len + sha_len + 1) > buffer_end)
146 return -1;
148 if (memcmp(buffer, header, header_len) != 0)
149 return -1;
151 if (buffer[header_len + sha_len] != '\n')
152 return -1;
154 if (git_oid_mkstr(oid, buffer + header_len) < 0)
155 return -1;
157 *buffer_out = buffer + (header_len + sha_len + 1);
159 return 0;
162 int git_commit__parse_buffer(git_commit *commit, void *data, size_t len)
164 char *buffer = (char *)data;
165 const char *buffer_end = (char *)data + len;
167 git_oid oid;
169 if (commit->parsed)
170 return 0;
172 if (git_commit__parse_oid(&oid, &buffer, buffer_end, "tree ") < 0)
173 return -1;
176 * TODO: load tree into commit object
177 * TODO: commit grafts!
180 while (git_commit__parse_oid(&oid, &buffer, buffer_end, "parent ") == 0) {
181 git_commit *parent;
183 if ((parent = git_commit_lookup(commit->object.pool, &oid)) == NULL)
184 return -1;
186 // Inherit uninteresting flag
187 if (commit->uninteresting)
188 parent->uninteresting = 1;
190 git_commit_list_append(&commit->parents, parent);
193 if (git_commit__parse_time(&commit->commit_time, buffer, buffer_end) < 0)
194 return -1;
196 commit->parsed = 1;
198 return 0;
201 void git_commit_list_append(git_commit_list *list, git_commit *commit)
203 git_commit_node *node = NULL;
205 node = git__malloc(sizeof(git_commit_list));
207 if (node == NULL)
208 return;
210 node->commit = commit;
211 node->next = NULL;
212 node->prev = list->tail;
214 if (list->tail == NULL)
216 list->head = list->tail = node;
218 else
220 list->tail->next = node;
221 list->tail = node;
224 list->size++;
227 git_commit *git_commit_list_pop_back(git_commit_list *list)
229 git_commit_node *node;
230 git_commit *commit;
232 if (list->tail == NULL)
233 return NULL;
235 node = list->tail;
236 list->tail = list->tail->prev;
237 if (list->tail == NULL)
238 list->head = NULL;
240 commit = node->commit;
241 free(node);
243 list->size--;
245 return commit;
248 git_commit *git_commit_list_pop_front(git_commit_list *list)
250 git_commit_node *node;
251 git_commit *commit;
253 if (list->head == NULL)
254 return NULL;
256 node = list->head;
257 list->head = list->head->next;
258 if (list->head == NULL)
259 list->tail = NULL;
261 commit = node->commit;
262 free(node);
264 list->size--;
266 return commit;
269 void git_commit_list_clear(git_commit_list *list, int free_commits)
271 git_commit_node *node, *next_node;
273 node = list->head;
274 while (node)
276 if (free_commits)
277 free(node->commit);
279 next_node = node->next;
280 free(node);
281 node = next_node;
284 list->head = list->tail = NULL;
285 list->size = 0;