diff options
author | Vicent Marti <tanoku@gmail.com> | 2011-11-25 08:16:26 +0100 |
---|---|---|
committer | Vicent Marti <tanoku@gmail.com> | 2011-11-26 08:37:08 +0100 |
commit | 9462c471435b4de74848408bebe41d770dc49a50 (patch) | |
tree | aac5e696d1b3e7b4cba64082b28255e5c4593b66 /src | |
parent | 880b6f0c22153db164ecb3a18c362ba8337365d3 (diff) | |
download | libgit2-9462c471435b4de74848408bebe41d770dc49a50.tar.gz |
repository: Change ownership semantics
The ownership semantics have been changed all over the library to be
consistent. There are no more "borrowed" or duplicated references.
Main changes:
- `git_repository_open2` and `3` have been dropped.
- Added setters and getters to hotswap all the repository owned
objects:
`git_repository_index`
`git_repository_set_index`
`git_repository_odb`
`git_repository_set_odb`
`git_repository_config`
`git_repository_set_config`
`git_repository_workdir`
`git_repository_set_workdir`
Now working directories/index files/ODBs and so on can be
hot-swapped after creating a repository and between operations.
- All these objects now have proper ownership semantics with
refcounting: they all require freeing after they are no longer
needed (the repository always keeps its internal reference).
- Repository open and initialization has been updated to keep in
mind the configuration files. Bare repositories are now always
detected, and a default config file is created on init.
- All the tests affected by these changes have been dropped from the
old test suite and ported to the new one.
Diffstat (limited to 'src')
-rw-r--r-- | src/blob.c | 23 | ||||
-rw-r--r-- | src/commit.c | 7 | ||||
-rw-r--r-- | src/common.h | 1 | ||||
-rw-r--r-- | src/config.c | 15 | ||||
-rw-r--r-- | src/config.h | 2 | ||||
-rw-r--r-- | src/config_file.c | 3 | ||||
-rw-r--r-- | src/fetch.c | 10 | ||||
-rw-r--r-- | src/index.c | 53 | ||||
-rw-r--r-- | src/index.h | 3 | ||||
-rw-r--r-- | src/object.c | 23 | ||||
-rw-r--r-- | src/odb.c | 16 | ||||
-rw-r--r-- | src/odb.h | 2 | ||||
-rw-r--r-- | src/refs.c | 19 | ||||
-rw-r--r-- | src/remote.c | 18 | ||||
-rw-r--r-- | src/repository.c | 597 | ||||
-rw-r--r-- | src/repository.h | 12 | ||||
-rw-r--r-- | src/revwalk.c | 11 | ||||
-rw-r--r-- | src/status.c | 62 | ||||
-rw-r--r-- | src/tag.c | 18 | ||||
-rw-r--r-- | src/tree.c | 32 | ||||
-rw-r--r-- | src/util.h | 24 |
21 files changed, 571 insertions, 380 deletions
diff --git a/src/blob.c b/src/blob.c index f13a5be15..5fd0fd67e 100644 --- a/src/blob.c +++ b/src/blob.c @@ -41,9 +41,14 @@ int git_blob__parse(git_blob *blob, git_odb_object *odb_obj) int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len) { int error; + git_odb *odb; git_odb_stream *stream; - if ((error = git_odb_open_wstream(&stream, repo->db, len, GIT_OBJ_BLOB)) < GIT_SUCCESS) + error = git_repository_odb__weakptr(&odb, repo); + if (error < GIT_SUCCESS) + return error; + + if ((error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < GIT_SUCCESS) return git__rethrow(error, "Failed to create blob"); if ((error = stream->write(stream, buffer, len)) < GIT_SUCCESS) { @@ -69,11 +74,14 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat git_off_t size; git_odb_stream *stream; struct stat st; + const char *workdir; + git_odb *odb; - if (repo->path_workdir == NULL) + workdir = git_repository_workdir(repo); + if (workdir == NULL) return git__throw(GIT_ENOTFOUND, "Failed to create blob. (No working directory found)"); - git_path_join(full_path, repo->path_workdir, path); + git_path_join(full_path, workdir, path); error = p_lstat(full_path, &st); if (error < 0) { @@ -83,11 +91,16 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat islnk = S_ISLNK(st.st_mode); size = st.st_size; - if (!islnk) + error = git_repository_odb__weakptr(&odb, repo); + if (error < GIT_SUCCESS) + return error; + + if (!islnk) { if ((fd = p_open(full_path, O_RDONLY)) < 0) return git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path); + } - if ((error = git_odb_open_wstream(&stream, repo->db, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS) { + if ((error = git_odb_open_wstream(&stream, odb, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS) { if (!islnk) p_close(fd); return git__rethrow(error, "Failed to create blob"); diff --git a/src/commit.c b/src/commit.c index 83bc9fc4c..bf6ca7855 100644 --- a/src/commit.c +++ b/src/commit.c @@ -103,6 +103,7 @@ int git_commit_create( { git_buf commit = GIT_BUF_INIT; int error, i; + git_odb *odb; if (git_object_owner((const git_object *)tree) != repo) return git__throw(GIT_EINVALIDARGS, "The given tree does not belong to this repository"); @@ -132,7 +133,11 @@ int git_commit_create( goto cleanup; } - error = git_odb_write(oid, git_repository_database(repo), commit.ptr, commit.size, GIT_OBJ_COMMIT); + error = git_repository_odb__weakptr(&odb, repo); + if (error < GIT_SUCCESS) + goto cleanup; + + error = git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT); git_buf_free(&commit); if (error == GIT_SUCCESS && update_ref != NULL) { diff --git a/src/common.h b/src/common.h index 727a08e77..35316012d 100644 --- a/src/common.h +++ b/src/common.h @@ -60,4 +60,5 @@ extern void git___rethrow(const char *, ...) GIT_FORMAT_PRINTF(1, 2); #include "util.h" + #endif /* INCLUDE_common_h__ */ diff --git a/src/config.c b/src/config.c index 4e48ff7f4..a8e15405b 100644 --- a/src/config.c +++ b/src/config.c @@ -22,15 +22,12 @@ typedef struct { int priority; } file_internal; -void git_config_free(git_config *cfg) +static void config_free(git_config *cfg) { unsigned int i; git_config_file *file; file_internal *internal; - if (cfg == NULL) - return; - for(i = 0; i < cfg->files.length; ++i){ internal = git_vector_get(&cfg->files, i); file = internal->file; @@ -42,6 +39,14 @@ void git_config_free(git_config *cfg) git__free(cfg); } +void git_config_free(git_config *cfg) +{ + if (cfg == NULL) + return; + + GIT_REFCOUNT_DEC(cfg, config_free); +} + static int config_backend_cmp(const void *a, const void *b) { const file_internal *bk_a = (const file_internal *)(a); @@ -66,7 +71,7 @@ int git_config_new(git_config **out) } *out = cfg; - + GIT_REFCOUNT_INC(cfg); return GIT_SUCCESS; } diff --git a/src/config.h b/src/config.h index 43574a586..7f3494edc 100644 --- a/src/config.h +++ b/src/config.h @@ -17,8 +17,8 @@ #define GIT_CONFIG_FILE_MODE 0666 struct git_config { + git_refcount rc; git_vector files; - git_repository *repo; }; #endif diff --git a/src/config_file.c b/src/config_file.c index 5e862d487..7a5865210 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -256,6 +256,7 @@ static int config_open(git_config_file *cfg) diskfile_backend *b = (diskfile_backend *)cfg; error = git_futils_readbuffer(&b->reader.buffer, b->file_path); + /* It's fine if the file doesn't exist */ if (error == GIT_ENOTFOUND) return GIT_SUCCESS; @@ -269,7 +270,7 @@ static int config_open(git_config_file *cfg) git_futils_freebuffer(&b->reader.buffer); - return error; + return GIT_SUCCESS; cleanup: cvar_list_free(&b->var_list); diff --git a/src/fetch.c b/src/fetch.c index a42732925..93f0980ca 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -24,7 +24,7 @@ static int filter_wants(git_remote *remote) git_headarray refs; git_remote_head *head; git_transport *t = remote->transport; - git_repository *repo = remote->repo; + git_odb *odb = NULL; const git_refspec *spec; int error; unsigned int i = 0; @@ -39,6 +39,10 @@ static int filter_wants(git_remote *remote) goto cleanup; } + error = git_repository_odb__weakptr(&odb, remote->repo); + if (error < GIT_SUCCESS) + goto cleanup; + /* * The fetch refspec can be NULL, and what this means is that the * user didn't specify one. This is fine, as it means that we're @@ -53,7 +57,7 @@ static int filter_wants(git_remote *remote) */ head = refs.heads[0]; if (refs.len > 0 && !strcmp(head->name, GIT_HEAD_FILE)) { - if (git_odb_exists(repo->db, &head->oid)) + if (git_odb_exists(odb, &head->oid)) head->local = 1; else remote->need_pack = 1; @@ -77,7 +81,7 @@ static int filter_wants(git_remote *remote) } /* If we have the object, mark it so we don't ask for it */ - if (git_odb_exists(repo->db, &head->oid)) + if (git_odb_exists(odb, &head->oid)) head->local = 1; else remote->need_pack = 1; diff --git a/src/index.c b/src/index.c index aad117164..d01262b39 100644 --- a/src/index.c +++ b/src/index.c @@ -31,6 +31,8 @@ static const unsigned int INDEX_HEADER_SIG = 0x44495243; static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'}; static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'}; +#define INDEX_OWNER(idx) ((git_repository *)(GIT_REFCOUNT_OWNER(idx))) + struct index_header { uint32_t signature; uint32_t version; @@ -124,7 +126,7 @@ static unsigned int index_create_mode(unsigned int mode) return S_IFREG | ((mode & 0100) ? 0755 : 0644); } -static int index_initialize(git_index **index_out, git_repository *owner, const char *index_path) +int git_index_open(git_index **index_out, const char *index_path) { git_index *index; @@ -142,8 +144,6 @@ static int index_initialize(git_index **index_out, git_repository *owner, const return GIT_ENOMEM; } - index->repository = owner; - git_vector_init(&index->entries, 32, index_cmp); /* Check if index file is stored on disk already */ @@ -151,23 +151,18 @@ static int index_initialize(git_index **index_out, git_repository *owner, const index->on_disk = 1; *index_out = index; + GIT_REFCOUNT_INC(index); return git_index_read(index); } -int git_index_open(git_index **index_out, const char *index_path) +static void index_free(git_index *index) { - return index_initialize(index_out, NULL, index_path); -} - -/* - * Moved from `repository.c` - */ -int git_repository_index(git_index **index_out, git_repository *repo) -{ - if (repo->is_bare) - return git__throw(GIT_EBAREINDEX, "Failed to open index. Repository is bare"); + git_index_clear(index); + git_vector_free(&index->entries); + git_vector_free(&index->unmerged); - return index_initialize(index_out, repo, repo->path_index); + git__free(index->index_file_path); + git__free(index); } void git_index_free(git_index *index) @@ -175,12 +170,7 @@ void git_index_free(git_index *index) if (index == NULL) return; - git_index_clear(index); - git_vector_free(&index->entries); - git_vector_free(&index->unmerged); - - git__free(index->index_file_path); - git__free(index); + GIT_REFCOUNT_DEC(index, index_free); } void git_index_clear(git_index *index) @@ -298,20 +288,29 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const struct stat st; git_oid oid; int error; + const char *workdir; + + if (INDEX_OWNER(index) == NULL) + return git__throw(GIT_EBAREINDEX, + "Failed to initialize entry. Repository is bare"); - if (index->repository == NULL) - return git__throw(GIT_EBAREINDEX, "Failed to initialize entry. Repository is bare"); + workdir = git_repository_workdir(INDEX_OWNER(index)); + if (workdir == NULL) + return git__throw(GIT_EBAREINDEX, + "Failed to initialize entry. Cannot resolved workdir"); - git_path_join(full_path, index->repository->path_workdir, rel_path); + git_path_join(full_path, workdir, rel_path); if (p_lstat(full_path, &st) < 0) - return git__throw(GIT_ENOTFOUND, "Failed to initialize entry. '%s' cannot be opened", full_path); + return git__throw(GIT_ENOTFOUND, + "Failed to initialize entry. '%s' cannot be opened", full_path); if (stage < 0 || stage > 3) - return git__throw(GIT_ERROR, "Failed to initialize entry. Invalid stage %i", stage); + return git__throw(GIT_ERROR, + "Failed to initialize entry. Invalid stage %i", stage); /* write the blob to disk and get the oid */ - if ((error = git_blob_create_fromfile(&oid, index->repository, rel_path)) < GIT_SUCCESS) + if ((error = git_blob_create_fromfile(&oid, INDEX_OWNER(index), rel_path)) < GIT_SUCCESS) return git__rethrow(error, "Failed to initialize index entry"); entry = git__malloc(sizeof(git_index_entry)); diff --git a/src/index.h b/src/index.h index a1cd3403e..9464afb6c 100644 --- a/src/index.h +++ b/src/index.h @@ -18,7 +18,8 @@ #define GIT_INDEX_FILE_MODE 0666 struct git_index { - git_repository *repository; + git_refcount rc; + char *index_file_path; time_t last_modified; diff --git a/src/object.c b/src/object.c index c84e94b05..12947f035 100644 --- a/src/object.c +++ b/src/object.c @@ -77,9 +77,15 @@ static int create_object(git_object **object_out, git_otype type) return GIT_SUCCESS; } -int git_object_lookup_prefix(git_object **object_out, git_repository *repo, const git_oid *id, unsigned int len, git_otype type) +int git_object_lookup_prefix( + git_object **object_out, + git_repository *repo, + const git_oid *id, + unsigned int len, + git_otype type) { git_object *object = NULL; + git_odb *odb = NULL; git_odb_object *odb_obj; int error = GIT_SUCCESS; @@ -89,6 +95,10 @@ int git_object_lookup_prefix(git_object **object_out, git_repository *repo, cons return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to lookup object. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN); + error = git_repository_odb__weakptr(&odb, repo); + if (error < GIT_SUCCESS) + return error; + if (len > GIT_OID_HEXSZ) len = GIT_OID_HEXSZ; @@ -98,10 +108,11 @@ int git_object_lookup_prefix(git_object **object_out, git_repository *repo, cons */ object = git_cache_get(&repo->objects, id); if (object != NULL) { - if (type != GIT_OBJ_ANY && type != object->type) - { + if (type != GIT_OBJ_ANY && type != object->type) { git_object_close(object); - return git__throw(GIT_EINVALIDTYPE, "Failed to lookup object. The given type does not match the type on the ODB"); + return git__throw(GIT_EINVALIDTYPE, + "Failed to lookup object. " + "The given type does not match the type on the ODB"); } *object_out = object; @@ -113,7 +124,7 @@ int git_object_lookup_prefix(git_object **object_out, git_repository *repo, cons * 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); + error = git_odb_read(&odb_obj, odb, id); } else { git_oid short_oid; @@ -133,7 +144,7 @@ int git_object_lookup_prefix(git_object **object_out, git_repository *repo, cons * - We never explore the cache, go right to exploring the backends * We chose the latter : we explore directly the backends. */ - error = git_odb_read_prefix(&odb_obj, repo->db, &short_oid, len); + error = git_odb_read_prefix(&odb_obj, odb, &short_oid, len); } if (error < GIT_SUCCESS) @@ -275,6 +275,7 @@ int git_odb_new(git_odb **out) } *out = db; + GIT_REFCOUNT_INC(db); return GIT_SUCCESS; } @@ -405,17 +406,14 @@ int git_odb_open(git_odb **out, const char *objects_dir) return GIT_SUCCESS; cleanup: - git_odb_close(db); + git_odb_free(db); return error; /* error already set - pass through */ } -void git_odb_close(git_odb *db) +static void odb_free(git_odb *db) { unsigned int i; - if (db == NULL) - return; - for (i = 0; i < db->backends.length; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *backend = internal->backend; @@ -431,6 +429,14 @@ void git_odb_close(git_odb *db) git__free(db); } +void git_odb_free(git_odb *db) +{ + if (db == NULL) + return; + + GIT_REFCOUNT_DEC(db, odb_free); +} + int git_odb_exists(git_odb *db, const git_oid *id) { git_odb_object *object; @@ -33,7 +33,7 @@ struct git_odb_object { /* EXPORT */ struct git_odb { - void *_internal; + git_refcount rc; git_vector backends; git_cache cache; }; diff --git a/src/refs.c b/src/refs.c index 2374cc72f..4c45fec2c 100644 --- a/src/refs.c +++ b/src/refs.c @@ -1195,7 +1195,8 @@ cleanup: */ int git_reference_set_oid(git_reference *ref, const git_oid *id) { - int error = GIT_SUCCESS; + int error = GIT_SUCCESS, exists; + git_odb *odb = NULL; if ((ref->flags & GIT_REF_OID) == 0) return git__throw(GIT_EINVALIDREFSTATE, @@ -1203,23 +1204,29 @@ int git_reference_set_oid(git_reference *ref, const git_oid *id) assert(ref->owner); + error = git_repository_odb__weakptr(&odb, ref->owner); + if (error < GIT_SUCCESS) + return error; + + exists = git_odb_exists(odb, id); + + git_odb_free(odb); + /* Don't let the user create references to OIDs that * don't exist in the ODB */ - if (!git_odb_exists(git_repository_database(ref->owner), id)) + if (!exists) return git__throw(GIT_ENOTFOUND, "Failed to set OID target of reference. OID doesn't exist in ODB"); /* Update the OID value on `ref` */ git_oid_cpy(&ref->target.oid, id); + /* Write back to disk */ error = loose_write(ref); if (error < GIT_SUCCESS) - goto cleanup; + return git__rethrow(error, "Failed to set OID target of reference"); return GIT_SUCCESS; - -cleanup: - return git__rethrow(error, "Failed to set OID target of reference"); } /* diff --git a/src/remote.c b/src/remote.c index a222023d8..c6a9173af 100644 --- a/src/remote.c +++ b/src/remote.c @@ -88,14 +88,19 @@ int git_remote_new(git_remote **out, git_repository *repo, const char *url, cons return GIT_SUCCESS; } -int git_remote_get(git_remote **out, git_config *cfg, const char *name) +int git_remote_load(git_remote **out, git_repository *repo, const char *name) { git_remote *remote; char *buf = NULL; const char *val; int ret, error, buf_len; + git_config *config; - assert(out && cfg && name); + assert(out && repo && name); + + error = git_repository_config__weakptr(&config, repo); + if (error < GIT_SUCCESS) + return error; remote = git__malloc(sizeof(git_remote)); if (remote == NULL) @@ -122,13 +127,13 @@ int git_remote_get(git_remote **out, git_config *cfg, const char *name) goto cleanup; } - error = git_config_get_string(cfg, buf, &val); + error = git_config_get_string(config, buf, &val); if (error < GIT_SUCCESS) { error = git__rethrow(error, "Remote's url doesn't exist"); goto cleanup; } - remote->repo = cfg->repo; + remote->repo = repo; remote->url = git__strdup(val); if (remote->url == NULL) { error = GIT_ENOMEM; @@ -141,7 +146,7 @@ int git_remote_get(git_remote **out, git_config *cfg, const char *name) goto cleanup; } - error = parse_remote_refspec(cfg, &remote->fetch, buf); + error = parse_remote_refspec(config, &remote->fetch, buf); if (error < GIT_SUCCESS) { error = git__rethrow(error, "Failed to get fetch refspec"); goto cleanup; @@ -153,7 +158,7 @@ int git_remote_get(git_remote **out, git_config *cfg, const char *name) goto cleanup; } - error = parse_remote_refspec(cfg, &remote->push, buf); + error = parse_remote_refspec(config, &remote->push, buf); /* Not finding push is fine */ if (error == GIT_ENOTFOUND) error = GIT_SUCCESS; @@ -165,6 +170,7 @@ int git_remote_get(git_remote **out, git_config *cfg, const char *name) cleanup: git__free(buf); + if (error < GIT_SUCCESS) git_remote_free(remote); diff --git a/src/repository.c b/src/repository.c index f8195e2d9..7bac1b271 100644 --- a/src/repository.c +++ b/src/repository.c @@ -24,118 +24,57 @@ #define GIT_BRANCH_MASTER "master" -/* - * Git repository open methods - * - * Open a repository object from its path - */ -static int assign_repository_dirs( - git_repository *repo, - const char *git_dir, - const char *git_object_directory, - const char *git_index_file, - const char *git_work_tree) -{ - char path_aux[GIT_PATH_MAX]; - int error = GIT_SUCCESS; - - assert(repo); - - if (git_dir == NULL) - return git__throw(GIT_ENOTFOUND, "Failed to open repository. Git dir not found"); - error = git_path_prettify_dir(path_aux, git_dir, NULL); - if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to open repository"); - - /* store GIT_DIR */ - repo->path_repository = git__strdup(path_aux); - if (repo->path_repository == NULL) - return GIT_ENOMEM; - - /* path to GIT_OBJECT_DIRECTORY */ - if (git_object_directory == NULL) - git_path_join(path_aux, repo->path_repository, GIT_OBJECTS_DIR); - else { - error = git_path_prettify_dir(path_aux, git_object_directory, NULL); - if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to open repository"); +static void drop_odb(git_repository *repo) +{ + if (repo->_odb != NULL) { + GIT_REFCOUNT_OWN(repo->_odb, NULL); + git_odb_free(repo->_odb); + repo->_odb = NULL; } +} - /* Store GIT_OBJECT_DIRECTORY */ - repo->path_odb = git__strdup(path_aux); - if (repo->path_odb == NULL) - return GIT_ENOMEM; - - /* path to GIT_WORK_TREE */ - if (git_work_tree == NULL) - repo->is_bare = 1; - else { - error = git_path_prettify_dir(path_aux, git_work_tree, NULL); - if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to open repository"); - - /* Store GIT_WORK_TREE */ - repo->path_workdir = git__strdup(path_aux); - if (repo->path_workdir == NULL) - return GIT_ENOMEM; - - /* Path to GIT_INDEX_FILE */ - if (git_index_file == NULL) - git_path_join(path_aux, repo->path_repository, GIT_INDEX_FILE); - else { - error = git_path_prettify(path_aux, git_index_file, NULL); - if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to open repository"); - } - - /* store GIT_INDEX_FILE */ - repo->path_index = git__strdup(path_aux); - if (repo->path_index == NULL) - return GIT_ENOMEM; +static void drop_config(git_repository *repo) +{ + if (repo->_config != NULL) { + GIT_REFCOUNT_OWN(repo->_config, NULL); + git_config_free(repo->_config); + repo->_config = NULL; } - - return GIT_SUCCESS; } -static int check_repository_dirs(git_repository *repo) +static void drop_index(git_repository *repo) { - char path_aux[GIT_PATH_MAX]; - - if (git_futils_isdir(repo->path_repository) < GIT_SUCCESS) - return git__throw(GIT_ENOTAREPO, "`%s` is not a folder", repo->path_repository); - - /* Ensure GIT_OBJECT_DIRECTORY exists */ - if (git_futils_isdir(repo->path_odb) < GIT_SUCCESS) - return git__throw(GIT_ENOTAREPO, "`%s` does not exist", repo->path_odb); - - /* Ensure HEAD file exists */ - git_path_join(path_aux, repo->path_repository, GIT_HEAD_FILE); - if (git_futils_isfile(path_aux) < 0) - return git__throw(GIT_ENOTAREPO, "HEAD file is missing"); - - return GIT_SUCCESS; + if (repo->_index != NULL) { + GIT_REFCOUNT_OWN(repo->_index, NULL); + git_index_free(repo->_index); + repo->_index = NULL; + } } -static int guess_repository_dirs(git_repository *repo, const char *repository_path) +void git_repository_free(git_repository *repo) { - char buffer[GIT_PATH_MAX]; - const char *path_work_tree = NULL; + if (repo == NULL) + return; - /* Git directory name */ - if (git_path_basename_r(buffer, sizeof(buffer), repository_path) < 0) - return git__throw(GIT_EINVALIDPATH, "Unable to parse folder name from `%s`", repository_path); + git_cache_free(&repo->objects); + git_repository__refcache_free(&repo->references); - if (strcmp(buffer, DOT_GIT) == 0) { - /* Path to working dir */ - if (git_path_dirname_r(buffer, sizeof(buffer), repository_path) < 0) - return git__throw(GIT_EINVALIDPATH, "Unable to parse parent folder name from `%s`", repository_path); - path_work_tree = buffer; - } + git__free(repo->path_repository); + git__free(repo->workdir); - return assign_repository_dirs(repo, repository_path, NULL, NULL, path_work_tree); + drop_config(repo); + drop_index(repo); + drop_odb(repo); + + git__free(repo); } +/* + * Git repository open methods + * + * Open a repository object from its path + */ static int quickcheck_repository_dir(const char *repository_path) { char path_aux[GIT_PATH_MAX]; @@ -156,6 +95,7 @@ static int quickcheck_repository_dir(const char *repository_path) return GIT_SUCCESS; } + static git_repository *repository_alloc(void) { int error; @@ -175,94 +115,95 @@ static git_repository *repository_alloc(void) return repo; } -static int init_odb(git_repository *repo) +static int load_config_data(git_repository *repo) { - return git_odb_open(&repo->db, repo->path_odb); -} + int error, is_bare; + git_config *config; -int git_repository_open3(git_repository **repo_out, - const char *git_dir, - git_odb *object_database, - const char *git_index_file, - const char *git_work_tree) -{ - git_repository *repo; - int error = GIT_SUCCESS; - - assert(repo_out); + error = git_repository_config__weakptr(&config, repo); + if (error < GIT_SUCCESS) + return error; - if (object_database == NULL) - return git__throw(GIT_EINVALIDARGS, "Failed to open repository. `object_database` can't be null"); + error = git_config_get_bool(config, "core.bare", &is_bare); + if (error == GIT_SUCCESS) + repo->is_bare = is_bare; - repo = repository_alloc(); - if (repo == NULL) - return GIT_ENOMEM; + return GIT_SUCCESS; +} - error = assign_repository_dirs(repo, - git_dir, - NULL, - git_index_file, - git_work_tree); +static int load_workdir(git_repository *repo) +{ + if (!repo->is_bare) { + char workdir_buf[GIT_PATH_MAX]; - if (error < GIT_SUCCESS) - goto cleanup; + if (git_path_dirname_r(workdir_buf, sizeof(workdir_buf), repo->path_repository) < 0) + return git__throw(GIT_EOSERR, + "Failed to resolved working directory"); - error = check_repository_dirs(repo); - if (error < GIT_SUCCESS) - goto cleanup; + git_path_join(workdir_buf, workdir_buf, ""); - repo->db = object_database; + repo->workdir = git__strdup(workdir_buf); + if (repo->workdir == NULL) + return GIT_ENOMEM; + } - *repo_out = repo; return GIT_SUCCESS; - -cleanup: - git_repository_free(repo); - return git__rethrow(error, "Failed to open repository"); } - -int git_repository_open2(git_repository **repo_out, - const char *git_dir, - const char *git_object_directory, - const char *git_index_file, - const char *git_work_tree) +int git_repository_open(git_repository **repo_out, const char *path) { - git_repository *repo; int error = GIT_SUCCESS; + char path_buf[GIT_PATH_MAX]; + size_t path_len; + git_repository *repo = NULL; + + error = git_path_prettify_dir(path_buf, path, NULL); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to open repository"); - assert(repo_out); + path_len = strlen(path_buf); + + /** + * Check if the path we've been given is actually the path + * of the working dir, by testing if it contains a `.git` + * folder inside of it. + */ + git_path_join(path_buf, path_buf, DOT_GIT); + if (git_futils_isdir(path_buf) < GIT_SUCCESS) { + path_buf[path_len] = 0; + } + + if (quickcheck_repository_dir(path_buf) < GIT_SUCCESS) + return git__throw(GIT_ENOTAREPO, + "The given path is not a valid Git repository"); repo = repository_alloc(); if (repo == NULL) return GIT_ENOMEM; - error = assign_repository_dirs(repo, - git_dir, - git_object_directory, - git_index_file, - git_work_tree); - - if (error < GIT_SUCCESS) - goto cleanup; + repo->path_repository = git__strdup(path_buf); + if (repo->path_repository == NULL) { + git_repository_free(repo); + return GIT_ENOMEM; + } - error = check_repository_dirs(repo); - if (error < GIT_SUCCESS) - goto cleanup; + error = load_config_data(repo); + if (error < GIT_SUCCESS) { + git_repository_free(repo); + return error; + } - error = init_odb(repo); - if (error < GIT_SUCCESS) - goto cleanup; + error = load_workdir(repo); + if (error < GIT_SUCCESS) { + git_repository_free(repo); + return error; + } *repo_out = repo; return GIT_SUCCESS; - -cleanup: - git_repository_free(repo); - return git__rethrow(error, "Failed to open repository"); } -int git_repository_config( +static int load_config( git_config **out, git_repository *repo, const char *global_config_path, @@ -270,98 +211,178 @@ int git_repository_config( { char config_path[GIT_PATH_MAX]; int error; + git_config *cfg = NULL; - assert(out && repo); + assert(repo && out); - error = git_config_new(out); + error = git_config_new(&cfg); if (error < GIT_SUCCESS) return error; git_path_join(config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO); - error = git_config_add_file_ondisk(*out, config_path, 3); + error = git_config_add_file_ondisk(cfg, config_path, 3); if (error < GIT_SUCCESS) goto cleanup; if (global_config_path != NULL) { - error = git_config_add_file_ondisk(*out, global_config_path, 2); + error = git_config_add_file_ondisk(cfg, global_config_path, 2); if (error < GIT_SUCCESS) goto cleanup; } if (system_config_path != NULL) { - error = git_config_add_file_ondisk(*out, system_config_path, 1); + error = git_config_add_file_ondisk(cfg, system_config_path, 1); if (error < GIT_SUCCESS) goto cleanup; } - (*out)->repo = repo; + *out = cfg; return GIT_SUCCESS; cleanup: - git_config_free(*out); + git_config_free(cfg); + *out = NULL; return error; } -int git_repository_config_autoload( - git_config **out, - git_repository *repo) +int git_repository_config__weakptr(git_config **out, git_repository *repo) { - char global[GIT_PATH_MAX], system[GIT_PATH_MAX]; - char *global_path, *system_path; - int error; + if (repo->_config == NULL) { + int error; + + char buf_global[GIT_PATH_MAX], buf_system[GIT_PATH_MAX]; + + const char *global_config_path = NULL; + const char *system_config_path = NULL; + if (git_config_find_global(buf_global) == GIT_SUCCESS) + global_config_path = buf_global; - error = git_config_find_global(global); - global_path = error < GIT_SUCCESS ? NULL : global; + if (git_config_find_system(buf_system) == GIT_SUCCESS) + system_config_path = buf_system; - error = git_config_find_system(system); - system_path = error < GIT_SUCCESS ? NULL : system; + error = load_config(&repo->_config, repo, global_config_path, system_config_path); + if (error < GIT_SUCCESS) + return error; + + GIT_REFCOUNT_OWN(repo->_config, repo); + } - return git_repository_config(out, repo, global_path, system_path); + *out = repo->_config; + return GIT_SUCCESS; } -static int discover_repository_dirs(git_repository *repo, const char *path) +int git_repository_config(git_config **out, git_repository *repo) { - int error; + int error = git_repository_config__weakptr(out, repo); - error = guess_repository_dirs(repo, path); - if (error < GIT_SUCCESS) - return error; + if (error == GIT_SUCCESS) { + GIT_REFCOUNT_INC(*out); + } - error = check_repository_dirs(repo); - if (error < GIT_SUCCESS) - return error; + return error; +} + +void git_repository_set_config(git_repository *repo, git_config *config) +{ + assert(repo && config); + + drop_config(repo); + + repo->_config = config; + GIT_REFCOUNT_OWN(repo->_config, repo); +} + +int git_repository_odb__weakptr(git_odb **out, git_repository *repo) +{ + assert(repo && out); + + if (repo->_odb == NULL) { + int error; + char odb_path[GIT_PATH_MAX]; + + git_path_join(odb_path, repo->path_repository, GIT_OBJECTS_DIR); + + error = git_odb_open(&repo->_odb, odb_path); + if (error < GIT_SUCCESS) + return error; + + GIT_REFCOUNT_OWN(repo->_odb, repo); + } + GIT_REFCOUNT_INC(repo->_odb); + *out = repo->_odb; return GIT_SUCCESS; } -int git_repository_open(git_repository **repo_out, const char *path) +int git_repository_odb(git_odb **out, git_repository *repo) { - git_repository *repo; - int error = GIT_SUCCESS; + int error = git_repository_odb__weakptr(out, repo); - assert(repo_out && path); + if (error == GIT_SUCCESS) { + GIT_REFCOUNT_INC(*out); + } - repo = repository_alloc(); - if (repo == NULL) - return GIT_ENOMEM; + return error; +} - error = discover_repository_dirs(repo, path); - if (error < GIT_SUCCESS) - goto cleanup; +void git_repository_set_odb(git_repository *repo, git_odb *odb) +{ + assert(repo && odb); - error = init_odb(repo); - if (error < GIT_SUCCESS) - goto cleanup; + drop_odb(repo); - *repo_out = repo; + repo->_odb = odb; + GIT_REFCOUNT_OWN(repo->_odb, repo); +} + +int git_repository_index__weakptr(git_index **out, git_repository *repo) +{ + assert(out && repo); + + if (repo->is_bare) + return git__throw(GIT_EBAREINDEX, "Cannot open index in bare repository"); + + if (repo->_index == NULL) { + int error; + char index_path[GIT_PATH_MAX]; + + git_path_join(index_path, repo->path_repository, GIT_INDEX_FILE); + + error = git_index_open(&repo->_index, index_path); + if (error < GIT_SUCCESS) + return error; + + GIT_REFCOUNT_OWN(repo->_index, repo); + } + + GIT_REFCOUNT_INC(repo->_index); + *out = repo->_index; return GIT_SUCCESS; +} -cleanup: - git_repository_free(repo); - return git__rethrow(error, "Failed to open repository"); +int git_repository_index(git_index **out, git_repository *repo) +{ + int error = git_repository_index__weakptr(out, repo); + + if (error == GIT_SUCCESS) { + GIT_REFCOUNT_INC(*out); + } + + return error; } +void git_repository_set_index(git_repository *repo, git_index *index) +{ + assert(repo && index); + + drop_index(repo); + + repo->_index = index; + GIT_REFCOUNT_OWN(repo->_index, repo); +} + + static int retrieve_device(dev_t *device_out, const char *path) { struct stat path_info; @@ -460,34 +481,12 @@ static int read_gitfile(char *path_out, const char *file_path, const char *base_ return git__throw(GIT_EOBJCORRUPTED, "The `.git` file points to an inexisting path"); } -static void git_repository__free_dirs(git_repository *repo) -{ - git__free(repo->path_workdir); - repo->path_workdir = NULL; - git__free(repo->path_index); - repo->path_index = NULL; - git__free(repo->path_repository); - repo->path_repository = NULL; - git__free(repo->path_odb); - repo->path_odb = NULL; -} - -void git_repository_free(git_repository *repo) -{ - if (repo == NULL) - return; - - git_cache_free(&repo->objects); - git_repository__refcache_free(&repo->references); - git_repository__free_dirs(repo); - - if (repo->db != NULL) - git_odb_close(repo->db); - - git__free(repo); -} - -int git_repository_discover(char *repository_path, size_t size, const char *start_path, int across_fs, const char *ceiling_dirs) +int git_repository_discover( + char *repository_path, + size_t size, + const char *start_path, + int across_fs, + const char *ceiling_dirs) { int error, ceiling_offset; char bare_path[GIT_PATH_MAX]; @@ -519,11 +518,13 @@ int git_repository_discover(char *repository_path, size_t size, const char *star error = read_gitfile(repository_path, normal_path, bare_path); if (error < GIT_SUCCESS) - return git__rethrow(error, "Unable to read git file `%s`", normal_path); + return git__rethrow(error, + "Unable to read git file `%s`", normal_path); error = quickcheck_repository_dir(repository_path); if (error < GIT_SUCCESS) - return git__throw(GIT_ENOTFOUND, "The `.git` file found at '%s' points" + return git__throw(GIT_ENOTFOUND, + "The `.git` file found at '%s' points" "to an inexisting Git folder", normal_path); return GIT_SUCCESS; @@ -558,7 +559,8 @@ int git_repository_discover(char *repository_path, size_t size, const char *star error = retrieve_device(&new_device, normal_path); if (error < GIT_SUCCESS || current_device != new_device) { - return git__throw(GIT_ENOTAREPO,"Not a git repository (or any parent up to mount parent %s)\n" + return git__throw(GIT_ENOTAREPO, + "Not a git repository (or any parent up to mount parent %s)\n" "Stopping at filesystem boundary.", bare_path); } current_device = new_device; @@ -569,43 +571,81 @@ int git_repository_discover(char *repository_path, size_t size, const char *star // nothing has been found, lets try the parent directory if (bare_path[ceiling_offset] == '\0') { - return git__throw(GIT_ENOTAREPO,"Not a git repository (or any of the parent directories): %s", start_path); + return git__throw(GIT_ENOTAREPO, + "Not a git repository (or any of the parent directories): %s", start_path); } } if (size < strlen(found_path) + 2) { - return git__throw(GIT_ESHORTBUFFER, "The repository buffer is not long enough to handle the repository path `%s`", found_path); + return git__throw(GIT_ESHORTBUFFER, + "The repository buffer is not long enough to handle the repository path `%s`", found_path); } git_path_join(repository_path, found_path, ""); return GIT_SUCCESS; } -git_odb *git_repository_database(git_repository *repo) -{ - assert(repo); - return repo->db; -} - static int repo_init_reinit(const char *repository_path, int is_bare) { /* TODO: reinit the repository */ return git__throw(GIT_ENOTIMPLEMENTED, "Failed to reinitialize the %srepository at '%s'. " "This feature is not yet implemented", - is_bare ? "bare" : "", repository_path); + is_bare ? "bare " : "", repository_path); } -static int repo_init_createhead(git_repository *repo) +static int repo_init_createhead(const char *git_dir) { - int error; - git_reference *head_reference; + char ref_path[GIT_PATH_MAX]; + git_filebuf ref = GIT_FILEBUF_INIT; + + git_path_join(ref_path, git_dir, GIT_HEAD_FILE); + + git_filebuf_open(&ref, ref_path, 0); + git_filebuf_printf(&ref, "ref: refs/heads/master\n"); + + return git_filebuf_commit(&ref, GIT_REFS_FILE_MODE); +} + +static int repo_init_config(const char *git_dir, int is_bare) +{ + char cfg_path[GIT_PATH_MAX]; + git_filebuf cfg = GIT_FILEBUF_INIT; + + git_path_join(cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO); - error = git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE, 0); + git_filebuf_open(&cfg, cfg_path, 0); + git_filebuf_printf(&cfg, "[core]\n"); + git_filebuf_printf(&cfg, "\tbare = %s\n", is_bare ? "true" : "false"); + git_filebuf_printf(&cfg, "\trepositoryformatversion = 0\n"); - git_reference_free(head_reference); + return git_filebuf_commit(&cfg, GIT_REFS_FILE_MODE); + /* TODO: use the config backend to write this */ +#if 0 + git_config *config; + int error = GIT_SUCCESS; + +#define SET_REPO_CONFIG(type, name, val) {\ + error = git_config_set_##type(config, name, val);\ + if (error < GIT_SUCCESS)\ + goto cleanup;\ +} + + git_path_join(cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO); + + error = git_config_open_ondisk(&config, cfg_path); + if (error < GIT_SUCCESS) + return error; + + SET_REPO_CONFIG(bool, "core.bare", is_bare); + SET_REPO_CONFIG(int32, "core.repositoryformatversion", 0); + /* TODO: what other defaults? */ + +cleanup: + git_config_free(config); return error; +#endif } static int repo_init_structure(const char *git_dir, int is_bare) @@ -674,31 +714,15 @@ int git_repository_init(git_repository **repo_out, const char *path, unsigned is if (error < GIT_SUCCESS) goto cleanup; - repo = repository_alloc(); - if (repo == NULL) { - error = GIT_ENOMEM; - goto cleanup; - } - - error = guess_repository_dirs(repo, repository_path); + error = repo_init_config(repository_path, is_bare); if (error < GIT_SUCCESS) goto cleanup; - assert(repo->is_bare == is_bare); - - error = init_odb(repo); - if (error < GIT_SUCCESS) - goto cleanup; - - error = repo_init_createhead(repo); + error = repo_init_createhead(repository_path); if (error < GIT_SUCCESS) goto cleanup; - /* should never fail */ - assert(check_repository_dirs(repo) == GIT_SUCCESS); - - *repo_out = repo; - return GIT_SUCCESS; + return git_repository_open(repo_out, repository_path); cleanup: git_repository_free(repo); @@ -709,8 +733,13 @@ int git_repository_head_detached(git_repository *repo) { git_reference *ref; int error; - size_t GIT_UNUSED(_size); + size_t _size; git_otype type; + git_odb *odb = NULL; + + error = git_repository_odb__weakptr(&odb, repo); + if (error < GIT_SUCCESS) + return error; error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE); if (error < GIT_SUCCESS) @@ -721,7 +750,7 @@ int git_repository_head_detached(git_repository *repo) return 0; } - error = git_odb_read_header(&_size, &type, repo->db, git_reference_oid(ref)); + error = git_odb_read_header(&_size, &type, odb, git_reference_oid(ref)); git_reference_free(ref); @@ -797,26 +826,34 @@ int git_repository_is_empty(git_repository *repo) return error == GIT_ENOTFOUND ? 1 : error; } -const char *git_repository_path(git_repository *repo, git_repository_pathid id) +const char *git_repository_path(git_repository *repo) { assert(repo); + return repo->path_repository; +} - switch (id) { - case GIT_REPO_PATH: - return repo->path_repository; +const char *git_repository_workdir(git_repository *repo) +{ + assert(repo); - case GIT_REPO_PATH_INDEX: - return repo->path_index; + if (repo->is_bare) + return NULL; - case GIT_REPO_PATH_ODB: - return repo->path_odb; + return repo->workdir; +} - case GIT_REPO_PATH_WORKDIR: - return repo->path_workdir; +int git_repository_set_workdir(git_repository *repo, const char *workdir) +{ + assert(repo && workdir); - default: - return NULL; - } + free(repo->workdir); + + repo->workdir = git__strdup(workdir); + if (repo->workdir == NULL) + return GIT_ENOMEM; + + repo->is_bare = 0; + return GIT_SUCCESS; } int git_repository_is_bare(git_repository *repo) diff --git a/src/repository.h b/src/repository.h index 0c17958fd..c3a9a5c60 100644 --- a/src/repository.h +++ b/src/repository.h @@ -32,15 +32,15 @@ struct git_object { }; struct git_repository { - git_odb *db; + git_odb *_odb; + git_config *_config; + git_index *_index; git_cache objects; git_refcache references; char *path_repository; - char *path_index; - char *path_odb; - char *path_workdir; + char *workdir; unsigned is_bare:1; unsigned int lru_counter; @@ -53,4 +53,8 @@ void git_object__free(void *object); int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header); void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid); +int git_repository_config__weakptr(git_config **out, git_repository *repo); +int git_repository_odb__weakptr(git_odb **out, git_repository *repo); +int git_repository_index__weakptr(git_index **out, git_repository *repo); + #endif diff --git a/src/revwalk.c b/src/revwalk.c index 7e31650ff..64775649c 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -34,6 +34,7 @@ typedef struct commit_list { struct git_revwalk { git_repository *repo; + git_odb *odb; git_hashtable *commits; @@ -225,7 +226,7 @@ static int commit_parse(git_revwalk *walk, commit_object *commit) if (commit->parsed) return GIT_SUCCESS; - if ((error = git_odb_read(&obj, walk->repo->db, &commit->oid)) < GIT_SUCCESS) + if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < GIT_SUCCESS) return git__rethrow(error, "Failed to parse commit. Can't read object"); if (obj->raw.type != GIT_OBJ_COMMIT) { @@ -429,6 +430,7 @@ static int prepare_walk(git_revwalk *walk) int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo) { + int error; git_revwalk *walk; walk = git__malloc(sizeof(git_revwalk)); @@ -455,6 +457,12 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo) walk->repo = repo; + error = git_repository_odb(&walk->odb, repo); + if (error < GIT_SUCCESS) { + git_revwalk_free(walk); + return error; + } + *revwalk_out = walk; return GIT_SUCCESS; } @@ -469,6 +477,7 @@ void git_revwalk_free(git_revwalk *walk) return; git_revwalk_reset(walk); + git_odb_free(walk->odb); /* if the parent has more than PARENTS_PER_COMMIT parents, * we had to allocate a separate array for those parents. diff --git a/src/status.c b/src/status.c index d50199d9a..d09abfd69 100644 --- a/src/status.c +++ b/src/status.c @@ -410,9 +410,15 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig unsigned int i; git_tree *tree; struct status_entry *e; + const char *workdir; - if ((error = git_repository_index(&index, repo)) < GIT_SUCCESS) { - return git__rethrow(error, "Failed to determine statuses. Index can't be opened"); + if ((workdir = git_repository_workdir(repo)) == NULL) + return git__throw(GIT_ERROR, + "Cannot retrieve status on a bare repository"); + + if ((error = git_repository_index__weakptr(&index, repo)) < GIT_SUCCESS) { + return git__rethrow(error, + "Failed to determine statuses. Index can't be opened"); } if ((error = retrieve_head_tree(&tree, repo)) < GIT_SUCCESS) { @@ -422,7 +428,7 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig git_vector_init(&entries, DEFAULT_SIZE, status_cmp); - dirent_st.workdir_path_len = strlen(repo->path_workdir); + dirent_st.workdir_path_len = strlen(workdir); dirent_st.tree_position = 0; dirent_st.index_position = 0; dirent_st.tree = tree; @@ -432,18 +438,29 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig dirent_st.head_tree_relative_path_len = 0; dirent_st.is_dir = 1; - strcpy(temp_path, repo->path_workdir); + strcpy(temp_path, workdir); if (git_futils_isdir(temp_path)) { - error = git__throw(GIT_EINVALIDPATH, "Failed to determine status of file '%s'. Provided path doesn't lead to a folder", temp_path); + error = git__throw(GIT_EINVALIDPATH, + "Failed to determine status of file '%s'. " + "The given path doesn't lead to a folder", temp_path); goto exit; } - if ((error = alphasorted_futils_direach(temp_path, sizeof(temp_path), dirent_cb, &dirent_st)) < GIT_SUCCESS) - error = git__rethrow(error, "Failed to determine statuses. An error occured while processing the working directory"); + error = alphasorted_futils_direach( + temp_path, sizeof(temp_path), + dirent_cb, &dirent_st + ); + + if (error < GIT_SUCCESS) + error = git__rethrow(error, + "Failed to determine statuses. " + "An error occured while processing the working directory"); if ((error == GIT_SUCCESS) && ((error = dirent_cb(&dirent_st, NULL)) < GIT_SUCCESS)) - error = git__rethrow(error, "Failed to determine statuses. An error occured while post-processing the HEAD tree and the index"); + error = git__rethrow(error, + "Failed to determine statuses. " + "An error occured while post-processing the HEAD tree and the index"); for (i = 0; i < entries.length; ++i) { e = (struct status_entry *)git_vector_get(&entries, i); @@ -451,7 +468,8 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig if (error == GIT_SUCCESS) { error = callback(e->path, e->status_flags, payload); if (error < GIT_SUCCESS) - error = git__rethrow(error, "Failed to determine statuses. User callback failed"); + error = git__rethrow(error, + "Failed to determine statuses. User callback failed"); } git__free(e); @@ -460,7 +478,6 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig exit: git_vector_free(&entries); git_tree_close(tree); - git_index_free(index); return error; } @@ -506,12 +523,19 @@ int git_status_file(unsigned int *status_flags, git_repository *repo, const char char temp_path[GIT_PATH_MAX]; int error = GIT_SUCCESS; git_tree *tree = NULL; + const char *workdir; assert(status_flags && repo && path); - git_path_join(temp_path, repo->path_workdir, path); + if ((workdir = git_repository_workdir(repo)) == NULL) + return git__throw(GIT_ERROR, + "Cannot retrieve status on a bare repository"); + + git_path_join(temp_path, workdir, path); if (git_futils_isdir(temp_path) == GIT_SUCCESS) - return git__throw(GIT_EINVALIDPATH, "Failed to determine status of file '%s'. Provided path leads to a folder, not a file", path); + return git__throw(GIT_EINVALIDPATH, + "Failed to determine status of file '%s'. " + "Given path leads to a folder, not a file", path); e = status_entry_new(NULL, path); if (e == NULL) @@ -524,16 +548,18 @@ int git_status_file(unsigned int *status_flags, git_repository *repo, const char } /* Find file in Index */ - if ((error = git_repository_index(&index, repo)) < GIT_SUCCESS) { - error = git__rethrow(error, "Failed to determine status of file '%s'. Index can't be opened", path); + if ((error = git_repository_index__weakptr(&index, repo)) < GIT_SUCCESS) { + error = git__rethrow(error, + "Failed to determine status of file '%s'." + "Index can't be opened", path); goto exit; } status_entry_update_from_index(e, index); - git_index_free(index); if ((error = retrieve_head_tree(&tree, repo)) < GIT_SUCCESS) { - error = git__rethrow(error, "Failed to determine status of file '%s'", path); + error = git__rethrow(error, + "Failed to determine status of file '%s'", path); goto exit; } @@ -543,7 +569,9 @@ int git_status_file(unsigned int *status_flags, git_repository *repo, const char error = recurse_tree_entry(tree, e, temp_path); if (error < GIT_SUCCESS) { - error = git__rethrow(error, "Failed to determine status of file '%s'. An error occured while processing the tree", path); + error = git__rethrow(error, + "Failed to determine status of file '%s'. " + "An error occured while processing the tree", path); goto exit; } } @@ -175,6 +175,7 @@ static int write_tag_annotation( { int error = GIT_SUCCESS; git_buf tag = GIT_BUF_INIT; + git_odb *odb; git_oid__writebuf(&tag, "object ", git_object_id(target)); git_buf_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target))); @@ -188,7 +189,13 @@ static int write_tag_annotation( return git__throw(GIT_ENOMEM, "Not enough memory to build the tag data"); } - error = git_odb_write(oid, git_repository_database(repo), tag.ptr, tag.size, GIT_OBJ_TAG); + error = git_repository_odb__weakptr(&odb, repo); + if (error < GIT_SUCCESS) { + git_buf_free(&tag); + return error; + } + + error = git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJ_TAG); git_buf_free(&tag); if (error < GIT_SUCCESS) @@ -286,6 +293,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu { git_tag tag; int error, should_update_ref = 0; + git_odb *odb; git_odb_stream *stream; git_odb_object *target_obj; @@ -296,12 +304,16 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu memset(&tag, 0, sizeof(tag)); + error = git_repository_odb__weakptr(&odb, repo); + if (error < GIT_SUCCESS) + return error; + /* validate the buffer */ if ((error = parse_tag_buffer(&tag, buffer, buffer + strlen(buffer))) < GIT_SUCCESS) return git__rethrow(error, "Failed to create tag"); /* validate the target */ - if ((error = git_odb_read(&target_obj, repo->db, &tag.target)) < GIT_SUCCESS) + if ((error = git_odb_read(&target_obj, odb, &tag.target)) < GIT_SUCCESS) return git__rethrow(error, "Failed to create tag"); if (tag.type != target_obj->raw.type) @@ -334,7 +346,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu } /* write the buffer */ - if ((error = git_odb_open_wstream(&stream, repo->db, strlen(buffer), GIT_OBJ_TAG)) < GIT_SUCCESS) { + if ((error = git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG)) < GIT_SUCCESS) { git_reference_free(new_ref); return git__rethrow(error, "Failed to create tag"); } diff --git a/src/tree.c b/src/tree.c index 92ca5ab77..ec44d2492 100644 --- a/src/tree.c +++ b/src/tree.c @@ -300,9 +300,15 @@ static int append_entry(git_treebuilder *bld, const char *filename, const git_oi return GIT_SUCCESS; } -static int write_tree(git_oid *oid, git_index *index, const char *dirname, unsigned int start) +static int write_tree( + git_oid *oid, + git_repository *repo, + git_index *index, + const char *dirname, + unsigned int start) { git_treebuilder *bld = NULL; + unsigned int i, entries = git_index_entrycount(index); int error; size_t dirname_len = strlen(dirname); @@ -358,7 +364,7 @@ static int write_tree(git_oid *oid, git_index *index, const char *dirname, unsig } /* Write out the subtree */ - written = write_tree(&sub_oid, index, subdir, i); + written = write_tree(&sub_oid, repo, index, subdir, i); if (written < 0) { error = git__rethrow(written, "Failed to write subtree %s", subdir); } else { @@ -391,7 +397,7 @@ static int write_tree(git_oid *oid, git_index *index, const char *dirname, unsig } } - error = git_treebuilder_write(oid, index->repository, bld); + error = git_treebuilder_write(oid, repo, bld); if (error < GIT_SUCCESS) error = git__rethrow(error, "Failed to write tree to db"); @@ -406,10 +412,15 @@ static int write_tree(git_oid *oid, git_index *index, const char *dirname, unsig int git_tree_create_fromindex(git_oid *oid, git_index *index) { + git_repository *repo; int error; - if (index->repository == NULL) - return git__throw(GIT_EBAREINDEX, "Failed to create tree. The index file is not backed up by an existing repository"); + repo = (git_repository *)GIT_REFCOUNT_OWNER(index); + + if (repo == NULL) + return git__throw(GIT_EBAREINDEX, + "Failed to create tree. " + "The index file is not backed up by an existing repository"); if (index->tree != NULL && index->tree->entries >= 0) { git_oid_cpy(oid, &index->tree->oid); @@ -417,7 +428,7 @@ int git_tree_create_fromindex(git_oid *oid, git_index *index) } /* The tree cache didn't help us */ - error = write_tree(oid, index, "", 0); + error = write_tree(oid, repo, index, "", 0); return (error < GIT_SUCCESS) ? git__rethrow(error, "Failed to create tree") : GIT_SUCCESS; } @@ -546,6 +557,7 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b unsigned int i; int error; git_buf tree = GIT_BUF_INIT; + git_odb *odb; assert(bld); @@ -570,7 +582,13 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b return git__throw(GIT_ENOMEM, "Not enough memory to build the tree data"); } - error = git_odb_write(oid, git_repository_database(repo), tree.ptr, tree.size, GIT_OBJ_TREE); + error = git_repository_odb__weakptr(&odb, repo); + if (error < GIT_SUCCESS) { + git_buf_free(&tree); + return error; + } + + error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE); git_buf_free(&tree); return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write tree"); diff --git a/src/util.h b/src/util.h index fbf9012a3..2b239a0bf 100644 --- a/src/util.h +++ b/src/util.h @@ -129,4 +129,28 @@ extern void **git__bsearch(const void *key, void **base, size_t nmemb, extern int git__strcmp_cb(const void *a, const void *b); +typedef struct { + short refcount; + void *owner; +} git_refcount; + +typedef void (*git_refcount_freeptr)(void *r); + +#define GIT_REFCOUNT_INC(r) { \ + ((git_refcount *)(r))->refcount++; \ +} + +#define GIT_REFCOUNT_DEC(_r, do_free) { \ + git_refcount *r = (git_refcount *)(_r); \ + r->refcount--; \ + if (r->refcount == 0 && r->owner == NULL) { do_free(_r); } \ +} + +#define GIT_REFCOUNT_OWN(r, o) { \ + ((git_refcount *)(r))->owner = o; \ +} + +#define GIT_REFCOUNT_OWNER(r) (((git_refcount *)(r))->owner) + + #endif /* INCLUDE_util_h__ */ |