]>
Commit | Line | Data |
---|---|---|
e52ed7a5 | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
e52ed7a5 | 3 | * |
bb742ede VM |
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. | |
e52ed7a5 VM |
6 | */ |
7 | #include <stdarg.h> | |
8 | ||
9 | #include "git2/object.h" | |
10 | ||
11 | #include "common.h" | |
12 | #include "repository.h" | |
13 | ||
14 | #include "commit.h" | |
15 | #include "tree.h" | |
16 | #include "blob.h" | |
17 | #include "tag.h" | |
18 | ||
19 | static const int OBJECT_BASE_SIZE = 4096; | |
20 | ||
78606263 | 21 | typedef struct { |
87d9869f | 22 | const char *str; /* type name string */ |
e52ed7a5 | 23 | size_t size; /* size in bytes of the object structure */ |
78606263 | 24 | |
3f27127d | 25 | int (*parse)(void *self, git_odb_object *obj); |
78606263 RB |
26 | void (*free)(void *self); |
27 | } git_object_def; | |
28 | ||
29 | static git_object_def git_objects_table[] = { | |
e52ed7a5 | 30 | /* 0 = GIT_OBJ__EXT1 */ |
3f27127d | 31 | { "", 0, NULL, NULL }, |
e52ed7a5 VM |
32 | |
33 | /* 1 = GIT_OBJ_COMMIT */ | |
3f27127d | 34 | { "commit", sizeof(git_commit), git_commit__parse, git_commit__free }, |
e52ed7a5 VM |
35 | |
36 | /* 2 = GIT_OBJ_TREE */ | |
3f27127d | 37 | { "tree", sizeof(git_tree), git_tree__parse, git_tree__free }, |
e52ed7a5 VM |
38 | |
39 | /* 3 = GIT_OBJ_BLOB */ | |
3f27127d | 40 | { "blob", sizeof(git_blob), git_blob__parse, git_blob__free }, |
e52ed7a5 VM |
41 | |
42 | /* 4 = GIT_OBJ_TAG */ | |
3f27127d | 43 | { "tag", sizeof(git_tag), git_tag__parse, git_tag__free }, |
e52ed7a5 VM |
44 | |
45 | /* 5 = GIT_OBJ__EXT2 */ | |
3f27127d | 46 | { "", 0, NULL, NULL }, |
e52ed7a5 | 47 | /* 6 = GIT_OBJ_OFS_DELTA */ |
3f27127d | 48 | { "OFS_DELTA", 0, NULL, NULL }, |
e52ed7a5 | 49 | /* 7 = GIT_OBJ_REF_DELTA */ |
3f27127d | 50 | { "REF_DELTA", 0, NULL, NULL }, |
e52ed7a5 VM |
51 | }; |
52 | ||
c6ac28fd RB |
53 | int git_object__from_odb_object( |
54 | git_object **object_out, | |
55 | git_repository *repo, | |
56 | git_odb_object *odb_obj, | |
57 | git_otype type) | |
58 | { | |
59 | int error; | |
78606263 RB |
60 | size_t object_size; |
61 | git_object_def *def; | |
c6ac28fd RB |
62 | git_object *object = NULL; |
63 | ||
78606263 RB |
64 | assert(object_out); |
65 | *object_out = NULL; | |
66 | ||
67 | /* Validate type match */ | |
8842c75f VM |
68 | if (type != GIT_OBJ_ANY && type != odb_obj->cached.type) { |
69 | giterr_set(GITERR_INVALID, | |
70 | "The requested type does not match the type in the ODB"); | |
c6ac28fd RB |
71 | return GIT_ENOTFOUND; |
72 | } | |
73 | ||
78606263 RB |
74 | if ((object_size = git_object__size(odb_obj->cached.type)) == 0) { |
75 | giterr_set(GITERR_INVALID, "The requested type is invalid"); | |
76 | return GIT_ENOTFOUND; | |
77 | } | |
78 | ||
79 | /* Allocate and initialize base object */ | |
80 | object = git__calloc(1, object_size); | |
81 | GITERR_CHECK_ALLOC(object); | |
c6ac28fd | 82 | |
c6ac28fd | 83 | git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid); |
cf7850a4 | 84 | object->cached.type = odb_obj->cached.type; |
78606263 | 85 | object->cached.size = odb_obj->cached.size; |
c6ac28fd RB |
86 | object->repo = repo; |
87 | ||
78606263 RB |
88 | /* Parse raw object data */ |
89 | def = &git_objects_table[odb_obj->cached.type]; | |
3f27127d | 90 | assert(def->free && def->parse); |
c6ac28fd | 91 | |
3f27127d | 92 | if ((error = def->parse(object, odb_obj)) < 0) |
78606263 | 93 | def->free(object); |
3f27127d RB |
94 | else |
95 | *object_out = git_cache_store_parsed(&repo->objects, object); | |
c6ac28fd | 96 | |
3f27127d | 97 | return error; |
c6ac28fd RB |
98 | } |
99 | ||
78606263 RB |
100 | void git_object__free(void *obj) |
101 | { | |
102 | git_otype type = ((git_object *)obj)->cached.type; | |
103 | ||
104 | if (type < 0 || ((size_t)type) >= ARRAY_SIZE(git_objects_table) || | |
105 | !git_objects_table[type].free) | |
106 | git__free(obj); | |
107 | else | |
108 | git_objects_table[type].free(obj); | |
109 | } | |
110 | ||
9462c471 VM |
111 | int git_object_lookup_prefix( |
112 | git_object **object_out, | |
113 | git_repository *repo, | |
114 | const git_oid *id, | |
b8457baa | 115 | size_t len, |
9462c471 | 116 | git_otype type) |
5de079b8 VM |
117 | { |
118 | git_object *object = NULL; | |
9462c471 | 119 | git_odb *odb = NULL; |
72a3fe42 | 120 | git_odb_object *odb_obj; |
e172cf08 | 121 | int error = 0; |
5de079b8 VM |
122 | |
123 | assert(repo && object_out && id); | |
124 | ||
8915a140 RB |
125 | if (len < GIT_OID_MINPREFIXLEN) { |
126 | giterr_set(GITERR_OBJECT, "Ambiguous lookup - OID prefix is too short"); | |
904b67e6 | 127 | return GIT_EAMBIGUOUS; |
8915a140 | 128 | } |
d0323a5f | 129 | |
9462c471 | 130 | error = git_repository_odb__weakptr(&odb, repo); |
e172cf08 | 131 | if (error < 0) |
9462c471 VM |
132 | return error; |
133 | ||
d0323a5f | 134 | if (len > GIT_OID_HEXSZ) |
dd453c4d | 135 | len = GIT_OID_HEXSZ; |
bd1aa741 | 136 | |
87d9869f | 137 | if (len == GIT_OID_HEXSZ) { |
5df18424 VM |
138 | git_cached_obj *cached = NULL; |
139 | ||
dd453c4d MP |
140 | /* We want to match the full id : we can first look up in the cache, |
141 | * since there is no need to check for non ambiguousity | |
142 | */ | |
5df18424 VM |
143 | cached = git_cache_get_any(&repo->objects, id); |
144 | if (cached != NULL) { | |
145 | if (cached->flags == GIT_CACHE_STORE_PARSED) { | |
146 | object = (git_object *)cached; | |
147 | ||
cf7850a4 | 148 | if (type != GIT_OBJ_ANY && type != object->cached.type) { |
5df18424 VM |
149 | git_object_free(object); |
150 | giterr_set(GITERR_INVALID, | |
151 | "The requested type does not match the type in ODB"); | |
152 | return GIT_ENOTFOUND; | |
153 | } | |
154 | ||
155 | *object_out = object; | |
156 | return 0; | |
157 | } else if (cached->flags == GIT_CACHE_STORE_RAW) { | |
158 | odb_obj = (git_odb_object *)cached; | |
159 | } else { | |
160 | assert(!"Wrong caching type in the global object cache"); | |
7d3ec3ca | 161 | } |
5df18424 VM |
162 | } else { |
163 | /* Object was not found in the cache, let's explore the backends. | |
164 | * We could just use git_odb_read_unique_short_oid, | |
165 | * it is the same cost for packed and loose object backends, | |
166 | * but it may be much more costly for sqlite and hiredis. | |
167 | */ | |
168 | error = git_odb_read(&odb_obj, odb, id); | |
dd453c4d | 169 | } |
dd453c4d MP |
170 | } else { |
171 | git_oid short_oid; | |
172 | ||
173 | /* We copy the first len*4 bits from id and fill the remaining with 0s */ | |
174 | memcpy(short_oid.id, id->id, (len + 1) / 2); | |
175 | if (len % 2) | |
176 | short_oid.id[len / 2] &= 0xF0; | |
177 | memset(short_oid.id + (len + 1) / 2, 0, (GIT_OID_HEXSZ - len) / 2); | |
178 | ||
179 | /* If len < GIT_OID_HEXSZ (a strict short oid was given), we have | |
180 | * 2 options : | |
181 | * - We always search in the cache first. If we find that short oid is | |
87d9869f VM |
182 | * ambiguous, we can stop. But in all the other cases, we must then |
183 | * explore all the backends (to find an object if there was match, | |
184 | * or to check that oid is not ambiguous if we have found 1 match in | |
185 | * the cache) | |
dd453c4d MP |
186 | * - We never explore the cache, go right to exploring the backends |
187 | * We chose the latter : we explore directly the backends. | |
188 | */ | |
9462c471 | 189 | error = git_odb_read_prefix(&odb_obj, odb, &short_oid, len); |
5de079b8 VM |
190 | } |
191 | ||
3aa351ea | 192 | if (error < 0) |
282283ac | 193 | return error; |
5de079b8 | 194 | |
c6ac28fd | 195 | error = git_object__from_odb_object(object_out, repo, odb_obj, type); |
5de079b8 | 196 | |
45e79e37 | 197 | git_odb_object_free(odb_obj); |
72a3fe42 | 198 | |
c6ac28fd | 199 | return error; |
5de079b8 VM |
200 | } |
201 | ||
dd453c4d | 202 | int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) { |
d0323a5f | 203 | return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type); |
dd453c4d MP |
204 | } |
205 | ||
45e79e37 | 206 | void git_object_free(git_object *object) |
48c27f86 VM |
207 | { |
208 | if (object == NULL) | |
209 | return; | |
210 | ||
5df18424 | 211 | git_cached_obj_decref(object); |
48c27f86 VM |
212 | } |
213 | ||
17cdf252 | 214 | const git_oid *git_object_id(const git_object *obj) |
e52ed7a5 VM |
215 | { |
216 | assert(obj); | |
72a3fe42 | 217 | return &obj->cached.oid; |
e52ed7a5 VM |
218 | } |
219 | ||
17cdf252 | 220 | git_otype git_object_type(const git_object *obj) |
e52ed7a5 VM |
221 | { |
222 | assert(obj); | |
cf7850a4 | 223 | return obj->cached.type; |
e52ed7a5 VM |
224 | } |
225 | ||
17cdf252 | 226 | git_repository *git_object_owner(const git_object *obj) |
e52ed7a5 VM |
227 | { |
228 | assert(obj); | |
229 | return obj->repo; | |
230 | } | |
231 | ||
232 | const char *git_object_type2string(git_otype type) | |
233 | { | |
234 | if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table)) | |
235 | return ""; | |
236 | ||
237 | return git_objects_table[type].str; | |
238 | } | |
239 | ||
240 | git_otype git_object_string2type(const char *str) | |
241 | { | |
242 | size_t i; | |
243 | ||
244 | if (!str || !*str) | |
245 | return GIT_OBJ_BAD; | |
246 | ||
247 | for (i = 0; i < ARRAY_SIZE(git_objects_table); i++) | |
248 | if (!strcmp(str, git_objects_table[i].str)) | |
249 | return (git_otype)i; | |
250 | ||
251 | return GIT_OBJ_BAD; | |
252 | } | |
253 | ||
254 | int git_object_typeisloose(git_otype type) | |
255 | { | |
256 | if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table)) | |
257 | return 0; | |
258 | ||
78606263 | 259 | return (git_objects_table[type].size > 0) ? 1 : 0; |
e52ed7a5 VM |
260 | } |
261 | ||
262 | size_t git_object__size(git_otype type) | |
263 | { | |
264 | if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table)) | |
265 | return 0; | |
266 | ||
267 | return git_objects_table[type].size; | |
268 | } | |
269 | ||
db9be945 | 270 | static int dereference_object(git_object **dereferenced, git_object *obj) |
271 | { | |
272 | git_otype type = git_object_type(obj); | |
273 | ||
274 | switch (type) { | |
275 | case GIT_OBJ_COMMIT: | |
276 | return git_commit_tree((git_tree **)dereferenced, (git_commit*)obj); | |
db9be945 | 277 | |
278 | case GIT_OBJ_TAG: | |
279 | return git_tag_target(dereferenced, (git_tag*)obj); | |
d8057a5b RB |
280 | |
281 | case GIT_OBJ_BLOB: | |
bc05f30c | 282 | return GIT_ENOTFOUND; |
d8057a5b RB |
283 | |
284 | case GIT_OBJ_TREE: | |
bc05f30c | 285 | return GIT_EAMBIGUOUS; |
db9be945 | 286 | |
287 | default: | |
bc05f30c | 288 | return GIT_EINVALIDSPEC; |
db9be945 | 289 | } |
290 | } | |
291 | ||
bc05f30c | 292 | static int peel_error(int error, const git_oid *oid, git_otype type) |
293 | { | |
294 | const char *type_name; | |
295 | char hex_oid[GIT_OID_HEXSZ + 1]; | |
296 | ||
297 | type_name = git_object_type2string(type); | |
298 | ||
299 | git_oid_fmt(hex_oid, oid); | |
300 | hex_oid[GIT_OID_HEXSZ] = '\0'; | |
301 | ||
302 | giterr_set(GITERR_OBJECT, "The git_object of id '%s' can not be " | |
303 | "successfully peeled into a %s (git_otype=%i).", hex_oid, type_name, type); | |
304 | ||
305 | return error; | |
306 | } | |
307 | ||
db9be945 | 308 | int git_object_peel( |
d8057a5b | 309 | git_object **peeled, |
cfbe4be3 | 310 | const git_object *object, |
d8057a5b | 311 | git_otype target_type) |
db9be945 | 312 | { |
313 | git_object *source, *deref = NULL; | |
bc05f30c | 314 | int error; |
315 | ||
d8057a5b | 316 | assert(object && peeled); |
db9be945 | 317 | |
318 | if (git_object_type(object) == target_type) | |
575a54db | 319 | return git_object_dup(peeled, (git_object *)object); |
db9be945 | 320 | |
0cce210a RB |
321 | assert(target_type == GIT_OBJ_TAG || |
322 | target_type == GIT_OBJ_COMMIT || | |
323 | target_type == GIT_OBJ_TREE || | |
324 | target_type == GIT_OBJ_BLOB || | |
325 | target_type == GIT_OBJ_ANY); | |
8915a140 | 326 | |
cfbe4be3 | 327 | source = (git_object *)object; |
db9be945 | 328 | |
bc05f30c | 329 | while (!(error = dereference_object(&deref, source))) { |
db9be945 | 330 | |
331 | if (source != object) | |
332 | git_object_free(source); | |
333 | ||
334 | if (git_object_type(deref) == target_type) { | |
335 | *peeled = deref; | |
336 | return 0; | |
337 | } | |
338 | ||
d8057a5b RB |
339 | if (target_type == GIT_OBJ_ANY && |
340 | git_object_type(deref) != git_object_type(object)) | |
341 | { | |
342 | *peeled = deref; | |
343 | return 0; | |
344 | } | |
345 | ||
db9be945 | 346 | source = deref; |
347 | deref = NULL; | |
348 | } | |
349 | ||
db9be945 | 350 | if (source != object) |
351 | git_object_free(source); | |
d8057a5b | 352 | |
db9be945 | 353 | git_object_free(deref); |
bc05f30c | 354 | |
355 | if (error) | |
356 | error = peel_error(error, git_object_id(object), target_type); | |
357 | ||
358 | return error; | |
db9be945 | 359 | } |
613d5eb9 | 360 | |
575a54db VM |
361 | int git_object_dup(git_object **dest, git_object *source) |
362 | { | |
363 | git_cached_obj_incref(source); | |
364 | *dest = source; | |
365 | return 0; | |
366 | } |