2 * Copyright (C) the libgit2 contributors. All rights reserved.
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
8 #include "commit_list.h"
15 int git_commit_list_generation_cmp(const void *a
, const void *b
)
17 uint32_t generation_a
= ((git_commit_list_node
*) a
)->generation
;
18 uint32_t generation_b
= ((git_commit_list_node
*) b
)->generation
;
20 if (!generation_a
|| !generation_b
) {
21 /* Fall back to comparing by timestamps if at least one commit lacks a generation. */
22 return git_commit_list_time_cmp(a
, b
);
25 if (generation_a
< generation_b
)
27 if (generation_a
> generation_b
)
33 int git_commit_list_time_cmp(const void *a
, const void *b
)
35 int64_t time_a
= ((git_commit_list_node
*) a
)->time
;
36 int64_t time_b
= ((git_commit_list_node
*) b
)->time
;
46 git_commit_list
*git_commit_list_insert(git_commit_list_node
*item
, git_commit_list
**list_p
)
48 git_commit_list
*new_list
= git__malloc(sizeof(git_commit_list
));
49 if (new_list
!= NULL
) {
50 new_list
->item
= item
;
51 new_list
->next
= *list_p
;
57 git_commit_list
*git_commit_list_insert_by_date(git_commit_list_node
*item
, git_commit_list
**list_p
)
59 git_commit_list
**pp
= list_p
;
62 while ((p
= *pp
) != NULL
) {
63 if (git_commit_list_time_cmp(p
->item
, item
) > 0)
69 return git_commit_list_insert(item
, pp
);
72 git_commit_list_node
*git_commit_list_alloc_node(git_revwalk
*walk
)
74 return (git_commit_list_node
*)git_pool_mallocz(&walk
->commit_pool
, 1);
77 static git_commit_list_node
**alloc_parents(
78 git_revwalk
*walk
, git_commit_list_node
*commit
, size_t n_parents
)
82 if (n_parents
<= PARENTS_PER_COMMIT
)
83 return (git_commit_list_node
**)((char *)commit
+ sizeof(git_commit_list_node
));
85 if (git__multiply_sizet_overflow(&bytes
, n_parents
, sizeof(git_commit_list_node
*)))
88 return (git_commit_list_node
**)git_pool_malloc(&walk
->commit_pool
, bytes
);
92 void git_commit_list_free(git_commit_list
**list_p
)
94 git_commit_list
*list
= *list_p
;
100 git_commit_list
*temp
= list
;
108 git_commit_list_node
*git_commit_list_pop(git_commit_list
**stack
)
110 git_commit_list
*top
= *stack
;
111 git_commit_list_node
*item
= top
? top
->item
: NULL
;
120 static int commit_quick_parse(
122 git_commit_list_node
*node
,
129 commit
= git__calloc(1, sizeof(*commit
));
130 GIT_ERROR_CHECK_ALLOC(commit
);
131 commit
->object
.repo
= walk
->repo
;
133 if (git_commit__parse_ext(commit
, obj
, GIT_COMMIT_PARSE_QUICK
) < 0) {
138 if (!git__is_uint16(git_array_size(commit
->parent_ids
))) {
140 git_error_set(GIT_ERROR_INVALID
, "commit has more than 2^16 parents");
144 node
->generation
= 0;
145 node
->time
= commit
->committer
->when
.time
;
146 node
->out_degree
= (uint16_t) git_array_size(commit
->parent_ids
);
147 node
->parents
= alloc_parents(walk
, node
, node
->out_degree
);
148 GIT_ERROR_CHECK_ALLOC(node
->parents
);
150 git_array_foreach(commit
->parent_ids
, i
, parent_oid
) {
151 node
->parents
[i
] = git_revwalk__commit_lookup(walk
, parent_oid
);
154 git_commit__free(commit
);
161 int git_commit_list_parse(git_revwalk
*walk
, git_commit_list_node
*commit
)
164 git_commit_graph_file
*cgraph_file
= NULL
;
170 /* Let's try to use the commit graph first. */
171 git_odb__get_commit_graph_file(&cgraph_file
, walk
->odb
);
173 git_commit_graph_entry e
;
175 error
= git_commit_graph_entry_find(&e
, cgraph_file
, &commit
->oid
, GIT_OID_RAWSZ
);
176 if (error
== 0 && git__is_uint16(e
.parent_count
)) {
178 commit
->generation
= (uint32_t)e
.generation
;
179 commit
->time
= e
.commit_time
;
180 commit
->out_degree
= (uint16_t)e
.parent_count
;
181 commit
->parents
= alloc_parents(walk
, commit
, commit
->out_degree
);
182 GIT_ERROR_CHECK_ALLOC(commit
->parents
);
184 for (i
= 0; i
< commit
->out_degree
; ++i
) {
185 git_commit_graph_entry parent
;
186 error
= git_commit_graph_entry_parent(&parent
, cgraph_file
, &e
, i
);
189 commit
->parents
[i
] = git_revwalk__commit_lookup(walk
, &parent
.sha1
);
196 if ((error
= git_odb_read(&obj
, walk
->odb
, &commit
->oid
)) < 0)
199 if (obj
->cached
.type
!= GIT_OBJECT_COMMIT
) {
200 git_error_set(GIT_ERROR_INVALID
, "object is no commit object");
203 error
= commit_quick_parse(walk
, commit
, obj
);
205 git_odb_object_free(obj
);