object.c 6.55 KB
Newer Older
1
/*
Vicent Marti committed
2
 * Copyright (C) 2009-2011 the libgit2 contributors
3
 *
Vicent Marti committed
4 5
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 */
#include <stdarg.h>

#include "git2/object.h"

#include "common.h"
#include "repository.h"

#include "commit.h"
#include "tree.h"
#include "blob.h"
#include "tag.h"

static const int OBJECT_BASE_SIZE = 4096;

static struct {
Vicent Marti committed
22 23
	const char	*str;	/* type name string */
	int			loose; /* valid loose object type flag */
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
	size_t		size;	/* size in bytes of the object structure */
} git_objects_table[] = {
	/* 0 = GIT_OBJ__EXT1 */
	{ "", 0, 0},

	/* 1 = GIT_OBJ_COMMIT */
	{ "commit", 1, sizeof(struct git_commit)},

	/* 2 = GIT_OBJ_TREE */
	{ "tree", 1, sizeof(struct git_tree) },

	/* 3 = GIT_OBJ_BLOB */
	{ "blob", 1, sizeof(struct git_blob) },

	/* 4 = GIT_OBJ_TAG */
	{ "tag", 1, sizeof(struct git_tag) },

	/* 5 = GIT_OBJ__EXT2 */
	{ "", 0, 0 },

	/* 6 = GIT_OBJ_OFS_DELTA */
	{ "OFS_DELTA", 0, 0 },

	/* 7 = GIT_OBJ_REF_DELTA */
	{ "REF_DELTA", 0, 0	}
};

51 52 53 54 55 56 57 58 59 60 61 62
static int create_object(git_object **object_out, git_otype type)
{
	git_object *object = NULL;

	assert(object_out);

	*object_out = NULL;

	switch (type) {
	case GIT_OBJ_COMMIT:
	case GIT_OBJ_TAG:
	case GIT_OBJ_BLOB:
Vicent Marti committed
63
	case GIT_OBJ_TREE:
64 65 66 67 68 69 70
		object = git__malloc(git_object__size(type));
		if (object == NULL)
			return GIT_ENOMEM;
		memset(object, 0x0, git_object__size(type));
		break;

	default:
Vicent Marti committed
71
		return git__throw(GIT_EINVALIDTYPE, "The given type is invalid");
72 73
	}

Vicent Marti committed
74
	object->type = type;
75 76 77 78 79

	*object_out = object;
	return GIT_SUCCESS;
}

Vicent Marti committed
80
int git_object_lookup_prefix(git_object **object_out, git_repository *repo, const git_oid *id, unsigned int len, git_otype type)
81 82
{
	git_object *object = NULL;
Vicent Marti committed
83
	git_odb_object *odb_obj;
84 85 86 87
	int error = GIT_SUCCESS;

	assert(repo && object_out && id);

88
	if (len < GIT_OID_MINPREFIXLEN)
Vicent Marti committed
89 90 91 92
		return git__throw(GIT_EAMBIGUOUSOIDPREFIX,
			"Failed to lookup object. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);

	if (len > GIT_OID_HEXSZ)
93
		len = GIT_OID_HEXSZ;
94

Vicent Marti committed
95
	if (len == GIT_OID_HEXSZ) {
96 97 98 99 100 101
		/* We want to match the full id : we can first look up in the cache,
		 * since there is no need to check for non ambiguousity
		 */
		object = git_cache_get(&repo->objects, id);
		if (object != NULL) {
			if (type != GIT_OBJ_ANY && type != object->type)
102 103
			{
				git_object_close(object);
104
				return git__throw(GIT_EINVALIDTYPE, "Failed to lookup object. The given type does not match the type on the ODB");
105
			}
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128

			*object_out = object;
			return GIT_SUCCESS;
		}

		/* Object was not found in the cache, let's explore the backends.
		 * We could just use git_odb_read_unique_short_oid,
		 * it is the same cost for packed and loose object backends,
		 * but it may be much more costly for sqlite and hiredis.
		 */
		error = git_odb_read(&odb_obj, repo->db, id);
	} else {
		git_oid short_oid;

		/* We copy the first len*4 bits from id and fill the remaining with 0s */
		memcpy(short_oid.id, id->id, (len + 1) / 2);
		if (len % 2)
			short_oid.id[len / 2] &= 0xF0;
		memset(short_oid.id + (len + 1) / 2, 0, (GIT_OID_HEXSZ - len) / 2);

		/* If len < GIT_OID_HEXSZ (a strict short oid was given), we have
		 * 2 options :
		 * - We always search in the cache first. If we find that short oid is
Vicent Marti committed
129 130 131 132
		 *	ambiguous, we can stop. But in all the other cases, we must then
		 *	explore all the backends (to find an object if there was match,
		 *	or to check that oid is not ambiguous if we have found 1 match in
		 *	the cache)
133 134 135
		 * - We never explore the cache, go right to exploring the backends
		 * We chose the latter : we explore directly the backends.
		 */
Vicent Marti committed
136
		error = git_odb_read_prefix(&odb_obj, repo->db, &short_oid, len);
137 138 139
	}

	if (error < GIT_SUCCESS)
140
		return git__rethrow(error, "Failed to lookup object");
141

Vicent Marti committed
142 143
	if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) {
		git_odb_object_close(odb_obj);
Vicent Marti committed
144
		return git__throw(GIT_EINVALIDTYPE, "Failed to lookup object. The given type does not match the type on the ODB");
145 146
	}

Vicent Marti committed
147
	type = odb_obj->raw.type;
148 149

	if ((error = create_object(&object, type)) < GIT_SUCCESS)
150
		return git__rethrow(error, "Failed to lookup object");
151 152

	/* Initialize parent object */
Vicent Marti committed
153
	git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
154 155 156 157
	object->repo = repo;

	switch (type) {
	case GIT_OBJ_COMMIT:
Vicent Marti committed
158
		error = git_commit__parse((git_commit *)object, odb_obj);
159 160 161
		break;

	case GIT_OBJ_TREE:
Vicent Marti committed
162
		error = git_tree__parse((git_tree *)object, odb_obj);
163 164 165
		break;

	case GIT_OBJ_TAG:
Vicent Marti committed
166
		error = git_tag__parse((git_tag *)object, odb_obj);
167 168 169
		break;

	case GIT_OBJ_BLOB:
Vicent Marti committed
170
		error = git_blob__parse((git_blob *)object, odb_obj);
171 172 173 174 175 176
		break;

	default:
		break;
	}

Vicent Marti committed
177 178
	git_odb_object_close(odb_obj);

179
	if (error < GIT_SUCCESS) {
180
		git_object__free(object);
181
		return git__rethrow(error, "Failed to lookup object");
182 183
	}

Vicent Marti committed
184
	*object_out = git_cache_try_store(&repo->objects, object);
185 186 187
	return GIT_SUCCESS;
}

188
int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) {
Vicent Marti committed
189
	return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type);
190 191
}

Vicent Marti committed
192
void git_object__free(void *_obj)
193
{
Vicent Marti committed
194
	git_object *object = (git_object *)_obj;
195 196 197

	assert(object);

Vicent Marti committed
198
	switch (object->type) {
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
	case GIT_OBJ_COMMIT:
		git_commit__free((git_commit *)object);
		break;

	case GIT_OBJ_TREE:
		git_tree__free((git_tree *)object);
		break;

	case GIT_OBJ_TAG:
		git_tag__free((git_tag *)object);
		break;

	case GIT_OBJ_BLOB:
		git_blob__free((git_blob *)object);
		break;

	default:
		free(object);
		break;
	}
}

221 222 223 224 225
void git_object_close(git_object *object)
{
	if (object == NULL)
		return;

Vicent Marti committed
226
	git_cached_obj_decref((git_cached_obj *)object, git_object__free);
227 228
}

229
const git_oid *git_object_id(const git_object *obj)
230 231
{
	assert(obj);
Vicent Marti committed
232
	return &obj->cached.oid;
233 234
}

235
git_otype git_object_type(const git_object *obj)
236 237
{
	assert(obj);
Vicent Marti committed
238
	return obj->type;
239 240
}

241
git_repository *git_object_owner(const git_object *obj)
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
{
	assert(obj);
	return obj->repo;
}

const char *git_object_type2string(git_otype type)
{
	if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
		return "";

	return git_objects_table[type].str;
}

git_otype git_object_string2type(const char *str)
{
	size_t i;

	if (!str || !*str)
		return GIT_OBJ_BAD;

	for (i = 0; i < ARRAY_SIZE(git_objects_table); i++)
		if (!strcmp(str, git_objects_table[i].str))
			return (git_otype)i;

	return GIT_OBJ_BAD;
}

int git_object_typeisloose(git_otype type)
{
	if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
		return 0;

	return git_objects_table[type].loose;
}

size_t git_object__size(git_otype type)
{
	if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
		return 0;

	return git_objects_table[type].size;
}