summaryrefslogtreecommitdiff
path: root/object.c
diff options
context:
space:
mode:
Diffstat (limited to 'object.c')
-rw-r--r--object.c489
1 files changed, 391 insertions, 98 deletions
diff --git a/object.c b/object.c
index 20703f52ed..23a24e678a 100644
--- a/object.c
+++ b/object.c
@@ -1,21 +1,23 @@
#include "cache.h"
#include "object.h"
+#include "replace-object.h"
+#include "object-store.h"
#include "blob.h"
#include "tree.h"
#include "commit.h"
#include "tag.h"
-
-static struct object **obj_hash;
-static int nr_objs, obj_hash_size;
+#include "alloc.h"
+#include "packfile.h"
+#include "commit-graph.h"
unsigned int get_max_object_index(void)
{
- return obj_hash_size;
+ return the_repository->parsed_objects->obj_hash_size;
}
struct object *get_indexed_object(unsigned int idx)
{
- return obj_hash[idx];
+ return the_repository->parsed_objects->obj_hash[idx];
}
static const char *object_type_strings[] = {
@@ -26,33 +28,49 @@ static const char *object_type_strings[] = {
"tag", /* OBJ_TAG = 4 */
};
-const char *typename(unsigned int type)
+const char *type_name(unsigned int type)
{
if (type >= ARRAY_SIZE(object_type_strings))
return NULL;
return object_type_strings[type];
}
-int type_from_string(const char *str)
+int type_from_string_gently(const char *str, ssize_t len, int gentle)
{
int i;
+ if (len < 0)
+ len = strlen(str);
+
for (i = 1; i < ARRAY_SIZE(object_type_strings); i++)
- if (!strcmp(str, object_type_strings[i]))
+ if (!strncmp(str, object_type_strings[i], len) &&
+ object_type_strings[i][len] == '\0')
return i;
- die("invalid object type \"%s\"", str);
+
+ if (gentle)
+ return -1;
+
+ die(_("invalid object type \"%s\""), str);
}
-static unsigned int hash_obj(struct object *obj, unsigned int n)
+/*
+ * Return a numerical hash value between 0 and n-1 for the object with
+ * the specified sha1. n must be a power of 2. Please note that the
+ * return value is *not* consistent across computer architectures.
+ */
+static unsigned int hash_obj(const struct object_id *oid, unsigned int n)
{
- unsigned int hash;
- memcpy(&hash, obj->sha1, sizeof(unsigned int));
- return hash % n;
+ return oidhash(oid) & (n - 1);
}
+/*
+ * Insert obj into the hash table hash, which has length size (which
+ * must be a power of 2). On collisions, simply overflow to the next
+ * empty bucket.
+ */
static void insert_obj_hash(struct object *obj, struct object **hash, unsigned int size)
{
- unsigned int j = hash_obj(obj, size);
+ unsigned int j = hash_obj(&obj->oid, size);
while (hash[j]) {
j++;
@@ -62,91 +80,144 @@ static void insert_obj_hash(struct object *obj, struct object **hash, unsigned i
hash[j] = obj;
}
-static unsigned int hashtable_index(const unsigned char *sha1)
-{
- unsigned int i;
- memcpy(&i, sha1, sizeof(unsigned int));
- return i % obj_hash_size;
-}
-
-struct object *lookup_object(const unsigned char *sha1)
+/*
+ * Look up the record for the given sha1 in the hash map stored in
+ * obj_hash. Return NULL if it was not found.
+ */
+struct object *lookup_object(struct repository *r, const struct object_id *oid)
{
- unsigned int i;
+ unsigned int i, first;
struct object *obj;
- if (!obj_hash)
+ if (!r->parsed_objects->obj_hash)
return NULL;
- i = hashtable_index(sha1);
- while ((obj = obj_hash[i]) != NULL) {
- if (!hashcmp(sha1, obj->sha1))
+ first = i = hash_obj(oid, r->parsed_objects->obj_hash_size);
+ while ((obj = r->parsed_objects->obj_hash[i]) != NULL) {
+ if (oideq(oid, &obj->oid))
break;
i++;
- if (i == obj_hash_size)
+ if (i == r->parsed_objects->obj_hash_size)
i = 0;
}
+ if (obj && i != first) {
+ /*
+ * Move object to where we started to look for it so
+ * that we do not need to walk the hash table the next
+ * time we look for it.
+ */
+ SWAP(r->parsed_objects->obj_hash[i],
+ r->parsed_objects->obj_hash[first]);
+ }
return obj;
}
-static void grow_object_hash(void)
+/*
+ * Increase the size of the hash map stored in obj_hash to the next
+ * power of 2 (but at least 32). Copy the existing values to the new
+ * hash map.
+ */
+static void grow_object_hash(struct repository *r)
{
int i;
- int new_hash_size = obj_hash_size < 32 ? 32 : 2 * obj_hash_size;
+ /*
+ * Note that this size must always be power-of-2 to match hash_obj
+ * above.
+ */
+ int new_hash_size = r->parsed_objects->obj_hash_size < 32 ? 32 : 2 * r->parsed_objects->obj_hash_size;
struct object **new_hash;
- new_hash = xcalloc(new_hash_size, sizeof(struct object *));
- for (i = 0; i < obj_hash_size; i++) {
- struct object *obj = obj_hash[i];
+ CALLOC_ARRAY(new_hash, new_hash_size);
+ for (i = 0; i < r->parsed_objects->obj_hash_size; i++) {
+ struct object *obj = r->parsed_objects->obj_hash[i];
+
if (!obj)
continue;
insert_obj_hash(obj, new_hash, new_hash_size);
}
- free(obj_hash);
- obj_hash = new_hash;
- obj_hash_size = new_hash_size;
+ free(r->parsed_objects->obj_hash);
+ r->parsed_objects->obj_hash = new_hash;
+ r->parsed_objects->obj_hash_size = new_hash_size;
}
-void *create_object(const unsigned char *sha1, int type, void *o)
+void *create_object(struct repository *r, const struct object_id *oid, void *o)
{
struct object *obj = o;
obj->parsed = 0;
- obj->used = 0;
- obj->type = type;
obj->flags = 0;
- hashcpy(obj->sha1, sha1);
+ oidcpy(&obj->oid, oid);
- if (obj_hash_size - 1 <= nr_objs * 2)
- grow_object_hash();
+ if (r->parsed_objects->obj_hash_size - 1 <= r->parsed_objects->nr_objs * 2)
+ grow_object_hash(r);
- insert_obj_hash(obj, obj_hash, obj_hash_size);
- nr_objs++;
+ insert_obj_hash(obj, r->parsed_objects->obj_hash,
+ r->parsed_objects->obj_hash_size);
+ r->parsed_objects->nr_objs++;
return obj;
}
-struct object *lookup_unknown_object(const unsigned char *sha1)
+void *object_as_type(struct object *obj, enum object_type type, int quiet)
{
- struct object *obj = lookup_object(sha1);
+ if (obj->type == type)
+ return obj;
+ else if (obj->type == OBJ_NONE) {
+ if (type == OBJ_COMMIT)
+ init_commit_node((struct commit *) obj);
+ else
+ obj->type = type;
+ return obj;
+ }
+ else {
+ if (!quiet)
+ error(_("object %s is a %s, not a %s"),
+ oid_to_hex(&obj->oid),
+ type_name(obj->type), type_name(type));
+ return NULL;
+ }
+}
+
+struct object *lookup_unknown_object(struct repository *r, const struct object_id *oid)
+{
+ struct object *obj = lookup_object(r, oid);
if (!obj)
- obj = create_object(sha1, OBJ_NONE, alloc_object_node());
+ obj = create_object(r, oid, alloc_object_node(r));
return obj;
}
-struct object *parse_object_buffer(const unsigned char *sha1, enum object_type type, unsigned long size, void *buffer, int *eaten_p)
+struct object *lookup_object_by_type(struct repository *r,
+ const struct object_id *oid,
+ enum object_type type)
+{
+ switch (type) {
+ case OBJ_COMMIT:
+ return (struct object *)lookup_commit(r, oid);
+ case OBJ_TREE:
+ return (struct object *)lookup_tree(r, oid);
+ case OBJ_TAG:
+ return (struct object *)lookup_tag(r, oid);
+ case OBJ_BLOB:
+ return (struct object *)lookup_blob(r, oid);
+ default:
+ die("BUG: unknown object type %d", type);
+ }
+}
+
+struct object *parse_object_buffer(struct repository *r, const struct object_id *oid, enum object_type type, unsigned long size, void *buffer, int *eaten_p)
{
struct object *obj;
- int eaten = 0;
+ *eaten_p = 0;
obj = NULL;
if (type == OBJ_BLOB) {
- struct blob *blob = lookup_blob(sha1);
+ struct blob *blob = lookup_blob(r, oid);
if (blob) {
if (parse_blob_buffer(blob, buffer, size))
return NULL;
obj = &blob->object;
}
} else if (type == OBJ_TREE) {
- struct tree *tree = lookup_tree(sha1);
+ struct tree *tree = lookup_tree(r, oid);
if (tree) {
obj = &tree->object;
if (!tree->buffer)
@@ -154,80 +225,79 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
if (!tree->object.parsed) {
if (parse_tree_buffer(tree, buffer, size))
return NULL;
- eaten = 1;
+ *eaten_p = 1;
}
}
} else if (type == OBJ_COMMIT) {
- struct commit *commit = lookup_commit(sha1);
+ struct commit *commit = lookup_commit(r, oid);
if (commit) {
- if (parse_commit_buffer(commit, buffer, size))
+ if (parse_commit_buffer(r, commit, buffer, size, 1))
return NULL;
- if (!commit->buffer) {
- commit->buffer = buffer;
- eaten = 1;
+ if (!get_cached_commit_buffer(r, commit, NULL)) {
+ set_commit_buffer(r, commit, buffer, size);
+ *eaten_p = 1;
}
obj = &commit->object;
}
} else if (type == OBJ_TAG) {
- struct tag *tag = lookup_tag(sha1);
+ struct tag *tag = lookup_tag(r, oid);
if (tag) {
- if (parse_tag_buffer(tag, buffer, size))
+ if (parse_tag_buffer(r, tag, buffer, size))
return NULL;
obj = &tag->object;
}
} else {
- warning("object %s has unknown type id %d", sha1_to_hex(sha1), type);
+ warning(_("object %s has unknown type id %d"), oid_to_hex(oid), type);
obj = NULL;
}
- if (obj && obj->type == OBJ_NONE)
- obj->type = type;
- *eaten_p = eaten;
return obj;
}
-struct object *parse_object_or_die(const unsigned char *sha1,
+struct object *parse_object_or_die(const struct object_id *oid,
const char *name)
{
- struct object *o = parse_object(sha1);
+ struct object *o = parse_object(the_repository, oid);
if (o)
return o;
- die(_("unable to parse object: %s"), name ? name : sha1_to_hex(sha1));
+ die(_("unable to parse object: %s"), name ? name : oid_to_hex(oid));
}
-struct object *parse_object(const unsigned char *sha1)
+struct object *parse_object(struct repository *r, const struct object_id *oid)
{
unsigned long size;
enum object_type type;
int eaten;
- const unsigned char *repl = lookup_replace_object(sha1);
+ const struct object_id *repl = lookup_replace_object(r, oid);
void *buffer;
struct object *obj;
- obj = lookup_object(sha1);
+ obj = lookup_object(r, oid);
if (obj && obj->parsed)
return obj;
- if ((obj && obj->type == OBJ_BLOB) ||
- (!obj && has_sha1_file(sha1) &&
- sha1_object_info(sha1, NULL) == OBJ_BLOB)) {
- if (check_sha1_signature(repl, NULL, 0, NULL) < 0) {
- error("sha1 mismatch %s", sha1_to_hex(repl));
+ if ((obj && obj->type == OBJ_BLOB && repo_has_object_file(r, oid)) ||
+ (!obj && repo_has_object_file(r, oid) &&
+ oid_object_info(r, oid, NULL) == OBJ_BLOB)) {
+ if (check_object_signature(r, repl, NULL, 0, NULL, NULL) < 0) {
+ error(_("hash mismatch %s"), oid_to_hex(oid));
return NULL;
}
- parse_blob_buffer(lookup_blob(sha1), NULL, 0);
- return lookup_object(sha1);
+ parse_blob_buffer(lookup_blob(r, oid), NULL, 0);
+ return lookup_object(r, oid);
}
- buffer = read_sha1_file(sha1, &type, &size);
+ buffer = repo_read_object_file(r, oid, &type, &size);
if (buffer) {
- if (check_sha1_signature(repl, buffer, size, typename(type)) < 0) {
+ if (check_object_signature(r, repl, buffer, size,
+ type_name(type), NULL) < 0) {
free(buffer);
- error("sha1 mismatch %s", sha1_to_hex(repl));
+ error(_("hash mismatch %s"), oid_to_hex(repl));
return NULL;
}
- obj = parse_object_buffer(sha1, type, size, buffer, &eaten);
+ obj = parse_object_buffer(r, oid, type, size,
+ buffer, &eaten);
if (!eaten)
free(buffer);
return obj;
@@ -255,45 +325,139 @@ int object_list_contains(struct object_list *list, struct object *obj)
return 0;
}
-void add_object_array(struct object *obj, const char *name, struct object_array *array)
+void object_list_free(struct object_list **list)
{
- add_object_array_with_mode(obj, name, array, S_IFINVALID);
+ while (*list) {
+ struct object_list *p = *list;
+ *list = p->next;
+ free(p);
+ }
}
-void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode)
+/*
+ * A zero-length string to which object_array_entry::name can be
+ * initialized without requiring a malloc/free.
+ */
+static char object_array_slopbuf[1];
+
+void add_object_array_with_path(struct object *obj, const char *name,
+ struct object_array *array,
+ unsigned mode, const char *path)
{
unsigned nr = array->nr;
unsigned alloc = array->alloc;
struct object_array_entry *objects = array->objects;
+ struct object_array_entry *entry;
if (nr >= alloc) {
alloc = (alloc + 32) * 2;
- objects = xrealloc(objects, alloc * sizeof(*objects));
+ REALLOC_ARRAY(objects, alloc);
array->alloc = alloc;
array->objects = objects;
}
- objects[nr].item = obj;
- objects[nr].name = name;
- objects[nr].mode = mode;
+ entry = &objects[nr];
+ entry->item = obj;
+ if (!name)
+ entry->name = NULL;
+ else if (!*name)
+ /* Use our own empty string instead of allocating one: */
+ entry->name = object_array_slopbuf;
+ else
+ entry->name = xstrdup(name);
+ entry->mode = mode;
+ if (path)
+ entry->path = xstrdup(path);
+ else
+ entry->path = NULL;
array->nr = ++nr;
}
-void object_array_remove_duplicates(struct object_array *array)
+void add_object_array(struct object *obj, const char *name, struct object_array *array)
+{
+ add_object_array_with_path(obj, name, array, S_IFINVALID, NULL);
+}
+
+/*
+ * Free all memory associated with an entry; the result is
+ * in an unspecified state and should not be examined.
+ */
+static void object_array_release_entry(struct object_array_entry *ent)
+{
+ if (ent->name != object_array_slopbuf)
+ free(ent->name);
+ free(ent->path);
+}
+
+struct object *object_array_pop(struct object_array *array)
+{
+ struct object *ret;
+
+ if (!array->nr)
+ return NULL;
+
+ ret = array->objects[array->nr - 1].item;
+ object_array_release_entry(&array->objects[array->nr - 1]);
+ array->nr--;
+ return ret;
+}
+
+void object_array_filter(struct object_array *array,
+ object_array_each_func_t want, void *cb_data)
{
- unsigned int ref, src, dst;
+ unsigned nr = array->nr, src, dst;
struct object_array_entry *objects = array->objects;
- for (ref = 0; ref + 1 < array->nr; ref++) {
- for (src = ref + 1, dst = src;
- src < array->nr;
- src++) {
- if (!strcmp(objects[ref].name, objects[src].name))
- continue;
+ for (src = dst = 0; src < nr; src++) {
+ if (want(&objects[src], cb_data)) {
if (src != dst)
objects[dst] = objects[src];
dst++;
+ } else {
+ object_array_release_entry(&objects[src]);
+ }
+ }
+ array->nr = dst;
+}
+
+void object_array_clear(struct object_array *array)
+{
+ int i;
+ for (i = 0; i < array->nr; i++)
+ object_array_release_entry(&array->objects[i]);
+ FREE_AND_NULL(array->objects);
+ array->nr = array->alloc = 0;
+}
+
+/*
+ * Return true if array already contains an entry.
+ */
+static int contains_object(struct object_array *array,
+ const struct object *item, const char *name)
+{
+ unsigned nr = array->nr, i;
+ struct object_array_entry *object = array->objects;
+
+ for (i = 0; i < nr; i++, object++)
+ if (item == object->item && !strcmp(object->name, name))
+ return 1;
+ return 0;
+}
+
+void object_array_remove_duplicates(struct object_array *array)
+{
+ unsigned nr = array->nr, src;
+ struct object_array_entry *objects = array->objects;
+
+ array->nr = 0;
+ for (src = 0; src < nr; src++) {
+ if (!contains_object(array, objects[src].item,
+ objects[src].name)) {
+ if (src != array->nr)
+ objects[array->nr] = objects[src];
+ array->nr++;
+ } else {
+ object_array_release_entry(&objects[src]);
}
- array->nr = dst;
}
}
@@ -301,9 +465,138 @@ void clear_object_flags(unsigned flags)
{
int i;
- for (i=0; i < obj_hash_size; i++) {
- struct object *obj = obj_hash[i];
+ for (i=0; i < the_repository->parsed_objects->obj_hash_size; i++) {
+ struct object *obj = the_repository->parsed_objects->obj_hash[i];
if (obj)
obj->flags &= ~flags;
}
}
+
+void repo_clear_commit_marks(struct repository *r, unsigned int flags)
+{
+ int i;
+
+ for (i = 0; i < r->parsed_objects->obj_hash_size; i++) {
+ struct object *obj = r->parsed_objects->obj_hash[i];
+ if (obj && obj->type == OBJ_COMMIT)
+ obj->flags &= ~flags;
+ }
+}
+
+struct parsed_object_pool *parsed_object_pool_new(void)
+{
+ struct parsed_object_pool *o = xmalloc(sizeof(*o));
+ memset(o, 0, sizeof(*o));
+
+ o->blob_state = allocate_alloc_state();
+ o->tree_state = allocate_alloc_state();
+ o->commit_state = allocate_alloc_state();
+ o->tag_state = allocate_alloc_state();
+ o->object_state = allocate_alloc_state();
+
+ o->is_shallow = -1;
+ CALLOC_ARRAY(o->shallow_stat, 1);
+
+ o->buffer_slab = allocate_commit_buffer_slab();
+
+ return o;
+}
+
+struct raw_object_store *raw_object_store_new(void)
+{
+ struct raw_object_store *o = xmalloc(sizeof(*o));
+
+ memset(o, 0, sizeof(*o));
+ INIT_LIST_HEAD(&o->packed_git_mru);
+ hashmap_init(&o->pack_map, pack_map_entry_cmp, NULL, 0);
+ pthread_mutex_init(&o->replace_mutex, NULL);
+ return o;
+}
+
+static void free_object_directory(struct object_directory *odb)
+{
+ free(odb->path);
+ odb_clear_loose_cache(odb);
+ free(odb);
+}
+
+static void free_object_directories(struct raw_object_store *o)
+{
+ while (o->odb) {
+ struct object_directory *next;
+
+ next = o->odb->next;
+ free_object_directory(o->odb);
+ o->odb = next;
+ }
+ kh_destroy_odb_path_map(o->odb_by_path);
+ o->odb_by_path = NULL;
+}
+
+void raw_object_store_clear(struct raw_object_store *o)
+{
+ FREE_AND_NULL(o->alternate_db);
+
+ oidmap_free(o->replace_map, 1);
+ FREE_AND_NULL(o->replace_map);
+ pthread_mutex_destroy(&o->replace_mutex);
+
+ free_commit_graph(o->commit_graph);
+ o->commit_graph = NULL;
+ o->commit_graph_attempted = 0;
+
+ free_object_directories(o);
+ o->odb_tail = NULL;
+ o->loaded_alternates = 0;
+
+ INIT_LIST_HEAD(&o->packed_git_mru);
+ close_object_store(o);
+ o->packed_git = NULL;
+
+ hashmap_clear(&o->pack_map);
+}
+
+void parsed_object_pool_clear(struct parsed_object_pool *o)
+{
+ /*
+ * As objects are allocated in slabs (see alloc.c), we do
+ * not need to free each object, but each slab instead.
+ *
+ * Before doing so, we need to free any additional memory
+ * the objects may hold.
+ */
+ unsigned i;
+
+ for (i = 0; i < o->obj_hash_size; i++) {
+ struct object *obj = o->obj_hash[i];
+
+ if (!obj)
+ continue;
+
+ if (obj->type == OBJ_TREE)
+ free_tree_buffer((struct tree*)obj);
+ else if (obj->type == OBJ_COMMIT)
+ release_commit_memory(o, (struct commit*)obj);
+ else if (obj->type == OBJ_TAG)
+ release_tag_memory((struct tag*)obj);
+ }
+
+ FREE_AND_NULL(o->obj_hash);
+ o->obj_hash_size = 0;
+
+ free_commit_buffer_slab(o->buffer_slab);
+ o->buffer_slab = NULL;
+
+ clear_alloc_state(o->blob_state);
+ clear_alloc_state(o->tree_state);
+ clear_alloc_state(o->commit_state);
+ clear_alloc_state(o->tag_state);
+ clear_alloc_state(o->object_state);
+ stat_validity_clear(o->shallow_stat);
+ FREE_AND_NULL(o->blob_state);
+ FREE_AND_NULL(o->tree_state);
+ FREE_AND_NULL(o->commit_state);
+ FREE_AND_NULL(o->tag_state);
+ FREE_AND_NULL(o->object_state);
+ FREE_AND_NULL(o->shallow_stat);
+}