1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
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.
6 */
7
8 #include "commit_list.h"
9
10 #include "revwalk.h"
11 #include "pool.h"
12 #include "odb.h"
13 #include "commit.h"
14
git_commit_list_generation_cmp(const void * a,const void * b)15 int git_commit_list_generation_cmp(const void *a, const void *b)
16 {
17 uint32_t generation_a = ((git_commit_list_node *) a)->generation;
18 uint32_t generation_b = ((git_commit_list_node *) b)->generation;
19
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);
23 }
24
25 if (generation_a < generation_b)
26 return 1;
27 if (generation_a > generation_b)
28 return -1;
29
30 return 0;
31 }
32
git_commit_list_time_cmp(const void * a,const void * b)33 int git_commit_list_time_cmp(const void *a, const void *b)
34 {
35 int64_t time_a = ((git_commit_list_node *) a)->time;
36 int64_t time_b = ((git_commit_list_node *) b)->time;
37
38 if (time_a < time_b)
39 return 1;
40 if (time_a > time_b)
41 return -1;
42
43 return 0;
44 }
45
git_commit_list_insert(git_commit_list_node * item,git_commit_list ** list_p)46 git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p)
47 {
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;
52 }
53 *list_p = new_list;
54 return new_list;
55 }
56
git_commit_list_insert_by_date(git_commit_list_node * item,git_commit_list ** list_p)57 git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p)
58 {
59 git_commit_list **pp = list_p;
60 git_commit_list *p;
61
62 while ((p = *pp) != NULL) {
63 if (git_commit_list_time_cmp(p->item, item) > 0)
64 break;
65
66 pp = &p->next;
67 }
68
69 return git_commit_list_insert(item, pp);
70 }
71
git_commit_list_alloc_node(git_revwalk * walk)72 git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk)
73 {
74 return (git_commit_list_node *)git_pool_mallocz(&walk->commit_pool, 1);
75 }
76
alloc_parents(git_revwalk * walk,git_commit_list_node * commit,size_t n_parents)77 static git_commit_list_node **alloc_parents(
78 git_revwalk *walk, git_commit_list_node *commit, size_t n_parents)
79 {
80 size_t bytes;
81
82 if (n_parents <= PARENTS_PER_COMMIT)
83 return (git_commit_list_node **)((char *)commit + sizeof(git_commit_list_node));
84
85 if (git__multiply_sizet_overflow(&bytes, n_parents, sizeof(git_commit_list_node *)))
86 return NULL;
87
88 return (git_commit_list_node **)git_pool_malloc(&walk->commit_pool, bytes);
89 }
90
91
git_commit_list_free(git_commit_list ** list_p)92 void git_commit_list_free(git_commit_list **list_p)
93 {
94 git_commit_list *list = *list_p;
95
96 if (list == NULL)
97 return;
98
99 while (list) {
100 git_commit_list *temp = list;
101 list = temp->next;
102 git__free(temp);
103 }
104
105 *list_p = NULL;
106 }
107
git_commit_list_pop(git_commit_list ** stack)108 git_commit_list_node *git_commit_list_pop(git_commit_list **stack)
109 {
110 git_commit_list *top = *stack;
111 git_commit_list_node *item = top ? top->item : NULL;
112
113 if (top) {
114 *stack = top->next;
115 git__free(top);
116 }
117 return item;
118 }
119
commit_quick_parse(git_revwalk * walk,git_commit_list_node * node,git_odb_object * obj)120 static int commit_quick_parse(
121 git_revwalk *walk,
122 git_commit_list_node *node,
123 git_odb_object *obj)
124 {
125 git_oid *parent_oid;
126 git_commit *commit;
127 int error;
128 size_t i;
129
130 commit = git__calloc(1, sizeof(*commit));
131 GIT_ERROR_CHECK_ALLOC(commit);
132 commit->object.repo = walk->repo;
133
134 if ((error = git_commit__parse_ext(commit, obj, GIT_COMMIT_PARSE_QUICK)) < 0) {
135 git__free(commit);
136 return error;
137 }
138
139 if (!git__is_uint16(git_array_size(commit->parent_ids))) {
140 git__free(commit);
141 git_error_set(GIT_ERROR_INVALID, "commit has more than 2^16 parents");
142 return -1;
143 }
144
145 node->generation = 0;
146 node->time = commit->committer->when.time;
147 node->out_degree = (uint16_t) git_array_size(commit->parent_ids);
148 node->parents = alloc_parents(walk, node, node->out_degree);
149 GIT_ERROR_CHECK_ALLOC(node->parents);
150
151 git_array_foreach(commit->parent_ids, i, parent_oid) {
152 node->parents[i] = git_revwalk__commit_lookup(walk, parent_oid);
153 }
154
155 git_commit__free(commit);
156
157 node->parsed = 1;
158
159 return 0;
160 }
161
git_commit_list_parse(git_revwalk * walk,git_commit_list_node * commit)162 int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit)
163 {
164 git_odb_object *obj;
165 git_commit_graph_file *cgraph_file = NULL;
166 int error;
167
168 if (commit->parsed)
169 return 0;
170
171 /* Let's try to use the commit graph first. */
172 git_odb__get_commit_graph_file(&cgraph_file, walk->odb);
173 if (cgraph_file) {
174 git_commit_graph_entry e;
175
176 error = git_commit_graph_entry_find(&e, cgraph_file, &commit->oid, GIT_OID_RAWSZ);
177 if (error == 0 && git__is_uint16(e.parent_count)) {
178 size_t i;
179 commit->generation = (uint32_t)e.generation;
180 commit->time = e.commit_time;
181 commit->out_degree = (uint16_t)e.parent_count;
182 commit->parents = alloc_parents(walk, commit, commit->out_degree);
183 GIT_ERROR_CHECK_ALLOC(commit->parents);
184
185 for (i = 0; i < commit->out_degree; ++i) {
186 git_commit_graph_entry parent;
187 error = git_commit_graph_entry_parent(&parent, cgraph_file, &e, i);
188 if (error < 0)
189 return error;
190 commit->parents[i] = git_revwalk__commit_lookup(walk, &parent.sha1);
191 }
192 commit->parsed = 1;
193 return 0;
194 }
195 }
196
197 if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0)
198 return error;
199
200 if (obj->cached.type != GIT_OBJECT_COMMIT) {
201 git_error_set(GIT_ERROR_INVALID, "object is no commit object");
202 error = -1;
203 } else
204 error = commit_quick_parse(walk, commit, obj);
205
206 git_odb_object_free(obj);
207 return error;
208 }
209
210