Changed commit time sorting to be descending (from newest to oldest).
[libgit2.git] / src / commit.c
blob2e3e1ba4b6f21a028ff2d8f17febfe22fe93afe3
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)
101 return NULL;
103 commit = (git_commit *)git_revpool_table_lookup(pool->commits, id);
104 if (commit != NULL)
105 return commit;
107 commit = git__malloc(sizeof(git_commit));
109 if (commit == NULL)
110 return NULL;
112 memset(commit, 0x0, sizeof(git_commit));
114 // Initialize parent object
115 git_oid_cpy(&commit->object.id, id);
116 commit->object.pool = pool;
118 git_revpool_table_insert(pool->commits, (git_revpool_object *)commit);
120 return commit;
123 int git_commit__parse_time(time_t *commit_time, char *buffer, const char *buffer_end)
125 if (memcmp(buffer, "author ", 7) != 0)
126 return -1;
128 buffer = memchr(buffer, '\n', buffer_end - buffer);
129 if (buffer == 0 || ++buffer >= buffer_end)
130 return -1;
132 if (memcmp(buffer, "committer ", 10) != 0)
133 return -1;
135 buffer = memchr(buffer, '\n', buffer_end - buffer);
136 if (buffer == 0 || ++buffer >= buffer_end)
137 return -1;
139 *commit_time = strtol(buffer, &buffer, 10);
141 return (buffer < buffer_end) ? 0 : -1;
144 int git_commit__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header)
146 const size_t sha_len = GIT_OID_HEXSZ;
147 const size_t header_len = strlen(header);
149 char *buffer = *buffer_out;
151 if (buffer + (header_len + sha_len + 1) > buffer_end)
152 return -1;
154 if (memcmp(buffer, header, header_len) != 0)
155 return -1;
157 if (buffer[header_len + sha_len] != '\n')
158 return -1;
160 if (git_oid_mkstr(oid, buffer + header_len) < 0)
161 return -1;
163 *buffer_out = buffer + (header_len + sha_len + 1);
165 return 0;
168 int git_commit__parse_buffer(git_commit *commit, void *data, size_t len)
170 char *buffer = (char *)data;
171 const char *buffer_end = (char *)data + len;
173 git_oid oid;
175 if (commit->parsed)
176 return 0;
178 if (git_commit__parse_oid(&oid, &buffer, buffer_end, "tree ") < 0)
179 return -1;
182 * TODO: load tree into commit object
183 * TODO: commit grafts!
186 while (git_commit__parse_oid(&oid, &buffer, buffer_end, "parent ") == 0) {
187 git_commit *parent;
189 if ((parent = git_commit_lookup(commit->object.pool, &oid)) == NULL)
190 return -1;
192 // Inherit uninteresting flag
193 if (commit->uninteresting)
194 parent->uninteresting = 1;
196 git_commit_list_push_back(&commit->parents, parent);
199 if (git_commit__parse_time(&commit->commit_time, buffer, buffer_end) < 0)
200 return -1;
202 commit->parsed = 1;
204 return 0;
207 void git_commit_list_push_back(git_commit_list *list, git_commit *commit)
209 git_commit_node *node = NULL;
211 node = git__malloc(sizeof(git_commit_list));
213 if (node == NULL)
214 return;
216 node->commit = commit;
217 node->next = NULL;
218 node->prev = list->tail;
220 if (list->tail == NULL)
222 list->head = list->tail = node;
224 else
226 list->tail->next = node;
227 list->tail = node;
230 list->size++;
233 void git_commit_list_push_front(git_commit_list *list, git_commit *commit)
235 git_commit_node *node = NULL;
237 node = git__malloc(sizeof(git_commit_list));
239 if (node == NULL)
240 return;
242 node->commit = commit;
243 node->next = list->head;
244 node->prev = NULL;
246 if (list->head == NULL)
248 list->head = list->tail = node;
250 else
252 list->head->next = node;
253 list->head = node;
256 list->size++;
260 git_commit *git_commit_list_pop_back(git_commit_list *list)
262 git_commit_node *node;
263 git_commit *commit;
265 if (list->tail == NULL)
266 return NULL;
268 node = list->tail;
269 list->tail = list->tail->prev;
270 if (list->tail == NULL)
271 list->head = NULL;
273 commit = node->commit;
274 free(node);
276 list->size--;
278 return commit;
281 git_commit *git_commit_list_pop_front(git_commit_list *list)
283 git_commit_node *node;
284 git_commit *commit;
286 if (list->head == NULL)
287 return NULL;
289 node = list->head;
290 list->head = list->head->next;
291 if (list->head == NULL)
292 list->tail = NULL;
294 commit = node->commit;
295 free(node);
297 list->size--;
299 return commit;
302 void git_commit_list_clear(git_commit_list *list, int free_commits)
304 git_commit_node *node, *next_node;
306 node = list->head;
307 while (node)
309 if (free_commits)
310 free(node->commit);
312 next_node = node->next;
313 free(node);
314 node = next_node;
317 list->head = list->tail = NULL;
318 list->size = 0;
321 void git_commit_list_timesort(git_commit_list *list)
323 git_commit_node *p, *q, *e;
324 int in_size, p_size, q_size, merge_count, i;
326 if (list->head == NULL)
327 return;
329 in_size = 1;
333 p = list->head;
334 list->tail = NULL;
335 merge_count = 0;
337 while (p != NULL)
339 merge_count++;
340 q = p;
341 p_size = 0;
342 q_size = in_size;
344 for (i = 0; i < in_size && q; ++i, q = q->next)
345 p_size++;
347 while (p_size > 0 || (q_size > 0 && q))
349 if (p_size == 0)
350 e = q, q = q->next, q_size--;
352 else if (q_size == 0 || q == NULL ||
353 p->commit->commit_time >= q->commit->commit_time)
354 e = p, p = p->next, p_size--;
356 else
357 e = q, q = q->next, q_size--;
359 if (list->tail != NULL)
360 list->tail->next = e;
361 else
362 list->head = e;
364 list->tail = e;
367 p = q;
370 list->tail->next = NULL;
371 in_size *= 2;
373 } while (merge_count > 1);
376 void git_commit_list_toposort(git_commit_list *list)
378 git_commit *commit;
379 git_commit_list topo;
380 memset(&topo, 0x0, sizeof(git_commit_list));
382 while ((commit = git_commit_list_pop_front(list)) != NULL)
384 git_commit_node *p;
386 if (commit->in_degree > 0)
388 commit->topo_delay = 1;
389 continue;
392 for (p = commit->parents.head; p != NULL; p = p->next)
394 p->commit->in_degree--;
396 if (p->commit->in_degree == 0 && p->commit->topo_delay)
398 p->commit->topo_delay = 0;
399 git_commit_list_push_front(list, p->commit);
403 git_commit_list_push_back(&topo, commit);
406 list->head = topo.head;
407 list->tail = topo.tail;
408 list->size = topo.size;