Commit 584f49a5ceff463581f7f1b8bc23880dabca27ac

Vicent Marti 2011-03-01T01:37:28

Fix several issues with refcounting - Added several missing reference increases - Add new destructor to the repository that does not GC the objects Signed-off-by: Vicent Marti <tanoku@gmail.com>

diff --git a/src/commit.c b/src/commit.c
index 3edc573..dff3f1e 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -44,6 +44,13 @@
 
 static void clear_parents(git_commit *commit)
 {
+	unsigned int i;
+
+	for (i = 0; i < commit->parents.length; ++i) {
+		git_commit *parent = git_vector_get(&commit->parents, i);
+		git_object_close((git_object *)parent);
+	}
+
 	git_vector_clear(&commit->parents);
 }
 
@@ -242,12 +249,8 @@ const git_tree *git_commit_tree(git_commit *commit)
 	if (!commit->object.in_memory && commit->tree == NULL)
 		git_commit__parse_full(commit);
 
-	if (commit->tree) {
-		GIT_OBJECT_INCREF(commit->tree);
-		return commit->tree;
-	}
-
-	return NULL;
+	GIT_OBJECT_INCREF(commit->tree);
+	return commit->tree;
 }
 
 GIT_COMMIT_GETTER(git_signature *, author)
@@ -273,10 +276,15 @@ unsigned int git_commit_parentcount(git_commit *commit)
 	return commit->parents.length;
 }
 
-git_commit * git_commit_parent(git_commit *commit, unsigned int n)
+git_commit *git_commit_parent(git_commit *commit, unsigned int n)
 {
+	git_commit *parent;
+
 	assert(commit);
-	return git_vector_get(&commit->parents, n);
+
+	parent = git_vector_get(&commit->parents, n);
+	GIT_OBJECT_INCREF(parent);
+	return parent;
 }
 
 void git_commit_set_tree(git_commit *commit, git_tree *tree)
@@ -341,7 +349,10 @@ void git_commit_set_message(git_commit *commit, const char *message)
 
 int git_commit_add_parent(git_commit *commit, git_commit *new_parent)
 {
+	assert(commit && new_parent);
+
 	CHECK_FULL_PARSE();
 	commit->object.modified = 1;
+	GIT_OBJECT_INCREF(new_parent);
 	return git_vector_insert(&commit->parents, new_parent);
 }
diff --git a/src/git2/repository.h b/src/git2/repository.h
index dbb9ddd..5327f8c 100644
--- a/src/git2/repository.h
+++ b/src/git2/repository.h
@@ -158,6 +158,9 @@ GIT_EXTERN(int) git_repository_index(git_index **index, git_repository *repo);
  */
 GIT_EXTERN(void) git_repository_free(git_repository *repo);
 
+
+GIT_EXTERN(void) git_repository_free__no_gc(git_repository *repo);
+
 /**
  * Creates a new Git repository in the given folder.
  *
diff --git a/src/object.c b/src/object.c
index e232714..de02ef5 100644
--- a/src/object.c
+++ b/src/object.c
@@ -277,6 +277,7 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o
 	object = git_hashtable_lookup(repo->objects, id);
 	if (object != NULL) {
 		*object_out = object;
+		GIT_OBJECT_INCREF(object);
 		return GIT_SUCCESS;
 	}
 
@@ -329,7 +330,7 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o
 	git_object__source_close(object);
 	git_hashtable_insert(repo->objects, &object->id, object);
 
-	object->refcount++;
+	GIT_OBJECT_INCREF(object);
 	*object_out = object;
 	return GIT_SUCCESS;
 }
@@ -383,11 +384,13 @@ void git_object__free(git_object *object)
 
 	git_object__source_close(object);
 
-	if (object->in_memory) {
-		int idx = git_vector_search(&object->repo->memory_objects, object);
-		git_vector_remove(&object->repo->memory_objects, idx);
-	} else {
-		git_hashtable_remove(object->repo->objects, &object->id);
+	if (object->repo != NULL) {
+		if (object->in_memory) {
+			int idx = git_vector_search(&object->repo->memory_objects, object);
+			git_vector_remove(&object->repo->memory_objects, idx);
+		} else {
+			git_hashtable_remove(object->repo->objects, &object->id);
+		}
 	}
 
 	switch (object->source.raw.type) {
diff --git a/src/repository.c b/src/repository.c
index 9f27a38..d3852d3 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -332,7 +332,30 @@ int git_repository_open(git_repository **repo_out, const char *path)
 	return repository_open_internal(repo_out, path, 0);
 }
 
-void git_repository_free(git_repository *repo)
+static void repository_free(git_repository *repo)
+{
+	assert(repo);
+
+	free(repo->path_workdir);
+	free(repo->path_index);
+	free(repo->path_repository);
+	free(repo->path_odb);
+
+	git_hashtable_free(repo->objects);
+	git_vector_free(&repo->memory_objects);
+
+	git_repository__refcache_free(&repo->references);
+
+	if (repo->db != NULL)
+		git_odb_close(repo->db);
+
+	if (repo->index != NULL)
+		git_index_free(repo->index);
+
+	free(repo);
+}
+
+void git_repository_free__no_gc(git_repository *repo)
 {
 	git_object *object;
 	const void *_unused;
@@ -341,10 +364,28 @@ void git_repository_free(git_repository *repo)
 	if (repo == NULL)
 		return;
 
-	free(repo->path_workdir);
-	free(repo->path_index);
-	free(repo->path_repository);
-	free(repo->path_odb);
+	GIT_HASHTABLE_FOREACH(repo->objects, _unused, object,
+		object->repo = NULL;
+		object->refcount = 0;
+	);
+
+	for (i = 0; i < repo->memory_objects.length; ++i) {
+		object = git_vector_get(&repo->memory_objects, i);
+		object->repo = NULL;
+		object->refcount = 0;
+	}
+
+	repository_free(repo);
+}
+
+void git_repository_free(git_repository *repo)
+{
+	git_object *object;
+	const void *_unused;
+	unsigned int i;
+
+	if (repo == NULL)
+		return;
 
 	/* Increment the refcount of all the objects in the repository
 	 * to prevent freeing dependencies */
@@ -362,18 +403,7 @@ void git_repository_free(git_repository *repo)
 		git_object__free(object);
 	}
 
-	git_hashtable_free(repo->objects);
-	git_vector_free(&repo->memory_objects);
-
-	git_repository__refcache_free(&repo->references);
-
-	if (repo->db != NULL)
-		git_odb_close(repo->db);
-
-	if (repo->index != NULL)
-		git_index_free(repo->index);
-
-	free(repo);
+	repository_free(repo);
 }
 
 int git_repository_index(git_index **index_out, git_repository *repo)
diff --git a/src/repository.h b/src/repository.h
index 2e8d187..4af062e 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -15,8 +15,6 @@
 #define GIT_OBJECTS_DIR "objects/"
 #define GIT_INDEX_FILE "index"
 
-#define GIT_OBJECT_INCREF(ob) ++(((git_object *)(ob))->refcount)
-
 typedef struct {
 	git_rawobj raw;
 	void *write_ptr;
@@ -62,4 +60,12 @@ int git__source_write(git_odb_source *source, const void *bytes, size_t len);
 int git__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header);
 int git__write_oid(git_odb_source *src, const char *header, const git_oid *oid);
 
+#define GIT_OBJECT_INCREF(ob) git_object__incref((git_object *)(ob))
+
+GIT_INLINE(void) git_object__incref(struct git_object *object)
+{
+	if (object)
+		object->refcount++;
+}
+
 #endif