Commit d45b4a9a1bcbb157a4f02cf5ed23fde5222db9c8

Vicent Marti 2010-09-20T21:39:11

Add support for in-memory objects All repository objects can now be created from scratch in memory using either the git_object_new() method, or the corresponding git_XXX_new() for each object. So far, only git_commits can be written back to disk once created in memory. Signed-off-by: Vicent Marti <tanoku@gmail.com>

diff --git a/src/commit.c b/src/commit.c
index 7cb8f24..90a30e0 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -63,9 +63,14 @@ void git_commit__free(git_commit *commit)
 	free(commit);
 }
 
+git_commit *git_commit_new(git_repository *repo)
+{
+	return (git_commit *)git_object_new(repo, GIT_OBJ_COMMIT);
+}
+
 const git_oid *git_commit_id(git_commit *c)
 {
-	return &c->object.id;
+	return git_object_id((git_object *)c);
 }
 
 int git_commit__parse(git_commit *commit)
diff --git a/src/git/commit.h b/src/git/commit.h
index 4387925..a584273 100644
--- a/src/git/commit.h
+++ b/src/git/commit.h
@@ -30,6 +30,18 @@ typedef struct git_commit git_commit;
  */
 GIT_EXTERN(git_commit *) git_commit_lookup(git_repository *repo, const git_oid *id);
 
+/*
+ * Create a new in-memory git_commit.
+ *
+ * The commit object must be manually filled using
+ * setter methods before it can be written to its
+ * repository.
+ *
+ * @param repo The repository where the object will reside
+ * @return the object if creation was posible; NULL otherwise
+ */
+GIT_EXTERN(git_commit *) git_commit_new(git_repository *repo);
+
 /**
  * Get the id of a commit.
  * @param commit a previously loaded commit.
diff --git a/src/git/repository.h b/src/git/repository.h
index 33bb2fc..9417d7b 100644
--- a/src/git/repository.h
+++ b/src/git/repository.h
@@ -57,9 +57,53 @@ GIT_EXTERN(git_object *) git_repository_lookup(git_repository *repo, const git_o
  */
 GIT_EXTERN(git_odb *) git_repository_database(git_repository *repo);
 
+/*
+ * Create a new in-memory repository object with
+ * the given type.
+ *
+ * The object's attributes can be filled in using the
+ * correspondign setter methods.
+ *
+ * The object will be written back to given git_repository
+ * when the git_object_write() function is called; objects
+ * cannot be written to disk until all their main
+ * attributes have been properly filled.
+ *
+ * Objects are instantiated with no SHA1 id; their id
+ * will be automatically generated when writing to the
+ * repository.
+ *
+ * @parem repo Repository where the object belongs
+ * @param type Type of the object to be created
+ * @return the new object
+ */
+GIT_EXTERN(git_object *) git_object_new(git_repository *repo, git_otype type);
+
+/*
+ * Write back an object to disk.
+ *
+ * The object will be written to its corresponding
+ * repository.
+ *
+ * If the object has no changes since it was first
+ * read from the repository, no actions will take place.
+ *
+ * If the object has been modified since it was read from
+ * the repository, or it has been created from scratch
+ * in memory, it will be written to the repository and
+ * its SHA1 ID will be updated accordingly.
+ *
+ * @param object Git object to write back
+ * @return 0 on success; otherwise an error code
+ */
+int git_object_write(git_object *object);
+
 /**
  * Get the id (SHA1) of a repository object
  *
+ * In-memory objects created by git_object_new() do not
+ * have a SHA1 ID until they are written on a repository.
+ *
  * @param obj the repository object
  * @return the SHA1 id
  */
diff --git a/src/git/tag.h b/src/git/tag.h
index a6efabb..33e3d6f 100644
--- a/src/git/tag.h
+++ b/src/git/tag.h
@@ -29,6 +29,18 @@ typedef struct git_tag git_tag;
  */
 GIT_EXTERN(git_tag *) git_tag_lookup(git_repository *repo, const git_oid *id);
 
+/*
+ * Create a new in-memory git_tag.
+ *
+ * The tag object must be manually filled using
+ * setter methods before it can be written to its
+ * repository.
+ *
+ * @param repo The repository where the object will reside
+ * @return the object if creation was posible; NULL otherwise
+ */
+GIT_EXTERN(git_tag *) git_tag_new(git_repository *repo);
+
 /**
  * Get the id of a tag.
  * @param tag a previously loaded tag.
diff --git a/src/git/tree.h b/src/git/tree.h
index 646c085..32fd652 100644
--- a/src/git/tree.h
+++ b/src/git/tree.h
@@ -32,6 +32,18 @@ typedef struct git_tree git_tree;
  */
 GIT_EXTERN(git_tree *) git_tree_lookup(git_repository *repo, const git_oid *id);
 
+/*
+ * Create a new in-memory git_tree.
+ *
+ * The tree object must be manually filled using
+ * setter methods before it can be written to its
+ * repository.
+ *
+ * @param repo The repository where the object will reside
+ * @return the object if creation was posible; NULL otherwise
+ */
+GIT_EXTERN(git_tree *) git_tree_new(git_repository *repo);
+
 /**
  * Get the id of a tree.
  * @param tree a previously loaded tree.
diff --git a/src/repository.c b/src/repository.c
index f353ce0..dc1fe0e 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -32,6 +32,15 @@
 static const int default_table_size = 32;
 static const double max_load_factor = 0.65;
 
+static const size_t object_sizes[] = {
+	0,
+	sizeof(git_commit),
+	sizeof(git_tree),
+	sizeof(git_object), /* TODO: sizeof(git_blob) */ 
+	sizeof(git_tag)
+};
+
+
 uint32_t git_object_hash(const void *key)
 {
 	uint32_t r;
@@ -160,7 +169,7 @@ int git__source_write(git_odb_source *source, const void *bytes, size_t len)
 	return GIT_SUCCESS;
 }
 
-void git_object__source_prepare_write(git_object *object)
+static void prepare_write(git_object *object)
 {
 	const size_t base_size = 4096; /* 4Kb base size */
 
@@ -175,10 +184,9 @@ void git_object__source_prepare_write(git_object *object)
 	object->source.written_bytes = 0;
 
 	object->source.open = 1;
-	object->source.out_of_sync = 1;
 }
 
-int git_object__source_writeback(git_object *object)
+static int write_back(git_object *object)
 {
 	int error;
 	git_oid new_id;
@@ -188,8 +196,7 @@ int git_object__source_writeback(git_object *object)
 	if (!object->source.open)
 		return GIT_ERROR;
 
-	if (!object->source.out_of_sync)
-		return GIT_SUCCESS;
+	assert(object->modified);
 	
 	object->source.raw.len = object->source.written_bytes;
 
@@ -198,7 +205,9 @@ int git_object__source_writeback(git_object *object)
 	if ((error = git_odb_write(&new_id, object->repo->db, &object->source.raw)) < 0)
 		return error;
 
-	git_hashtable_remove(object->repo->objects, &object->id);
+	if (!object->in_memory)
+		git_hashtable_remove(object->repo->objects, &object->id);
+
 	git_oid_cpy(&object->id, &new_id);
 	git_hashtable_insert(object->repo->objects, &object->id, object);
 
@@ -206,6 +215,7 @@ int git_object__source_writeback(git_object *object)
 	object->source.written_bytes = 0;
 
 	object->modified = 0;
+	object->in_memory = 0;
 
 	git_object__source_close(object);
 	return GIT_SUCCESS;
@@ -215,9 +225,9 @@ int git_object__source_open(git_object *object)
 {
 	int error;
 
-	assert(object);
+	assert(object && !object->in_memory);
 
-	if (object->source.open && object->source.out_of_sync)
+	if (object->source.open)
 		git_object__source_close(object);
 
 	if (object->source.open)
@@ -228,7 +238,6 @@ int git_object__source_open(git_object *object)
 		return error;
 
 	object->source.open = 1;
-	object->source.out_of_sync = 0;
 	return GIT_SUCCESS;
 }
 
@@ -239,7 +248,6 @@ void git_object__source_close(git_object *object)
 	if (!object->source.open) {
 		git_obj_close(&object->source.raw);
 		object->source.open = 0;
-		object->source.out_of_sync = 0;
 	}
 }
 
@@ -253,7 +261,7 @@ int git_object_write(git_object *object)
 	if (object->modified == 0)
 		return GIT_SUCCESS;
 
-	git_object__source_prepare_write(object);
+	prepare_write(object);
 	source = &object->source;
 
 	switch (source->raw.type) {
@@ -265,14 +273,15 @@ int git_object_write(git_object *object)
 	case GIT_OBJ_TAG:
 	default:
 		error = GIT_ERROR;
+		break;
 	}
 
 	if (error < 0) {
 		git_object__source_close(object);
 		return error;
 	}
-	
-	return git_object__source_writeback(object);
+
+	return write_back(object);
 }
 
 void git_object_free(git_object *object)
@@ -310,6 +319,10 @@ git_odb *git_repository_database(git_repository *repo)
 const git_oid *git_object_id(git_object *obj)
 {
 	assert(obj);
+
+	if (obj->in_memory)
+		return NULL;
+
 	return &obj->id;
 }
 
@@ -319,16 +332,38 @@ git_otype git_object_type(git_object *obj)
 	return obj->source.raw.type;
 }
 
-git_object *git_repository_lookup(git_repository *repo, const git_oid *id, git_otype type)
+git_object *git_object_new(git_repository *repo, git_otype type)
 {
-	static const size_t object_sizes[] = {
-		0,
-		sizeof(git_commit),
-		sizeof(git_tree),
-		sizeof(git_object), /* TODO: sizeof(git_blob) */ 
-		sizeof(git_tag)
-	};
+	git_object *object = NULL;
 
+	switch (type) {
+	case GIT_OBJ_COMMIT:
+	case GIT_OBJ_TAG:
+	case GIT_OBJ_TREE:
+	case GIT_OBJ_BLOB:
+		break;
+
+	default:
+		return NULL;
+	}
+
+	object = git__malloc(object_sizes[type]);
+
+	if (object == NULL)
+		return NULL;
+
+	memset(object, 0x0, object_sizes[type]);
+	object->repo = repo;
+	object->in_memory = 1;
+	object->modified = 1;
+
+	object->source.raw.type = type;
+
+	return object;
+}
+
+git_object *git_repository_lookup(git_repository *repo, const git_oid *id, git_otype type)
+{
 	git_object *object = NULL;
 	git_rawobj obj_file;
 
diff --git a/src/repository.h b/src/repository.h
index 5074c61..9a7bd24 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -12,7 +12,7 @@ typedef struct {
 	git_rawobj raw;
 	void *write_ptr;
 	size_t written_bytes;
-	int open:1, out_of_sync:1;
+	int open:1;
 } git_odb_source;
 
 struct git_object {
@@ -30,8 +30,6 @@ struct git_repository {
 
 int git_object__source_open(git_object *object);
 void git_object__source_close(git_object *object);
-void git_object__source_prepare_write(git_object *object);
-int git_object__source_writeback(git_object *object);
 
 int git__source_printf(git_odb_source *source, const char *format, ...);
 int git__source_write(git_odb_source *source, const void *bytes, size_t len);
diff --git a/src/tag.c b/src/tag.c
index 13679ec..734556b 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -38,9 +38,14 @@ void git_tag__free(git_tag *tag)
 	free(tag);
 }
 
-const git_oid *git_tag_id(git_tag *t)
+git_tag *git_tag_new(git_repository *repo)
 {
-	return &t->object.id;
+	return (git_tag *)git_object_new(repo, GIT_OBJ_TAG);
+}
+
+const git_oid *git_tag_id(git_tag *c)
+{
+	return git_object_id((git_object *)c);
 }
 
 const git_object *git_tag_target(git_tag *t)
diff --git a/src/tree.c b/src/tree.c
index f1c191c..670f998 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -40,9 +40,14 @@ void git_tree__free(git_tree *tree)
 	free(tree);
 }
 
-const git_oid *git_tree_id(git_tree *tree)
+git_tree *git_tree_new(git_repository *repo)
 {
-	return &tree->object.id;
+	return (git_tree *)git_object_new(repo, GIT_OBJ_TREE);
+}
+
+const git_oid *git_tree_id(git_tree *c)
+{
+	return git_object_id((git_object *)c);
 }
 
 git_tree *git_tree_lookup(git_repository *repo, const git_oid *id)