diff options
author | Vicent Marti <tanoku@gmail.com> | 2011-03-18 20:38:49 +0300 |
---|---|---|
committer | Vicent Marti <tanoku@gmail.com> | 2011-03-20 22:45:11 +0300 |
commit | 72a3fe42fb7208712bbe8f0981f4c6274c05e9c3 (patch) | |
tree | b073a1c36b3215d94b1e78a0f294d7aa30d949df /src | |
parent | bb3de0c472b2d5d6b8091c190bee3db79c4b5e27 (diff) |
I broke your bindings
Hey. Apologies in advance -- I broke your bindings.
This is a major commit that includes a long-overdue redesign of the
whole object-database structure. This is expected to be the last major
external API redesign of the library until the first non-alpha release.
Please get your bindings up to date with these changes. They will be
included in the next minor release. Sorry again!
Major features include:
- Real caching and refcounting on parsed objects
- Real caching and refcounting on objects read from the ODB
- Streaming writes & reads from the ODB
- Single-method writes for all object types
- The external API is now partially thread-safe
The speed increases are significant in all aspects, specially when
reading an object several times from the ODB (revwalking) and when
writing big objects to the ODB.
Here's a full changelog for the external API:
blob.h
------
- Remove `git_blob_new`
- Remove `git_blob_set_rawcontent`
- Remove `git_blob_set_rawcontent_fromfile`
- Rename `git_blob_writefile` -> `git_blob_create_fromfile`
- Change `git_blob_create_fromfile`:
The `path` argument is now relative to the repository's working dir
- Add `git_blob_create_frombuffer`
commit.h
--------
- Remove `git_commit_new`
- Remove `git_commit_add_parent`
- Remove `git_commit_set_message`
- Remove `git_commit_set_committer`
- Remove `git_commit_set_author`
- Remove `git_commit_set_tree`
- Add `git_commit_create`
- Add `git_commit_create_v`
- Add `git_commit_create_o`
- Add `git_commit_create_ov`
tag.h
-----
- Remove `git_tag_new`
- Remove `git_tag_set_target`
- Remove `git_tag_set_name`
- Remove `git_tag_set_tagger`
- Remove `git_tag_set_message`
- Add `git_tag_create`
- Add `git_tag_create_o`
tree.h
------
- Change `git_tree_entry_2object`:
New signature is `(git_object **object_out, git_repository *repo, git_tree_entry *entry)`
- Remove `git_tree_new`
- Remove `git_tree_add_entry`
- Remove `git_tree_remove_entry_byindex`
- Remove `git_tree_remove_entry_byname`
- Remove `git_tree_clearentries`
- Remove `git_tree_entry_set_id`
- Remove `git_tree_entry_set_name`
- Remove `git_tree_entry_set_attributes`
object.h
------------
- Remove `git_object_new
- Remove `git_object_write`
- Change `git_object_close`:
This method is now *mandatory*. Not closing an object causes a
memory leak.
odb.h
-----
- Remove type `git_rawobj`
- Remove `git_rawobj_close`
- Rename `git_rawobj_hash` -> `git_odb_hash`
- Change `git_odb_hash`:
New signature is `(git_oid *id, const void *data, size_t len, git_otype type)`
- Add type `git_odb_object`
- Add `git_odb_object_close`
- Change `git_odb_read`:
New signature is `(git_odb_object **out, git_odb *db, const git_oid *id)`
- Change `git_odb_read_header`:
New signature is `(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)`
- Remove `git_odb_write`
- Add `git_odb_open_wstream`
- Add `git_odb_open_rstream`
odb_backend.h
-------------
- Change type `git_odb_backend`:
New internal signatures are as follows
int (* read)(void **, size_t *, git_otype *, struct git_odb_backend *, const git_oid *)
int (* read_header)(size_t *, git_otype *, struct git_odb_backend *, const git_oid *)
int (* writestream)(struct git_odb_stream **, struct git_odb_backend *, size_t, git_otype)
int (* readstream)( struct git_odb_stream **, struct git_odb_backend *, const git_oid *)
- Add type `git_odb_stream`
- Add enum `git_odb_streammode`
Signed-off-by: Vicent Marti <tanoku@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/backends/sqlite.c | 9 | ||||
-rw-r--r-- | src/blob.c | 113 | ||||
-rw-r--r-- | src/blob.h | 6 | ||||
-rw-r--r-- | src/cache.c | 46 | ||||
-rw-r--r-- | src/cache.h | 21 | ||||
-rw-r--r-- | src/commit.c | 268 | ||||
-rw-r--r-- | src/commit.h | 4 | ||||
-rw-r--r-- | src/delta-apply.h | 2 | ||||
-rw-r--r-- | src/errors.c | 3 | ||||
-rw-r--r-- | src/filebuf.c | 188 | ||||
-rw-r--r-- | src/filebuf.h | 22 | ||||
-rw-r--r-- | src/fileops.c | 90 | ||||
-rw-r--r-- | src/fileops.h | 2 | ||||
-rw-r--r-- | src/index.c | 2 | ||||
-rw-r--r-- | src/object.c | 276 | ||||
-rw-r--r-- | src/odb.c | 133 | ||||
-rw-r--r-- | src/odb.h | 18 | ||||
-rw-r--r-- | src/odb_loose.c | 264 | ||||
-rw-r--r-- | src/odb_pack.c | 19 | ||||
-rw-r--r-- | src/oid.c | 14 | ||||
-rw-r--r-- | src/repository.c | 72 | ||||
-rw-r--r-- | src/repository.h | 28 | ||||
-rw-r--r-- | src/revwalk.c | 13 | ||||
-rw-r--r-- | src/signature.c | 23 | ||||
-rw-r--r-- | src/signature.h | 2 | ||||
-rw-r--r-- | src/tag.c | 144 | ||||
-rw-r--r-- | src/tag.h | 4 | ||||
-rw-r--r-- | src/thread-utils.h | 10 | ||||
-rw-r--r-- | src/tree.c | 191 | ||||
-rw-r--r-- | src/tree.h | 7 | ||||
-rw-r--r-- | src/util.h | 2 |
31 files changed, 918 insertions, 1078 deletions
diff --git a/src/backends/sqlite.c b/src/backends/sqlite.c index b4c941a59..72d7b4d8e 100644 --- a/src/backends/sqlite.c +++ b/src/backends/sqlite.c @@ -272,4 +272,13 @@ cleanup: return GIT_ERROR; } +#else + +int git_odb_backend_sqlite(git_odb_backend **GIT_UNUSED(backend_out), const char *GIT_UNUSED(sqlite_db)) +{ + GIT_UNUSED_ARG(backend_out); + GIT_UNUSED_ARG(sqlite_db); + return GIT_ENOTIMPLEMENTED; +} + #endif /* HAVE_SQLITE3 */ diff --git a/src/blob.c b/src/blob.c index 1e03b6b67..bc0a08a8a 100644 --- a/src/blob.c +++ b/src/blob.c @@ -33,104 +33,89 @@ const void *git_blob_rawcontent(git_blob *blob) { assert(blob); - - if (blob->content.data != NULL) - return blob->content.data; - - if (blob->object.in_memory) - return NULL; - - if (!blob->object.source.open && git_object__source_open((git_object *)blob) < GIT_SUCCESS) - return NULL; - - return blob->object.source.raw.data; + return blob->odb_object->raw.data; } int git_blob_rawsize(git_blob *blob) { assert(blob); - - if (blob->content.data != NULL) - return blob->content.len; - - return blob->object.source.raw.len; + return blob->odb_object->raw.len; } void git_blob__free(git_blob *blob) { - gitfo_free_buf(&blob->content); + git_odb_object_close(blob->odb_object); free(blob); } -int git_blob__parse(git_blob *blob) +int git_blob__parse(git_blob *blob, git_odb_object *odb_obj) { assert(blob); + git_cached_obj_incref((git_cached_obj *)odb_obj); + blob->odb_object = odb_obj; return GIT_SUCCESS; } -int git_blob__writeback(git_blob *blob, git_odb_source *src) -{ - assert(blob->object.modified); - - if (blob->content.data == NULL) - return GIT_EMISSINGOBJDATA; - - return git__source_write(src, blob->content.data, blob->content.len); -} - -int git_blob_set_rawcontent(git_blob *blob, const void *buffer, size_t len) +int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len) { - assert(blob && buffer); - - blob->object.modified = 1; - - git_object__source_close((git_object *)blob); - - if (blob->content.data != NULL) - gitfo_free_buf(&blob->content); + int error; + git_odb_stream *stream; - blob->content.data = git__malloc(len); - blob->content.len = len; + if ((error = git_odb_open_wstream(&stream, repo->db, len, GIT_OBJ_BLOB)) < GIT_SUCCESS) + return error; - if (blob->content.data == NULL) - return GIT_ENOMEM; + stream->write(stream, buffer, len); - memcpy(blob->content.data, buffer, len); + error = stream->finalize_write(oid, stream); + stream->free(stream); - return GIT_SUCCESS; + return error; } -int git_blob_set_rawcontent_fromfile(git_blob *blob, const char *filename) +int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path) { - assert(blob && filename); - blob->object.modified = 1; + int error, fd; + char full_path[GIT_PATH_MAX]; + char buffer[2048]; + git_off_t size; + git_odb_stream *stream; - if (blob->content.data != NULL) - gitfo_free_buf(&blob->content); - - return gitfo_read_file(&blob->content, filename); -} + if (repo->path_workdir == NULL) + return GIT_ENOTFOUND; -int git_blob_writefile(git_oid *written_id, git_repository *repo, const char *path) -{ - int error; - git_blob *blob; + git__joinpath(full_path, repo->path_workdir, path); - if (gitfo_exists(path) < 0) + if ((fd = gitfo_open(full_path, O_RDONLY)) < 0) return GIT_ENOTFOUND; - if ((error = git_blob_new(&blob, repo)) < GIT_SUCCESS) - return error; + if ((size = gitfo_size(fd)) < 0 || !git__is_sizet(size)) { + gitfo_close(fd); + return GIT_EOSERR; + } - if ((error = git_blob_set_rawcontent_fromfile(blob, path)) < GIT_SUCCESS) + if ((error = git_odb_open_wstream(&stream, repo->db, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS) { + gitfo_close(fd); return error; + } - if ((error = git_object_write((git_object *)blob)) < GIT_SUCCESS) - return error; + while (size > 0) { + ssize_t read_len; - git_oid_cpy(written_id, git_object_id((git_object *)blob)); + read_len = read(fd, buffer, sizeof(buffer)); - git_object_close((git_object*)blob); - return GIT_SUCCESS; + if (read_len < 0) { + gitfo_close(fd); + stream->free(stream); + return GIT_EOSERR; + } + + stream->write(stream, buffer, read_len); + size -= read_len; + } + + error = stream->finalize_write(oid, stream); + stream->free(stream); + + return error; } diff --git a/src/blob.h b/src/blob.h index febc296fe..4300d7e54 100644 --- a/src/blob.h +++ b/src/blob.h @@ -3,15 +3,15 @@ #include "git2/blob.h" #include "repository.h" +#include "odb.h" #include "fileops.h" struct git_blob { git_object object; - gitfo_buf content; + git_odb_object *odb_object; }; void git_blob__free(git_blob *blob); -int git_blob__parse(git_blob *blob); -int git_blob__writeback(git_blob *blob, git_odb_source *src); +int git_blob__parse(git_blob *blob, git_odb_object *obj); #endif diff --git a/src/cache.c b/src/cache.c index 5a9b8415c..fdf5c1b80 100644 --- a/src/cache.c +++ b/src/cache.c @@ -32,23 +32,6 @@ #define GIT_CACHE_OPENADR 3 -GIT_INLINE(int) cached_obj_compare(git_cached_obj *obj, const git_oid *oid) -{ - return git_oid_cmp(&obj->oid, oid); -} - -GIT_INLINE(void) cached_obj_incref(git_cached_obj *obj) -{ - git_atomic_inc(&obj->refcount); -} - -GIT_INLINE(void) cached_obj_decref(git_cached_obj *obj, git_cached_obj_freeptr free_obj) -{ - if (git_atomic_dec(&obj->refcount) == 0) - free_obj(obj); -} - - void git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr) { size_t i; @@ -82,7 +65,9 @@ void git_cache_free(git_cache *cache) size_t i; for (i = 0; i < (cache->size_mask + 1); ++i) { - cached_obj_decref(cache->nodes[i].ptr, cache->free_obj); + if (cache->nodes[i].ptr) + git_cached_obj_decref(cache->nodes[i].ptr, cache->free_obj); + git_mutex_free(&cache->nodes[i].lock); } @@ -103,8 +88,8 @@ void *git_cache_get(git_cache *cache, const git_oid *oid) git_mutex_lock(&node->lock); { - if (cached_obj_compare(node->ptr, oid) == 0) { - cached_obj_incref(node->ptr); + if (node->ptr && git_cached_obj_compare(node->ptr, oid) == 0) { + git_cached_obj_incref(node->ptr); node->lru = ++cache->lru_count; found = 1; } @@ -121,14 +106,14 @@ void *git_cache_try_store(git_cache *cache, void *entry) cache_node *nodes[GIT_CACHE_OPENADR], *lru_node; const uint32_t *hash; const git_oid *oid; - size_t i, stored = 0; + size_t i; oid = &((git_cached_obj*)entry)->oid; hash = (const uint32_t *)oid->id; /* increase the refcount on this object, because * the cache now owns it */ - cached_obj_incref(entry); + git_cached_obj_incref(entry); for (i = 0; i < GIT_CACHE_OPENADR; ++i) { size_t pos = hash[i] & cache->size_mask; @@ -139,34 +124,35 @@ void *git_cache_try_store(git_cache *cache, void *entry) lru_node = nodes[0]; - for (i = 0; !stored && entry && i < GIT_CACHE_OPENADR; ++i) { + for (i = 0; i < GIT_CACHE_OPENADR; ++i) { if (nodes[i]->ptr == NULL) { nodes[i]->ptr = entry; nodes[i]->lru = ++cache->lru_count; - stored = 1; - } else if (cached_obj_compare(nodes[i]->ptr, oid) == 0) { - cached_obj_decref(entry, cache->free_obj); + break; + } else if (git_cached_obj_compare(nodes[i]->ptr, oid) == 0) { + git_cached_obj_decref(entry, cache->free_obj); entry = nodes[i]->ptr; - stored = 1; + nodes[i]->lru = ++cache->lru_count; + break; } if (nodes[i]->lru < lru_node->lru) lru_node = nodes[i]; } - if (!stored) { + if (i == GIT_CACHE_OPENADR) { void *old_entry = lru_node->ptr; assert(old_entry); - cached_obj_decref(old_entry, cache->free_obj); + git_cached_obj_decref(old_entry, cache->free_obj); lru_node->ptr = entry; lru_node->lru = ++cache->lru_count; } /* increase the refcount again, because we are * returning it to the user */ - cached_obj_incref(entry); + git_cached_obj_incref(entry); for (i = 0; i < GIT_CACHE_OPENADR; ++i) git_mutex_unlock(&nodes[i]->lock); diff --git a/src/cache.h b/src/cache.h index 9f525e68c..975aaff7e 100644 --- a/src/cache.h +++ b/src/cache.h @@ -7,6 +7,8 @@ #include "thread-utils.h" +#define GIT_DEFAULT_CACHE_SIZE 128 + typedef void (*git_cached_obj_freeptr)(void *); typedef struct { @@ -35,4 +37,23 @@ void git_cache_free(git_cache *cache); void *git_cache_try_store(git_cache *cache, void *entry); void *git_cache_get(git_cache *cache, const git_oid *oid); + +GIT_INLINE(int) git_cached_obj_compare(git_cached_obj *obj, const git_oid *oid) +{ + return git_oid_cmp(&obj->oid, oid); +} + +GIT_INLINE(void) git_cached_obj_incref(git_cached_obj *obj) +{ + git_atomic_inc(&obj->refcount); +} + +GIT_INLINE(void) git_cached_obj_decref(git_cached_obj *obj, git_cached_obj_freeptr free_obj) +{ + if (git_atomic_dec(&obj->refcount) == 0) + free_obj(obj); +} + + + #endif diff --git a/src/commit.c b/src/commit.c index 8da170cad..2670c2e0a 100644 --- a/src/commit.c +++ b/src/commit.c @@ -29,9 +29,12 @@ #include "git2/signature.h" #include "common.h" +#include "odb.h" #include "commit.h" #include "signature.h" +#include <stdarg.h> + #define COMMIT_BASIC_PARSE 0x0 #define COMMIT_FULL_PARSE 0x1 @@ -71,35 +74,165 @@ const git_oid *git_commit_id(git_commit *c) return git_object_id((git_object *)c); } -int git_commit__writeback(git_commit *commit, git_odb_source *src) + +int git_commit_create_v( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message, + const git_oid *tree_oid, + int parent_count, + ...) { - unsigned int i; + va_list ap; + int i, error; + const git_oid **oids; - git__write_oid(src, "tree", &commit->tree_oid); + oids = git__malloc(parent_count * sizeof(git_oid *)); - for (i = 0; i < commit->parent_oids.length; ++i) { - git_oid *parent_oid; + va_start(ap, parent_count); + for (i = 0; i < parent_count; ++i) + oids[i] = va_arg(ap, const git_oid *); + va_end(ap); - parent_oid = git_vector_get(&commit->parent_oids, i); - git__write_oid(src, "parent", parent_oid); - } + error = git_commit_create( + oid, repo, update_ref, author, committer, message, + tree_oid, parent_count, oids); + + free(oids); + return error; +} + +int git_commit_create_ov( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message, + const git_tree *tree, + int parent_count, + ...) +{ + va_list ap; + int i, error; + const git_oid **oids; + + oids = git__malloc(parent_count * sizeof(git_oid *)); + + va_start(ap, parent_count); + for (i = 0; i < parent_count; ++i) + oids[i] = git_object_id(va_arg(ap, const git_object *)); + va_end(ap); + + error = git_commit_create( + oid, repo, update_ref, author, committer, message, + git_object_id((git_object *)tree), + parent_count, oids); + + free(oids); + return error; +} + +int git_commit_create_o( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message, + const git_tree *tree, + int parent_count, + const git_commit *parents[]) +{ + int i, error; + const git_oid **oids; + + oids = git__malloc(parent_count * sizeof(git_oid *)); + + for (i = 0; i < parent_count; ++i) + oids[i] = git_object_id((git_object *)parents[i]); + + error = git_commit_create( + oid, repo, update_ref, author, committer, message, + git_object_id((git_object *)tree), + parent_count, oids); + + free(oids); + return error; +} - if (commit->author == NULL) - return GIT_EMISSINGOBJDATA; +int git_commit_create( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message, + const git_oid *tree_oid, + int parent_count, + const git_oid *parents[]) +{ + size_t final_size = 0; + int message_length, author_length, committer_length; + + char *author_str, *committer_str; + + int error, i; + git_odb_stream *stream; + + message_length = strlen(message); + author_length = git_signature__write(&author_str, "author", author); + committer_length = git_signature__write(&committer_str, "committer", committer); + + if (author_length < 0 || committer_length < 0) + return GIT_ENOMEM; + + final_size += GIT_OID_LINE_LENGTH("tree"); + final_size += GIT_OID_LINE_LENGTH("parent") * parent_count; + final_size += author_length; + final_size += committer_length; + final_size += 1 + message_length; + + if ((error = git_odb_open_wstream(&stream, repo->db, final_size, GIT_OBJ_COMMIT)) < GIT_SUCCESS) + return error; + + git__write_oid(stream, "tree", tree_oid); - git_signature__write(src, "author", commit->author); + for (i = 0; i < parent_count; ++i) + git__write_oid(stream, "parent", parents[i]); - if (commit->committer == NULL) - return GIT_EMISSINGOBJDATA; + stream->write(stream, author_str, author_length); + free(author_str); - git_signature__write(src, "committer", commit->committer); + stream->write(stream, committer_str, committer_length); + free(committer_str); - if (commit->message != NULL) { - git__source_write(src, "\n", 1); - git__source_write(src, commit->message, strlen(commit->message)); + + stream->write(stream, "\n", 1); + stream->write(stream, message, message_length); + + error = stream->finalize_write(oid, stream); + stream->free(stream); + + if (error == GIT_SUCCESS && update_ref != NULL) { + git_reference *head; + + error = git_reference_lookup(&head, repo, update_ref); + if (error < GIT_SUCCESS) + return error; + + if (git_reference_type(head) == GIT_REF_SYMBOLIC) { + if ((error = git_reference_resolve(&head, head)) < GIT_SUCCESS) + return error; + } + + error = git_reference_set_oid(head, oid); } - return GIT_SUCCESS; + return error; } int commit_parse_buffer(git_commit *commit, void *data, size_t len) @@ -110,12 +243,7 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len) git_oid parent_oid; int error; - /* first parse; the vector hasn't been initialized yet */ - if (commit->parent_oids.contents == NULL) { - git_vector_init(&commit->parent_oids, 4, NULL); - } - - clear_parents(commit); + git_vector_init(&commit->parent_oids, 4, NULL); if ((error = git__parse_oid(&commit->tree_oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS) return error; @@ -134,17 +262,11 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len) return GIT_ENOMEM; } - if (commit->author) - git_signature_free(commit->author); - commit->author = git__malloc(sizeof(git_signature)); if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ")) < GIT_SUCCESS) return error; /* Always parse the committer; we need the commit time */ - if (commit->committer) - git_signature_free(commit->committer); - commit->committer = git__malloc(sizeof(git_signature)); if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ")) < GIT_SUCCESS) return error; @@ -176,11 +298,10 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len) return GIT_SUCCESS; } -int git_commit__parse(git_commit *commit) +int git_commit__parse(git_commit *commit, git_odb_object *obj) { - assert(commit && commit->object.source.open); - return commit_parse_buffer(commit, - commit->object.source.raw.data, commit->object.source.raw.len); + assert(commit); + return commit_parse_buffer(commit, obj->raw.data, obj->raw.len); } #define GIT_COMMIT_GETTER(_rvalue, _name, _return) \ @@ -218,82 +339,3 @@ int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n) } - -int git_commit_set_tree(git_commit *commit, git_tree *tree) -{ - const git_oid *oid; - - assert(commit && tree); - - if ((oid = git_object_id((git_object *)tree)) == NULL) - return GIT_EMISSINGOBJDATA; - - commit->object.modified = 1; - git_oid_cpy(&commit->tree_oid, oid); - return GIT_SUCCESS; -} - -int git_commit_add_parent(git_commit *commit, git_commit *new_parent) -{ - const git_oid *parent_oid; - git_oid *new_oid; - assert(commit && new_parent); - - if ((parent_oid = git_object_id((git_object *)new_parent)) == NULL) - return GIT_EMISSINGOBJDATA; - - new_oid = git__malloc(sizeof(git_oid)); - if (new_oid == NULL) - return GIT_ENOMEM; - - commit->object.modified = 1; - git_oid_cpy(new_oid, parent_oid); - return git_vector_insert(&commit->parent_oids, new_oid); -} - -void git_commit_set_author(git_commit *commit, const git_signature *author_sig) -{ - assert(commit && author_sig); - commit->object.modified = 1; - - git_signature_free(commit->author); - commit->author = git_signature_dup(author_sig); -} - -void git_commit_set_committer(git_commit *commit, const git_signature *committer_sig) -{ - assert(commit && committer_sig); - commit->object.modified = 1; - - git_signature_free(commit->committer); - commit->committer = git_signature_dup(committer_sig); -} - -void git_commit_set_message(git_commit *commit, const char *message) -{ - const char *line_end; - size_t message_len; - - commit->object.modified = 1; - - if (commit->message) - free(commit->message); - - if (commit->message_short) - free(commit->message_short); - - commit->message = git__strdup(message); - - /* Short message */ - if((line_end = strchr(message, '\n')) == NULL) { - commit->message_short = git__strdup(message); - return; - } - - message_len = line_end - message; - - commit->message_short = git__malloc(message_len + 1); - memcpy(commit->message_short, message, message_len); - commit->message_short[message_len] = 0; -} - diff --git a/src/commit.h b/src/commit.h index aaf349ca6..3d15c5044 100644 --- a/src/commit.h +++ b/src/commit.h @@ -22,8 +22,6 @@ struct git_commit { }; void git_commit__free(git_commit *c); -int git_commit__parse(git_commit *commit); - -int git_commit__writeback(git_commit *commit, git_odb_source *src); +int git_commit__parse(git_commit *commit, git_odb_object *obj); #endif diff --git a/src/delta-apply.h b/src/delta-apply.h index 642442de0..36c5cc60d 100644 --- a/src/delta-apply.h +++ b/src/delta-apply.h @@ -1,6 +1,8 @@ #ifndef INCLUDE_delta_apply_h__ #define INCLUDE_delta_apply_h__ +#include "odb.h" + /** * Apply a git binary delta to recover the original content. * diff --git a/src/errors.c b/src/errors.c index 880163f78..f6b964837 100644 --- a/src/errors.c +++ b/src/errors.c @@ -27,7 +27,8 @@ static struct { {GIT_EPACKEDREFSCORRUPTED, "The pack-refs file is either corrupted of its format is not currently supported"}, {GIT_EINVALIDPATH, "The path is invalid" }, {GIT_EREVWALKOVER, "The revision walker is empty; there are no more commits left to iterate"}, - {GIT_EINVALIDREFSTATE, "The state of the reference is not valid"} + {GIT_EINVALIDREFSTATE, "The state of the reference is not valid"}, + {GIT_ENOTIMPLEMENTED, "This feature has not been implemented yet"} }; const char *git_strerror(int num) diff --git a/src/filebuf.c b/src/filebuf.c index 4fc4f1486..607ad618d 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -77,43 +77,81 @@ void git_filebuf_cleanup(git_filebuf *file) if (file->fd >= 0) gitfo_close(file->fd); - if (gitfo_exists(file->path_lock) == GIT_SUCCESS) + if (file->path_lock && gitfo_exists(file->path_lock) == GIT_SUCCESS) gitfo_unlink(file->path_lock); if (file->digest) git_hash_free_ctx(file->digest); free(file->buffer); + free(file->z_buf); -#ifdef GIT_FILEBUF_THREADS - free(file->buffer_back); -#endif + deflateEnd(&file->zs); free(file->path_original); free(file->path_lock); } -static int flush_buffer(git_filebuf *file) +GIT_INLINE(int) flush_buffer(git_filebuf *file) { - int result = GIT_SUCCESS; + int result = file->write(file, file->buffer, file->buf_pos); + file->buf_pos = 0; + return result; +} - if (file->buf_pos > 0) { - result = gitfo_write(file->fd, file->buffer, file->buf_pos); - if (file->digest) - git_hash_update(file->digest, file->buffer, file->buf_pos); +static int write_normal(git_filebuf *file, const void *source, size_t len) +{ + int result = 0; - file->buf_pos = 0; + if (len > 0) { + result = gitfo_write(file->fd, (void *)source, len); + if (file->digest) + git_hash_update(file->digest, source, len); } return result; } +static int write_deflate(git_filebuf *file, const void *source, size_t len) +{ + int result = Z_OK; + z_stream *zs = &file->zs; + + if (len > 0 || file->flush_mode == Z_FINISH) { + zs->next_in = (void *)source; + zs->avail_in = len; + + do { + int have; + + zs->next_out = file->z_buf; + zs->avail_out = file->buf_size; + + result = deflate(zs, file->flush_mode); + assert(result != Z_STREAM_ERROR); + + have = file->buf_size - zs->avail_out; + + if (gitfo_write(file->fd, file->z_buf, have) < GIT_SUCCESS) + return GIT_EOSERR; + + } while (zs->avail_out == 0); + + assert(zs->avail_in == 0); + + if (file->digest) + git_hash_update(file->digest, source, len); + } + + return GIT_SUCCESS; +} + int git_filebuf_open(git_filebuf *file, const char *path, int flags) { int error; size_t path_len; - if (file == NULL || path == NULL) + if (file == NULL) return GIT_ERROR; memset(file, 0x0, sizeof(git_filebuf)); @@ -122,46 +160,93 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags) file->buf_pos = 0; file->fd = -1; - path_len = strlen(path); - - file->path_original = git__strdup(path); - if (file->path_original == NULL) { + /* Allocate the main cache buffer */ + file->buffer = git__malloc(file->buf_size); + if (file->buffer == NULL){ error = GIT_ENOMEM; goto cleanup; } - file->path_lock = git__malloc(path_len + GIT_FILELOCK_EXTLENGTH); - if (file->path_lock == NULL) { - error = GIT_ENOMEM; - goto cleanup; + /* If we are hashing on-write, allocate a new hash context */ + if (flags & GIT_FILEBUF_HASH_CONTENTS) { + if ((file->digest = git_hash_new_ctx()) == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } } - memcpy(file->path_lock, file->path_original, path_len); - memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH); + /* If we are deflating on-write, */ + if (flags & GIT_FILEBUF_DEFLATE_CONTENTS) { - file->buffer = git__malloc(file->buf_size); - if (file->buffer == NULL){ - error = GIT_ENOMEM; - goto cleanup; - } + /* Initialize the ZLib stream */ + if (deflateInit(&file->zs, Z_DEFAULT_COMPRESSION) != Z_OK) { + error = GIT_EZLIB; + goto cleanup; + } -#ifdef GIT_FILEBUF_THREADS - file->buffer_back = git__malloc(file->buf_size); - if (file->buffer_back == NULL){ - error = GIT_ENOMEM; - goto cleanup; + /* Allocate the Zlib cache buffer */ + file->z_buf = git__malloc(file->buf_size); + if (file->z_buf == NULL){ + error = GIT_ENOMEM; + goto cleanup; + } + + /* Never flush */ + file->flush_mode = Z_NO_FLUSH; + file->write = &write_deflate; + } else { + file->write = &write_normal; } -#endif - if (flags & GIT_FILEBUF_HASH_CONTENTS) { - if ((file->digest = git_hash_new_ctx()) == NULL) { + /* If we are writing to a temp file */ + if (flags & GIT_FILEBUF_TEMPORARY) { + char tmp_path[GIT_PATH_MAX]; + + /* Open the file as temporary for locking */ + file->fd = gitfo_creat_tmp(tmp_path, "_filebuf_"); + if (file->fd < 0) { + error = GIT_EOSERR; + goto cleanup; + } + + /* No original path */ + file->path_original = NULL; + file->path_lock = git__strdup(tmp_path); + + if (file->path_lock == NULL) { error = GIT_ENOMEM; goto cleanup; } - } + } else { + /* If the file is not temporary, make sure we have a path */ + if (path == NULL) { + error = GIT_ERROR; + goto cleanup; + } - if ((error = lock_file(file, flags)) < GIT_SUCCESS) - goto cleanup; + path_len = strlen(path); + + /* Save the original path of the file */ + file->path_original = git__strdup(path); + if (file->path_original == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } + + /* create the locking path by appending ".lock" to the original */ + file->path_lock = git__malloc(path_len + GIT_FILELOCK_EXTLENGTH); + if (file->path_lock == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } + + memcpy(file->path_lock, file->path_original, path_len); + memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH); + + /* open the file for locking */ + if ((error = lock_file(file, flags)) < GIT_SUCCESS) + goto cleanup; + } return GIT_SUCCESS; @@ -187,10 +272,25 @@ int git_filebuf_hash(git_oid *oid, git_filebuf *file) return GIT_SUCCESS; } +int git_filebuf_commit_at(git_filebuf *file, const char *path) +{ + free(file->path_original); + file->path_original = git__strdup(path); + if (file->path_original == NULL) + return GIT_ENOMEM; + + return git_filebuf_commit(file); +} + int git_filebuf_commit(git_filebuf *file) { int error; + /* tmp file cannot be commited */ + if (file->path_original == NULL) + return GIT_EOSERR; + + file->flush_mode = Z_FINISH; if ((error = flush_buffer(file)) < GIT_SUCCESS) goto cleanup; @@ -204,16 +304,16 @@ cleanup: return error; } -GIT_INLINE(void) add_to_cache(git_filebuf *file, void *buf, size_t len) +GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len) { memcpy(file->buffer + file->buf_pos, buf, len); file->buf_pos += len; } -int git_filebuf_write(git_filebuf *file, void *buff, size_t len) +int git_filebuf_write(git_filebuf *file, const void *buff, size_t len) { int error; - unsigned char *buf = buff; + const unsigned char *buf = buff; for (;;) { size_t space_left = file->buf_size - file->buf_pos; @@ -237,9 +337,9 @@ int git_filebuf_write(git_filebuf *file, void *buff, size_t len) /* write too-large chunks immediately */ if (len > file->buf_size) { - error = gitfo_write(file->fd, buf, len); - if (file->digest) - git_hash_update(file->digest, buf, len); + error = file->write(file, buf, len); + if (error < GIT_SUCCESS) + return error; } } } diff --git a/src/filebuf.h b/src/filebuf.h index 9db615fbd..37cb36784 100644 --- a/src/filebuf.h +++ b/src/filebuf.h @@ -3,14 +3,17 @@ #include "fileops.h" #include "hash.h" +#include "git2/zlib.h" #ifdef GIT_THREADS # define GIT_FILEBUF_THREADS #endif -#define GIT_FILEBUF_HASH_CONTENTS 0x1 -#define GIT_FILEBUF_APPEND 0x2 -#define GIT_FILEBUF_FORCE 0x4 +#define GIT_FILEBUF_HASH_CONTENTS (1 << 0) +#define GIT_FILEBUF_APPEND (1 << 2) +#define GIT_FILEBUF_FORCE (1 << 3) +#define GIT_FILEBUF_TEMPORARY (1 << 4) +#define GIT_FILEBUF_DEFLATE_CONTENTS (1 << 5) #define GIT_FILELOCK_EXTENSION ".lock\0" #define GIT_FILELOCK_EXTLENGTH 6 @@ -19,12 +22,16 @@ struct git_filebuf { char *path_original; char *path_lock; + int (*write)(struct git_filebuf *file, + const void *source, size_t len); + git_hash_ctx *digest; unsigned char *buffer; -#ifdef GIT_FILEBUF_THREADS - unsigned char *buffer_back; -#endif + unsigned char *z_buf; + + z_stream zs; + int flush_mode; size_t buf_size, buf_pos; git_file fd; @@ -32,12 +39,13 @@ struct git_filebuf { typedef struct git_filebuf git_filebuf; -int git_filebuf_write(git_filebuf *lock, void *buff, size_t len); +int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len); int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len); int git_filebuf_printf(git_filebuf *file, const char *format, ...); int git_filebuf_open(git_filebuf *lock, const char *path, int flags); int git_filebuf_commit(git_filebuf *lock); +int git_filebuf_commit_at(git_filebuf *lock, const char *path); void git_filebuf_cleanup(git_filebuf *lock); int git_filebuf_hash(git_oid *oid, git_filebuf *file); diff --git a/src/fileops.c b/src/fileops.c index 76e689e8a..237a16a9f 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -2,13 +2,13 @@ #include "fileops.h" #include <ctype.h> -static int force_path(const char *to) +int gitfo_mkdir_2file(const char *file_path) { const int mode = 0755; /* or 0777 ? */ int error = GIT_SUCCESS; char target_folder_path[GIT_PATH_MAX]; - error = git__dirname_r(target_folder_path, sizeof(target_folder_path), to); + error = git__dirname_r(target_folder_path, sizeof(target_folder_path), file_path); if (error < GIT_SUCCESS) return error; @@ -25,6 +25,87 @@ static int force_path(const char *to) return GIT_SUCCESS; } +static int creat_tempfile(char *path_out, const char *tmp_dir, const char *filename) +{ + int fd; + + git__joinpath(path_out, tmp_dir, filename); + strcat(path_out, "_git2_XXXXXX"); + +#ifdef GIT_WIN32 + /* FIXME: there may be race conditions when multi-threading + * with the library */ + if (_mktemp_s(path_out, GIT_PATH_MAX) != 0) + return GIT_EOSERR; + + fd = gitfo_creat(path_out, 0744); +#else + fd = mkstemp(path_out); +#endif + + return fd >= 0 ? fd : GIT_EOSERR; +} + +static const char *find_tmpdir(void) +{ + static int tmpdir_not_found = 0; + static char temp_dir[GIT_PATH_MAX]; + static const char *env_vars[] = { + "TEMP", "TMP", "TMPDIR" + }; + + unsigned int i, j; + char test_file[GIT_PATH_MAX]; + + if (tmpdir_not_found) + return NULL; + + if (temp_dir[0] != '\0') + return temp_dir; + + for (i = 0; i < ARRAY_SIZE(env_vars); ++i) { + char *env_path; + + env_path = getenv(env_vars[i]); + if (env_path == NULL) + continue; + + strcpy(temp_dir, env_path); + + /* Fix backslashes because Windows environment vars + * are probably fucked up */ + for (j = 0; j < strlen(temp_dir); ++j) + if (temp_dir[j] == '\\') + temp_dir[j] = '/'; + + if (creat_tempfile(test_file, temp_dir, "writetest") >= 0) { + gitfo_unlink(test_file); + return temp_dir; + } + } + + /* last resort: current folder. */ + strcpy(temp_dir, "./"); + if (creat_tempfile(test_file, temp_dir, "writetest") >= 0) { + gitfo_unlink(test_file); + return temp_dir; + } + + tmpdir_not_found = 1; + return NULL; +} + +int gitfo_creat_tmp(char *path_out, const char *filename) +{ + const char *tmp_dir; + + tmp_dir = find_tmpdir(); + if (tmp_dir == NULL) + return GIT_EOSERR; + + return creat_tempfile(path_out, tmp_dir, filename); +} + int gitfo_open(const char *path, int flags) { int fd = open(path, flags | O_BINARY); @@ -39,7 +120,7 @@ int gitfo_creat(const char *path, int mode) int gitfo_creat_force(const char *path, int mode) { - if (force_path(path) < GIT_SUCCESS) + if (gitfo_mkdir_2file(path) < GIT_SUCCESS) return GIT_EOSERR; return gitfo_creat(path, mode); @@ -117,6 +198,7 @@ int gitfo_isdir(const char *path) int gitfo_exists(const char *path) { + assert(path); return access(path, F_OK); } @@ -198,7 +280,7 @@ int gitfo_mv(const char *from, const char *to) int gitfo_mv_force(const char *from, const char *to) { - if (force_path(to) < GIT_SUCCESS) + if (gitfo_mkdir_2file(to) < GIT_SUCCESS) return GIT_EOSERR; return gitfo_mv(from, to); diff --git a/src/fileops.h b/src/fileops.h index 5aa302b54..bc636fc38 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -58,8 +58,10 @@ extern int gitfo_exists(const char *path); extern int gitfo_open(const char *path, int flags); extern int gitfo_creat(const char *path, int mode); extern int gitfo_creat_force(const char *path, int mode); +extern int gitfo_creat_tmp(char *path_out, const char *filename); extern int gitfo_isdir(const char *path); extern int gitfo_mkdir_recurs(const char *path, int mode); +extern int gitfo_mkdir_2file(const char *path); #define gitfo_close(fd) close(fd) extern int gitfo_read(git_file fd, void *buf, size_t cnt); diff --git a/src/index.c b/src/index.c index 95e56b7d5..6a355e11b 100644 --- a/src/index.c +++ b/src/index.c @@ -324,7 +324,7 @@ int git_index_add(git_index *index, const char *rel_path, int stage) entry.file_size = st.st_size; /* write the blob to disk and get the oid */ - if ((error = git_blob_writefile(&entry.oid, index->repository, full_path)) < GIT_SUCCESS) + if ((error = git_blob_create_fromfile(&entry.oid, index->repository, rel_path)) < GIT_SUCCESS) return error; entry.flags |= (stage << GIT_IDXENTRY_STAGESHIFT); diff --git a/src/object.c b/src/object.c index c432c6da0..0572663eb 100644 --- a/src/object.c +++ b/src/object.c @@ -66,153 +66,6 @@ static struct { { "REF_DELTA", 0, 0 } }; -/* - * Object source methods - * - * Abstract buffer methods that allow the writeback system - * to prepare the contents of any git file in-memory before - * writing them to disk. - */ -static int source_resize(git_odb_source *src) -{ - size_t write_offset, new_size; - void *new_data; - - write_offset = (size_t)((char *)src->write_ptr - (char *)src->raw.data); - - new_size = src->raw.len * 2; - if ((new_data = git__malloc(new_size)) == NULL) - return GIT_ENOMEM; - - memcpy(new_data, src->raw.data, src->written_bytes); - free(src->raw.data); - - src->raw.data = new_data; - src->raw.len = new_size; - src->write_ptr = (char *)new_data + write_offset; - - return GIT_SUCCESS; -} - -int git__source_printf(git_odb_source *source, const char *format, ...) -{ - va_list arglist; - int len; - - assert(source->open && source->write_ptr); - - va_start(arglist, format); - - len = vsnprintf(source->write_ptr, source->raw.len - source->written_bytes, format, arglist); - - while (source->written_bytes + len >= source->raw.len) { - if (source_resize(source) < GIT_SUCCESS) - return GIT_ENOMEM; - - len = vsnprintf(source->write_ptr, source->raw.len - source->written_bytes, format, arglist); - } - - source->write_ptr = (char *)source->write_ptr + len; - source->written_bytes += len; - - return GIT_SUCCESS; -} - -int git__source_write(git_odb_source *source, const void *bytes, size_t len) -{ - assert(source); - - assert(source->open && source->write_ptr); - - while (source->written_bytes + len >= source->raw.len) { - if (source_resize(source) < GIT_SUCCESS) - return GIT_ENOMEM; - } - - memcpy(source->write_ptr, bytes, len); - source->write_ptr = (char *)source->write_ptr + len; - source->written_bytes += len; - - return GIT_SUCCESS; -} - -static void prepare_write(git_object *object) -{ - if (object->source.write_ptr != NULL || object->source.open) - git_object__source_close(object); - - /* TODO: proper size calculation */ - object->source.raw.data = git__malloc(OBJECT_BASE_SIZE); - object->source.raw.len = OBJECT_BASE_SIZE; - - object->source.write_ptr = object->source.raw.data; - object->source.written_bytes = 0; - - object->source.open = 1; -} - -static int write_back(git_object *object) -{ - int error; - git_oid new_id; - - assert(object); - - assert(object->source.open); - assert(object->modified); - - object->source.raw.len = object->source.written_bytes; - - if ((error = git_odb_write(&new_id, object->repo->db, &object->source.raw)) < GIT_SUCCESS) - return error; - - 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); - } - - git_oid_cpy(&object->id, &new_id); - git_hashtable_insert(object->repo->objects, &object->id, object); - - object->source.write_ptr = NULL; - object->source.written_bytes = 0; - - object->modified = 0; - object->in_memory = 0; - - git_object__source_close(object); - return GIT_SUCCESS; -} - -int git_object__source_open(git_object *object) -{ - int error; - - assert(object && !object->in_memory); - - if (object->source.open) - git_object__source_close(object); - - error = git_odb_read(&object->source.raw, object->repo->db, &object->id); - if (error < GIT_SUCCESS) - return error; - - object->source.open = 1; - return GIT_SUCCESS; -} - -void git_object__source_close(git_object *object) -{ - assert(object); - - if (object->source.open) { - git_rawobj_close(&object->source.raw); - object->source.open = 0; - } -} - static int create_object(git_object **object_out, git_otype type) { git_object *object = NULL; @@ -225,43 +78,19 @@ static int create_object(git_object **object_out, git_otype type) case GIT_OBJ_COMMIT: case GIT_OBJ_TAG: case GIT_OBJ_BLOB: + case GIT_OBJ_TREE: object = git__malloc(git_object__size(type)); if (object == NULL) return GIT_ENOMEM; memset(object, 0x0, git_object__size(type)); break; - - case GIT_OBJ_TREE: - object = (git_object *)git_tree__new(); - if (object == NULL) - return GIT_ENOMEM; - break; default: return GIT_EINVALIDTYPE; } - *object_out = object; - return GIT_SUCCESS; -} - -int git_object_new(git_object **object_out, git_repository *repo, git_otype type) -{ - git_object *object = NULL; - int error; - - assert(object_out && repo); - - if ((error = create_object(&object, type)) < GIT_SUCCESS) - return error; - - object->repo = repo; - object->in_memory = 1; - object->modified = 1; - - object->source.raw.type = type; + object->type = type; - object->lru = ++repo->lru_counter; *object_out = object; return GIT_SUCCESS; } @@ -269,126 +98,77 @@ int git_object_new(git_object **object_out, git_repository *repo, git_otype type int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) { git_object *object = NULL; - git_rawobj obj_file; + git_odb_object *odb_obj; int error = GIT_SUCCESS; assert(repo && object_out && id); - object = git_hashtable_lookup(repo->objects, id); + object = git_cache_get(&repo->objects, id); if (object != NULL) { - if (type != GIT_OBJ_ANY && type != object->source.raw.type) + if (type != GIT_OBJ_ANY && type != object->type) return GIT_EINVALIDTYPE; *object_out = object; - object->lru = ++repo->lru_counter; - object->can_free = 0; return GIT_SUCCESS; } - error = git_odb_read(&obj_file, repo->db, id); + error = git_odb_read(&odb_obj, repo->db, id); if (error < GIT_SUCCESS) return error; - if (type != GIT_OBJ_ANY && type != obj_file.type) { - git_rawobj_close(&obj_file); + if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) { + git_odb_object_close(odb_obj); return GIT_EINVALIDTYPE; } - type = obj_file.type; + type = odb_obj->raw.type; if ((error = create_object(&object, type)) < GIT_SUCCESS) return error; /* Initialize parent object */ - git_oid_cpy(&object->id, id); + git_oid_cpy(&object->cached.oid, id); object->repo = repo; - memcpy(&object->source.raw, &obj_file, sizeof(git_rawobj)); - object->source.open = 1; switch (type) { case GIT_OBJ_COMMIT: - error = git_commit__parse((git_commit *)object); + error = git_commit__parse((git_commit *)object, odb_obj); break; case GIT_OBJ_TREE: - error = git_tree__parse((git_tree *)object); + error = git_tree__parse((git_tree *)object, odb_obj); break; case GIT_OBJ_TAG: - error = git_tag__parse((git_tag *)object); + error = git_tag__parse((git_tag *)object, odb_obj); break; case GIT_OBJ_BLOB: - error = git_blob__parse((git_blob *)object); + error = git_blob__parse((git_blob *)object, odb_obj); break; default: break; } + git_odb_object_close(odb_obj); + if (error < GIT_SUCCESS) { git_object__free(object); return error; } - git_object__source_close(object); - git_hashtable_insert(repo->objects, &object->id, object); - - object->lru = ++repo->lru_counter; - *object_out = object; + *object_out = git_cache_try_store(&repo->objects, object); return GIT_SUCCESS; } -int git_object_write(git_object *object) +void git_object__free(void *_obj) { - int error; - git_odb_source *source; + git_object *object = (git_object *)_obj; assert(object); - if (object->modified == 0) - return GIT_SUCCESS; - - prepare_write(object); - source = &object->source; - - switch (source->raw.type) { - case GIT_OBJ_COMMIT: - error = git_commit__writeback((git_commit *)object, source); - break; - - case GIT_OBJ_TREE: - error = git_tree__writeback((git_tree *)object, source); - break; - - case GIT_OBJ_TAG: - error = git_tag__writeback((git_tag *)object, source); - break; - - case GIT_OBJ_BLOB: - error = git_blob__writeback((git_blob *)object, source); - break; - - default: - error = GIT_ERROR; - break; - } - - if (error < GIT_SUCCESS) { - git_object__source_close(object); - return error; - } - - return write_back(object); -} - -void git_object__free(git_object *object) -{ - assert(object); - - git_object__source_close(object); - - switch (object->source.raw.type) { + switch (object->type) { case GIT_OBJ_COMMIT: git_commit__free((git_commit *)object); break; @@ -416,29 +196,19 @@ void git_object_close(git_object *object) if (object == NULL) return; - if (object->in_memory) { - int idx = git_vector_search(&object->repo->memory_objects, object); - git_vector_remove(&object->repo->memory_objects, idx); - git_object__free(object); - } else { - object->can_free = 1; - } + git_cached_obj_decref((git_cached_obj *)object, git_object__free); } const git_oid *git_object_id(const git_object *obj) { assert(obj); - - if (obj->in_memory) - return NULL; - - return &obj->id; + return &obj->cached.oid; } git_otype git_object_type(const git_object *obj) { assert(obj); - return obj->source.raw.type; + return obj->type; } git_repository *git_object_owner(const git_object *obj) @@ -87,56 +87,49 @@ int git_odb__hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_rawobj *ob return GIT_SUCCESS; } -void git_rawobj_close(git_rawobj *obj) -{ - free(obj->data); - obj->data = NULL; -} -int git_rawobj_hash(git_oid *id, git_rawobj *obj) +static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source) { - char hdr[64]; - int hdrlen; + git_odb_object *object = git__malloc(sizeof(git_odb_object)); + memset(object, 0x0, sizeof(git_odb_object)); - assert(id && obj); + git_oid_cpy(&object->cached.oid, oid); + memcpy(&object->raw, source, sizeof(git_rawobj)); - return git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, obj); + return object; } -int git_odb__inflate_buffer(void *in, size_t inlen, void *out, size_t outlen) +static void free_odb_object(void *o) { - z_stream zs; - int status = Z_OK; - - memset(&zs, 0x0, sizeof(zs)); - - zs.next_out = out; - zs.avail_out = outlen; - - zs.next_in = in; - zs.avail_in = inlen; + git_odb_object *object = (git_odb_object *)o; - if (inflateInit(&zs) < Z_OK) - return GIT_ERROR; + if (object != NULL) { + free(object->raw.data); + free(object); + } +} - while (status == Z_OK) - status = inflate(&zs, Z_FINISH); +void git_odb_object_close(git_odb_object *object) +{ + git_cached_obj_decref((git_cached_obj *)object, &free_odb_object); +} - inflateEnd(&zs); +int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type) +{ + char hdr[64]; + int hdrlen; + git_rawobj raw; - if ((status != Z_STREAM_END) /*|| (zs.avail_in != 0) */) - return GIT_ERROR; + assert(id); - if (zs.total_out != outlen) - return GIT_ERROR; + raw.data = (void *)data; + raw.len = len; + raw.type = type; - return GIT_SUCCESS; + return git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, &raw); } - - - /*********************************************************** * * OBJECT DATABASE PUBLIC API @@ -162,6 +155,8 @@ int git_odb_new(git_odb **out) if (!db) return GIT_ENOMEM; + git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object); + if (git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) { free(db); return GIT_ENOMEM; @@ -306,16 +301,23 @@ void git_odb_close(git_odb *db) } git_vector_free(&db->backends); + git_cache_free(&db->cache); free(db); } int git_odb_exists(git_odb *db, const git_oid *id) { + git_odb_object *object; unsigned int i; int found = 0; assert(db && id); + if ((object = git_cache_get(&db->cache, id)) != NULL) { + git_odb_object_close(object); + return 1; + } + for (i = 0; i < db->backends.length && !found; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; @@ -327,19 +329,27 @@ int git_odb_exists(git_odb *db, const git_oid *id) return found; } -int git_odb_read_header(git_rawobj *out, git_odb *db, const git_oid *id) +int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id) { unsigned int i; int error = GIT_ENOTFOUND; + git_odb_object *object; - assert(out && db && id); + assert(db && id); + + if ((object = git_cache_get(&db->cache, id)) != NULL) { + *len_p = object->raw.len; + *type_p = object->raw.type; + git_odb_object_close(object); + return GIT_SUCCESS; + } for (i = 0; i < db->backends.length && error < 0; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (b->read_header != NULL) - error = b->read_header(out, b, id); + error = b->read_header(len_p, type_p, b, id); } /* @@ -347,37 +357,50 @@ int git_odb_read_header(git_rawobj *out, git_odb *db, const git_oid *id) * try reading the whole object and freeing the contents */ if (error < 0) { - error = git_odb_read(out, db, id); - git_rawobj_close(out); + if ((error = git_odb_read(&object, db, id)) < GIT_SUCCESS) + return error; + + *len_p = object->raw.len; + *type_p = object->raw.len; + git_odb_object_close(object); } - return error; + return GIT_SUCCESS; } -int git_odb_read(git_rawobj *out, git_odb *db, const git_oid *id) +int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) { unsigned int i; int error = GIT_ENOTFOUND; + git_rawobj raw; assert(out && db && id); + *out = git_cache_get(&db->cache, id); + if (*out != NULL) + return GIT_SUCCESS; + for (i = 0; i < db->backends.length && error < 0; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (b->read != NULL) - error = b->read(out, b, id); + error = b->read(&raw.data, &raw.len, &raw.type, b, id); + } + + if (error == GIT_SUCCESS) { + *out = git_cache_try_store(&db->cache, new_odb_object(id, &raw)); } return error; } -int git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj) +int git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_otype type) { unsigned int i; int error = GIT_ERROR; - assert(obj && db && id); + assert(stream && db); for (i = 0; i < db->backends.length && error < 0; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); @@ -387,8 +410,26 @@ int git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj) if (internal->is_alternate) continue; - if (b->write != NULL) - error = b->write(id, b, obj); + if (b->writestream != NULL) + error = b->writestream(stream, b, size, type); + } + + return error; +} + +int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid) +{ + unsigned int i; + int error = GIT_ERROR; + + assert(stream && db); + + for (i = 0; i < db->backends.length && error < 0; ++i) { + backend_internal *internal = git_vector_get(&db->backends, i); + git_odb_backend *b = internal->backend; + + if (b->readstream != NULL) + error = b->readstream(stream, b, oid); } return error; @@ -3,15 +3,31 @@ #include "git2/odb.h" #include "git2/oid.h" +#include "git2/types.h" #include "vector.h" +#include "cache.h" +/* DO NOT EXPORT */ +typedef struct { + void *data; /**< Raw, decompressed object data. */ + size_t len; /**< Total number of bytes in data. */ + git_otype type; /**< Type of this object. */ +} git_rawobj; + +/* EXPORT */ +struct git_odb_object { + git_cached_obj cached; + git_rawobj raw; +}; + +/* EXPORT */ struct git_odb { void *_internal; git_vector backends; + git_cache cache; }; int git_odb__hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_rawobj *obj); -int git_odb__inflate_buffer(void *in, size_t inlen, void *out, size_t outlen); #endif diff --git a/src/odb_loose.c b/src/odb_loose.c index 4e2d9a639..4ab1128f3 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -30,14 +30,22 @@ #include "hash.h" #include "odb.h" #include "delta-apply.h" +#include "filebuf.h" #include "git2/odb_backend.h" +#include "git2/types.h" typedef struct { /* object header data */ git_otype type; /* object type */ size_t size; /* object size */ } obj_hdr; +typedef struct { + git_odb_stream stream; + git_filebuf fbuf; + int finished; +} loose_writestream; + typedef struct loose_backend { git_odb_backend parent; @@ -53,38 +61,6 @@ typedef struct loose_backend { * ***********************************************************/ -static int make_temp_file(git_file *fd, char *tmp, size_t n, char *file) -{ - char *template = "/tmp_obj_XXXXXX"; - size_t tmplen = strlen(template); - int dirlen; - - if ((dirlen = git__dirname_r(tmp, n, file)) < 0) - return GIT_ERROR; - - if ((dirlen + tmplen) >= n) - return GIT_ERROR; - - strcpy(tmp + dirlen, (dirlen) ? template : template + 1); - - *fd = gitfo_mkstemp(tmp); - if (*fd < 0 && dirlen) { - /* create directory if it doesn't exist */ - tmp[dirlen] = '\0'; - if ((gitfo_exists(tmp) < 0) && gitfo_mkdir(tmp, 0755)) - return GIT_ERROR; - /* try again */ - strcpy(tmp + dirlen, template); - *fd = gitfo_mkstemp(tmp); - } - if (*fd < 0) - return GIT_ERROR; - - return GIT_SUCCESS; -} - - - static size_t object_file_name(char *name, size_t n, char *dir, const git_oid *id) { size_t len = strlen(dir); @@ -236,72 +212,44 @@ static int finish_inflate(z_stream *s) return GIT_SUCCESS; } -static int deflate_buf(z_stream *s, void *in, size_t len, int flush) +static int is_zlib_compressed_data(unsigned char *data) { - int status = Z_OK; + unsigned int w; - set_stream_input(s, in, len); - while (status == Z_OK) { - status = deflate(s, flush); - if (s->avail_in == 0) - break; - } - return status; + w = ((unsigned int)(data[0]) << 8) + data[1]; + return data[0] == 0x78 && !(w % 31); } -static int deflate_obj(gitfo_buf *buf, char *hdr, int hdrlen, git_rawobj *obj, int level) +static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen) { z_stream zs; - int status; - size_t size; - - assert(buf && !buf->data && hdr && obj); - assert(level == Z_DEFAULT_COMPRESSION || (level >= 0 && level <= 9)); + int status = Z_OK; - buf->data = NULL; - buf->len = 0; - init_stream(&zs, NULL, 0); + memset(&zs, 0x0, sizeof(zs)); - if (deflateInit(&zs, level) < Z_OK) - return GIT_ERROR; + zs.next_out = out; + zs.avail_out = outlen; - size = deflateBound(&zs, hdrlen + obj->len); + zs.next_in = in; + zs.avail_in = inlen; - if ((buf->data = git__malloc(size)) == NULL) { - deflateEnd(&zs); + if (inflateInit(&zs) < Z_OK) return GIT_ERROR; - } - set_stream_output(&zs, buf->data, size); - - /* compress the header */ - status = deflate_buf(&zs, hdr, hdrlen, Z_NO_FLUSH); + while (status == Z_OK) + status = inflate(&zs, Z_FINISH); - /* if header compressed OK, compress the object */ - if (status == Z_OK) - status = deflate_buf(&zs, obj->data, obj->len, Z_FINISH); + inflateEnd(&zs); - if (status != Z_STREAM_END) { - deflateEnd(&zs); - free(buf->data); - buf->data = NULL; + if ((status != Z_STREAM_END) /*|| (zs.avail_in != 0) */) return GIT_ERROR; - } - buf->len = zs.total_out; - deflateEnd(&zs); + if (zs.total_out != outlen) + return GIT_ERROR; return GIT_SUCCESS; } -static int is_zlib_compressed_data(unsigned char *data) -{ - unsigned int w; - - w = ((unsigned int)(data[0]) << 8) + data[1]; - return data[0] == 0x78 && !(w % 31); -} - static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr) { unsigned char *buf, *head = hb; @@ -371,7 +319,7 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, gitfo_buf *obj) in = ((unsigned char *)obj->data) + used; len = obj->len - used; - if (git_odb__inflate_buffer(in, len, buf, hdr.size)) { + if (inflate_buffer(in, len, buf, hdr.size)) { free(buf); return GIT_ERROR; } @@ -505,37 +453,6 @@ cleanup: return error; } -static int write_obj(gitfo_buf *buf, git_oid *id, loose_backend *backend) -{ - char file[GIT_PATH_MAX]; - char temp[GIT_PATH_MAX]; - git_file fd; - - if (object_file_name(file, sizeof(file), backend->objects_dir, id)) - return GIT_EOSERR; - - if (make_temp_file(&fd, temp, sizeof(temp), file) < 0) - return GIT_EOSERR; - - if (gitfo_write(fd, buf->data, buf->len) < 0) { - gitfo_close(fd); - gitfo_unlink(temp); - return GIT_EOSERR; - } - - if (backend->fsync_object_files) - gitfo_fsync(fd); - gitfo_close(fd); - gitfo_chmod(temp, 0444); - - if (gitfo_mv(temp, file) < 0) { - gitfo_unlink(temp); - return GIT_EOSERR; - } - - return GIT_SUCCESS; -} - static int locate_object(char *object_location, loose_backend *backend, const git_oid *oid) { object_file_name(object_location, GIT_PATH_MAX, backend->objects_dir, oid); @@ -558,29 +475,44 @@ static int locate_object(char *object_location, loose_backend *backend, const gi * ***********************************************************/ -int loose_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid) +int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { char object_path[GIT_PATH_MAX]; + git_rawobj raw; + int error; - assert(obj && backend && oid); + assert(backend && oid); if (locate_object(object_path, (loose_backend *)backend, oid) < 0) return GIT_ENOTFOUND; - return read_header_loose(obj, object_path); -} + if ((error = read_header_loose(&raw, object_path)) < GIT_SUCCESS) + return error; + *len_p = raw.len; + *type_p = raw.type; + return GIT_SUCCESS; +} -int loose_backend__read(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid) +int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { char object_path[GIT_PATH_MAX]; + git_rawobj raw; + int error; - assert(obj && backend && oid); + assert(backend && oid); if (locate_object(object_path, (loose_backend *)backend, oid) < 0) return GIT_ENOTFOUND; - return read_loose(obj, object_path); + if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS) + return error; + + *buffer_p = raw.data; + *len_p = raw.len; + *type_p = raw.type; + + return GIT_SUCCESS; } int loose_backend__exists(git_odb_backend *backend, const git_oid *oid) @@ -592,32 +524,104 @@ int loose_backend__exists(git_odb_backend *backend, const git_oid *oid) return locate_object(object_path, (loose_backend *)backend, oid) == GIT_SUCCESS; } +int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream) +{ + loose_writestream *stream = (loose_writestream *)_stream; + loose_backend *backend = (loose_backend *)_stream->backend; + + int error; + char final_path[GIT_PATH_MAX]; + + if ((error = git_filebuf_hash(oid, &stream->fbuf)) < GIT_SUCCESS) + return error; + + if (object_file_name(final_path, sizeof(final_path), backend->objects_dir, oid)) + return GIT_ENOMEM; -int loose_backend__write(git_oid *id, git_odb_backend *_backend, git_rawobj *obj) + if ((error = gitfo_mkdir_2file(final_path)) < GIT_SUCCESS) + return error; + + stream->finished = 1; + return git_filebuf_commit_at(&stream->fbuf, final_path); +} + +int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len) { + loose_writestream *stream = (loose_writestream *)_stream; + return git_filebuf_write(&stream->fbuf, data, len); +} + +void loose_backend__stream_free(git_odb_stream *_stream) +{ + loose_writestream *stream = (loose_writestream *)_stream; + + if (!stream->finished) + git_filebuf_cleanup(&stream->fbuf); + + free(stream); +} + +static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type) +{ + const char *type_str = git_object_type2string(obj_type); + int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len); + + assert(len > 0); /* otherwise snprintf() is broken */ + assert(((size_t) len) < n); /* otherwise the caller is broken! */ + + if (len < 0 || ((size_t) len) >= n) + return GIT_ERROR; + return len+1; +} + +int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, size_t length, git_otype type) +{ + loose_backend *backend; + loose_writestream *stream; + char hdr[64]; int hdrlen; - gitfo_buf buf = GITFO_BUF_INIT; int error; - loose_backend *backend; - assert(id && _backend && obj); + assert(_backend); backend = (loose_backend *)_backend; + *stream_out = NULL; - if ((error = git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, obj)) < 0) - return error; + hdrlen = format_object_header(hdr, sizeof(hdr), length, type); + if (hdrlen < GIT_SUCCESS) + return GIT_EOBJCORRUPTED; + + stream = git__calloc(1, sizeof(loose_writestream)); + if (stream == NULL) + return GIT_ENOMEM; - if (git_odb_exists(_backend->odb, id)) - return GIT_SUCCESS; + stream->stream.backend = _backend; + stream->stream.read = NULL; /* read only */ + stream->stream.write = &loose_backend__stream_write; + stream->stream.finalize_write = &loose_backend__stream_fwrite; + stream->stream.free = &loose_backend__stream_free; + stream->stream.mode = GIT_STREAM_WRONLY; - if ((error = deflate_obj(&buf, hdr, hdrlen, obj, backend->object_zlib_level)) < 0) + error = git_filebuf_open(&stream->fbuf, NULL, + GIT_FILEBUF_HASH_CONTENTS | + GIT_FILEBUF_DEFLATE_CONTENTS | + GIT_FILEBUF_TEMPORARY); + + if (error < GIT_SUCCESS) { + free(stream); return error; + } - error = write_obj(&buf, id, backend); + error = stream->stream.write((git_odb_stream *)stream, hdr, hdrlen); + if (error < GIT_SUCCESS) { + git_filebuf_cleanup(&stream->fbuf); + free(stream); + return error; + } - gitfo_free_buf(&buf); - return error; + *stream_out = (git_odb_stream *)stream; + return GIT_SUCCESS; } void loose_backend__free(git_odb_backend *_backend) @@ -649,7 +653,7 @@ int git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir backend->parent.read = &loose_backend__read; backend->parent.read_header = &loose_backend__read_header; - backend->parent.write = &loose_backend__write; + backend->parent.writestream = &loose_backend__stream; backend->parent.exists = &loose_backend__exists; backend->parent.free = &loose_backend__free; diff --git a/src/odb_pack.c b/src/odb_pack.c index 3067179be..65210f0b0 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -1243,7 +1243,7 @@ static int packfile_unpack_delta( error = packfile_unpack_compressed(&delta, backend, p, w_curs, curpos, delta_size, delta_type); if (error < GIT_SUCCESS) { - git_rawobj_close(&base); + free(base.data); return error; } @@ -1252,8 +1252,8 @@ static int packfile_unpack_delta( base.data, base.len, delta.data, delta.len); - git_rawobj_close(&base); - git_rawobj_close(&delta); + free(base.data); + free(delta.data); /* TODO: we might want to cache this shit. eventually */ //add_delta_base_cache(p, base_offset, base, base_size, *type); @@ -1337,15 +1337,23 @@ int pack_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const g } */ -int pack_backend__read(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid) +int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { struct pack_entry e; + git_rawobj raw; int error; if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < GIT_SUCCESS) return error; - return packfile_unpack(obj, (struct pack_backend *)backend, e.p, e.offset); + if ((error = packfile_unpack(&raw, (struct pack_backend *)backend, e.p, e.offset)) < GIT_SUCCESS) + return error; + + *buffer_p = raw.data; + *len_p = raw.len; + *type_p = raw.type; + + return GIT_SUCCESS; } int pack_backend__exists(git_odb_backend *backend, const git_oid *oid) @@ -1397,7 +1405,6 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) backend->parent.read = &pack_backend__read; backend->parent.read_header = NULL; - backend->parent.write = NULL; backend->parent.exists = &pack_backend__exists; backend->parent.free = &pack_backend__free; @@ -143,14 +143,18 @@ int git__parse_oid(git_oid *oid, char **buffer_out, return GIT_SUCCESS; } -int git__write_oid(git_odb_source *src, const char *header, const git_oid *oid) +int git__write_oid(git_odb_stream *stream, const char *header, const git_oid *oid) { - char hex_oid[41]; + char hex_oid[42]; - git_oid_fmt(hex_oid, oid); - hex_oid[40] = 0; + git_oid_fmt(hex_oid + 1, oid); - return git__source_printf(src, "%s %s\n", header, hex_oid); + hex_oid[0] = ' '; + hex_oid[41] = '\n'; + + stream->write(stream, header, strlen(header)); + stream->write(stream, hex_oid, 42); + return GIT_SUCCESS; } void git_oid_mkraw(git_oid *out, const unsigned char *raw) diff --git a/src/repository.c b/src/repository.c index 37aa44781..132969402 100644 --- a/src/repository.c +++ b/src/repository.c @@ -40,30 +40,12 @@ #define GIT_BRANCH_MASTER "master" -static const int OBJECT_TABLE_SIZE = 32; - typedef struct { char *path_repository; unsigned is_bare:1, has_been_reinit:1; } repo_init; /* - * Hash table methods - * - * Callbacks for the ODB cache, implemented - * as a hash table - */ -static uint32_t object_table_hash(const void *key, int hash_id) -{ - uint32_t r; - git_oid *id; - - id = (git_oid *)key; - memcpy(&r, id->id + (hash_id * sizeof(uint32_t)), sizeof(r)); - return r; -} - -/* * Git repository open methods * * Open a repository object from its path @@ -186,25 +168,9 @@ static git_repository *repository_alloc() memset(repo, 0x0, sizeof(git_repository)); - repo->objects = git_hashtable_alloc( - OBJECT_TABLE_SIZE, - object_table_hash, - (git_hash_keyeq_ptr)git_oid_cmp); - - if (repo->objects == NULL) { - free(repo); - return NULL; - } + git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free); if (git_repository__refcache_init(&repo->references) < GIT_SUCCESS) { - git_hashtable_free(repo->objects); - free(repo); - return NULL; - } - - if (git_vector_init(&repo->memory_objects, 16, NULL) < GIT_SUCCESS) { - git_hashtable_free(repo->objects); - git_repository__refcache_free(&repo->references); free(repo); return NULL; } @@ -330,51 +296,19 @@ cleanup: return error; } -int git_repository_gc(git_repository *repo) -{ - int collected = 0; - git_object *object; - const void *_unused; - - GIT_HASHTABLE_FOREACH(repo->objects, _unused, object, - if (object->can_free) { - git_object__free(object); - collected++; - } - ); - - return collected; -} - void git_repository_free(git_repository *repo) { - git_object *object; - const void *_unused; - unsigned int i; - if (repo == NULL) return; - /* force free all the objects */ - GIT_HASHTABLE_FOREACH(repo->objects, _unused, object, - git_object__free(object); - ); - - for (i = 0; i < repo->memory_objects.length; ++i) { - object = git_vector_get(&repo->memory_objects, i); - git_object__free(object); - } + git_cache_free(&repo->objects); + git_repository__refcache_free(&repo->references); 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); diff --git a/src/repository.h b/src/repository.h index 48b8dae6b..fef1c7da0 100644 --- a/src/repository.h +++ b/src/repository.h @@ -9,6 +9,7 @@ #include "hashtable.h" #include "index.h" +#include "cache.h" #include "refs.h" #define DOT_GIT ".git" @@ -16,28 +17,17 @@ #define GIT_OBJECTS_DIR "objects/" #define GIT_INDEX_FILE "index" -typedef struct { - git_rawobj raw; - void *write_ptr; - size_t written_bytes; - int open:1; -} git_odb_source; - struct git_object { - git_oid id; + git_cached_obj cached; git_repository *repo; - git_odb_source source; - unsigned int lru; - unsigned char in_memory, modified, can_free, _pad; + git_otype type; }; struct git_repository { git_odb *db; git_index *index; - git_hashtable *objects; - git_vector memory_objects; - + git_cache objects; git_refcache references; char *path_repository; @@ -49,17 +39,11 @@ struct git_repository { unsigned int lru_counter; }; -int git_object__source_open(git_object *object); -void git_object__source_close(git_object *object); - /* fully free the object; internal method, do not * export */ -void git_object__free(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); +void git_object__free(void *object); 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); +int git__write_oid(git_odb_stream *src, const char *header, const git_oid *oid); #endif diff --git a/src/revwalk.c b/src/revwalk.c index 3b7ad34e6..a9d4f8734 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -25,6 +25,7 @@ #include "common.h" #include "commit.h" +#include "odb.h" #include "hashtable.h" #include "pqueue.h" @@ -236,22 +237,22 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo static int commit_parse(git_revwalk *walk, commit_object *commit) { - git_rawobj data; + git_odb_object *obj; int error; if (commit->parsed) return GIT_SUCCESS; - if ((error = git_odb_read(&data, walk->repo->db, &commit->oid)) < GIT_SUCCESS) + if ((error = git_odb_read(&obj, walk->repo->db, &commit->oid)) < GIT_SUCCESS) return error; - if (data.type != GIT_OBJ_COMMIT) { - git_rawobj_close(&data); + if (obj->raw.type != GIT_OBJ_COMMIT) { + git_odb_object_close(obj); return GIT_EOBJTYPE; } - error = commit_quick_parse(walk, commit, &data); - git_rawobj_close(&data); + error = commit_quick_parse(walk, commit, &obj->raw); + git_odb_object_close(obj); return error; } diff --git a/src/signature.c b/src/signature.c index 5c9f15973..13816c396 100644 --- a/src/signature.c +++ b/src/signature.c @@ -46,13 +46,7 @@ git_signature *git_signature_new(const char *name, const char *email, time_t tim goto cleanup; p->name = git__strdup(name); - if (p->name == NULL) - goto cleanup; - p->email = git__strdup(email); - if (p->email == NULL) - goto cleanup; - p->when.time = time; p->when.offset = offset; @@ -179,10 +173,12 @@ int git_signature__parse(git_signature *sig, char **buffer_out, return GIT_SUCCESS; } -int git_signature__write(git_odb_source *src, const char *header, const git_signature *sig) +int git_signature__write(char **signature, const char *header, const git_signature *sig) { - char sign; int offset, hours, mins; + char sig_buffer[2048]; + int sig_buffer_len; + char sign; offset = sig->when.offset; sign = (sig->when.offset < 0) ? '-' : '+'; @@ -193,7 +189,16 @@ int git_signature__write(git_odb_source *src, const char *header, const git_sign hours = offset / 60; mins = offset % 60; - return git__source_printf(src, "%s %s <%s> %u %c%02d%02d\n", header, sig->name, sig->email, (unsigned)sig->when.time, sign, hours, mins); + sig_buffer_len = snprintf(sig_buffer, sizeof(sig_buffer), + "%s %s <%s> %u %c%02d%02d\n", + header, sig->name, sig->email, + (unsigned)sig->when.time, sign, hours, mins); + + if (sig_buffer_len < 0 || (size_t)sig_buffer_len > sizeof(sig_buffer)) + return GIT_ENOMEM; + + *signature = git__strdup(sig_buffer); + return sig_buffer_len; } diff --git a/src/signature.h b/src/signature.h index ee212c2dc..3534cb21f 100644 --- a/src/signature.h +++ b/src/signature.h @@ -7,6 +7,6 @@ #include <time.h> int git_signature__parse(git_signature *sig, char **buffer_out, const char *buffer_end, const char *header); -int git_signature__write(git_odb_source *src, const char *header, const git_signature *sig); +int git_signature__write(char **signature, const char *header, const git_signature *sig); #endif @@ -56,21 +56,6 @@ const git_oid *git_tag_target_oid(git_tag *t) return &t->target; } -int git_tag_set_target(git_tag *tag, git_object *target) -{ - const git_oid *oid; - - assert(tag && target); - - if ((oid = git_object_id(target)) == NULL) - return GIT_EMISSINGOBJDATA; - - tag->object.modified = 1; - git_oid_cpy(&tag->target, oid); - tag->type = git_object_type(target); - return GIT_SUCCESS; -} - git_otype git_tag_type(git_tag *t) { assert(t); @@ -83,50 +68,17 @@ const char *git_tag_name(git_tag *t) return t->tag_name; } -void git_tag_set_name(git_tag *tag, const char *name) -{ - assert(tag && name); - - tag->object.modified = 1; - - if (tag->tag_name) - free(tag->tag_name); - - tag->tag_name = git__strdup(name); -} - const git_signature *git_tag_tagger(git_tag *t) { return t->tagger; } -void git_tag_set_tagger(git_tag *tag, const git_signature *tagger_sig) -{ - assert(tag && tagger_sig); - tag->object.modified = 1; - - git_signature_free(tag->tagger); - tag->tagger = git_signature_dup(tagger_sig); -} - const char *git_tag_message(git_tag *t) { assert(t); return t->message; } -void git_tag_set_message(git_tag *tag, const char *message) -{ - assert(tag && message); - - tag->object.modified = 1; - - if (tag->message) - free(tag->message); - - tag->message = git__strdup(message); -} - static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) { static const char *tag_types[] = { @@ -187,9 +139,6 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) buffer = search + 1; - if (tag->tagger != NULL) - git_signature_free(tag->tagger); - tag->tagger = git__malloc(sizeof(git_signature)); if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ")) != 0) @@ -197,9 +146,6 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) text_len = buffer_end - ++buffer; - if (tag->message != NULL) - free(tag->message); - tag->message = git__malloc(text_len + 1); memcpy(tag->message, buffer, text_len); tag->message[text_len] = '\0'; @@ -207,26 +153,90 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) return GIT_SUCCESS; } -int git_tag__writeback(git_tag *tag, git_odb_source *src) +int git_tag_create_o( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + const git_signature *tagger, + const char *message) { - if (tag->tag_name == NULL || tag->tagger == NULL) - return GIT_EMISSINGOBJDATA; + return git_tag_create( + oid, repo, tag_name, + git_object_id(target), + git_object_type(target), + tagger, message); +} - git__write_oid(src, "object", &tag->target); - git__source_printf(src, "type %s\n", git_object_type2string(tag->type)); - git__source_printf(src, "tag %s\n", tag->tag_name); - git_signature__write(src, "tagger", tag->tagger); +int git_tag_create( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_oid *target, + git_otype target_type, + const git_signature *tagger, + const char *message) +{ + size_t final_size = 0; + git_odb_stream *stream; - if (tag->message != NULL) - git__source_printf(src, "\n%s", tag->message); + const char *type_str; + char *tagger_str; - return GIT_SUCCESS; + int type_str_len, tag_name_len, tagger_str_len, message_len; + int error; + + + type_str = git_object_type2string(target_type); + + tagger_str_len = git_signature__write(&tagger_str, "tagger", tagger); + + type_str_len = strlen(type_str); + tag_name_len = strlen(tag_name); + message_len = strlen(message); + + final_size += GIT_OID_LINE_LENGTH("object"); + final_size += STRLEN("type ") + type_str_len + 1; + final_size += STRLEN("tag ") + tag_name_len + 1; + final_size += tagger_str_len; + final_size += 1 + message_len; + + if ((error = git_odb_open_wstream(&stream, repo->db, final_size, GIT_OBJ_TAG)) < GIT_SUCCESS) + return error; + + git__write_oid(stream, "object", target); + + stream->write(stream, "type ", STRLEN("type ")); + stream->write(stream, type_str, type_str_len); + + stream->write(stream, "\ntag ", STRLEN("\ntag ")); + stream->write(stream, tag_name, tag_name_len); + stream->write(stream, "\n", 1); + + stream->write(stream, tagger_str, tagger_str_len); + free(tagger_str); + + stream->write(stream, "\n", 1); + stream->write(stream, message, message_len); + + + error = stream->finalize_write(oid, stream); + stream->free(stream); + + if (error == GIT_SUCCESS) { + char ref_name[512]; + git_reference *new_ref; + git__joinpath(ref_name, GIT_REFS_TAGS_DIR, tag_name); + error = git_reference_create_oid(&new_ref, repo, ref_name, oid); + } + + return error; } -int git_tag__parse(git_tag *tag) +int git_tag__parse(git_tag *tag, git_odb_object *obj) { - assert(tag && tag->object.source.open); - return parse_tag_buffer(tag, tag->object.source.raw.data, (char *)tag->object.source.raw.data + tag->object.source.raw.len); + assert(tag); + return parse_tag_buffer(tag, obj->raw.data, (char *)obj->raw.data + obj->raw.len); } @@ -3,6 +3,7 @@ #include "git2/tag.h" #include "repository.h" +#include "odb.h" struct git_tag { git_object object; @@ -16,7 +17,6 @@ struct git_tag { }; void git_tag__free(git_tag *tag); -int git_tag__parse(git_tag *tag); -int git_tag__writeback(git_tag *tag, git_odb_source *src); +int git_tag__parse(git_tag *tag, git_odb_object *obj); #endif diff --git a/src/thread-utils.h b/src/thread-utils.h index 1cf0e3407..e542639c8 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -8,7 +8,7 @@ typedef struct { volatile int val; } git_atomic; -static inline void git_atomic_set(git_atomic *a, int val) +GIT_INLINE(void) git_atomic_set(git_atomic *a, int val) { a->val = val; } @@ -36,7 +36,7 @@ static inline void git_atomic_set(git_atomic *a, int val) #define git_cond_signal(c) (void)0 //pthread_cond_signal(c) #define git_cond_broadcast(c) (void)0 //pthread_cond_broadcast(c) -static inline int git_atomic_inc(git_atomic *a) +GIT_INLINE(int) git_atomic_inc(git_atomic *a) { #ifdef __GNUC__ return __sync_add_and_fetch(&a->val, 1); @@ -47,7 +47,7 @@ static inline int git_atomic_inc(git_atomic *a) #endif } -static inline int git_atomic_dec(git_atomic *a) +GIT_INLINE(int) git_atomic_dec(git_atomic *a) { #ifdef __GNUC__ return __sync_sub_and_fetch(&a->val, 1); @@ -81,12 +81,12 @@ static inline int git_atomic_dec(git_atomic *a) #define git_cond_signal(c) (void)0 #define git_cond_broadcast(c) (void)0 -static inline int git_atomic_inc(git_atomic *a) +GIT_INLINE(int) git_atomic_inc(git_atomic *a) { return ++a->val; } -static inline int git_atomic_dec(git_atomic *a) +GIT_INLINE(int) git_atomic_dec(git_atomic *a) { return --a->val; } diff --git a/src/tree.c b/src/tree.c index 307d41b0d..31b286e69 100644 --- a/src/tree.c +++ b/src/tree.c @@ -41,9 +41,11 @@ int entry_search_cmp(const void *key, const void *array_member) return strcmp(filename, entry->filename); } +#if 0 static int valid_attributes(const int attributes) { return attributes >= 0 && attributes <= MAX_FILEMODE; } +#endif int entry_sort_cmp(const void *a, const void *b) { @@ -56,13 +58,10 @@ int entry_sort_cmp(const void *a, const void *b) entry_b->attr & 040000); } -void git_tree_clear_entries(git_tree *tree) +void git_tree__free(git_tree *tree) { unsigned int i; - if (tree == NULL) - return; - for (i = 0; i < tree->entries.length; ++i) { git_tree_entry *e; e = git_vector_get(&tree->entries, i); @@ -71,32 +70,6 @@ void git_tree_clear_entries(git_tree *tree) free(e); } - git_vector_clear(&tree->entries); - tree->object.modified = 1; -} - - -git_tree *git_tree__new(void) -{ - git_tree *tree; - - tree = git__malloc(sizeof(struct git_tree)); - if (tree == NULL) - return NULL; - - memset(tree, 0x0, sizeof(struct git_tree)); - - if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < GIT_SUCCESS) { - free(tree); - return NULL; - } - - return tree; -} - -void git_tree__free(git_tree *tree) -{ - git_tree_clear_entries(tree); git_vector_free(&tree->entries); free(tree); } @@ -106,37 +79,6 @@ const git_oid *git_tree_id(git_tree *c) return git_object_id((git_object *)c); } -int git_tree_entry_set_attributes(git_tree_entry *entry, unsigned int attr) -{ - assert(entry && entry->owner); - - if (!valid_attributes(attr)) { - return GIT_ERROR; - } - - entry->attr = attr; - entry->owner->object.modified = 1; - return GIT_SUCCESS; -} - -void git_tree_entry_set_name(git_tree_entry *entry, const char *name) -{ - assert(entry && entry->owner); - - free(entry->filename); - entry->filename = git__strdup(name); - git_vector_sort(&entry->owner->entries); - entry->owner->object.modified = 1; -} - -void git_tree_entry_set_id(git_tree_entry *entry, const git_oid *oid) -{ - assert(entry && entry->owner); - - git_oid_cpy(&entry->oid, oid); - entry->owner->object.modified = 1; -} - unsigned int git_tree_entry_attributes(git_tree_entry *entry) { return entry->attr; @@ -154,15 +96,10 @@ const git_oid *git_tree_entry_id(git_tree_entry *entry) return &entry->oid; } -int git_tree_entry_2object(git_object **object_out, git_tree_entry *entry) +int git_tree_entry_2object(git_object **object_out, git_repository *repo, git_tree_entry *entry) { assert(entry && object_out); - return git_object_lookup(object_out, entry->owner->object.repo, &entry->oid, GIT_OBJ_ANY); -} - -static void sort_entries(git_tree *tree) -{ - git_vector_sort(&tree->entries); + return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY); } git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) @@ -171,8 +108,6 @@ git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) assert(tree && filename); - sort_entries(tree); - idx = git_vector_bsearch2(&tree->entries, entry_search_cmp, filename); if (idx == GIT_ENOTFOUND) return NULL; @@ -183,9 +118,6 @@ git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx) { assert(tree); - - sort_entries(tree); - return git_vector_get(&tree->entries, (unsigned int)idx); } @@ -195,107 +127,12 @@ size_t git_tree_entrycount(git_tree *tree) return tree->entries.length; } -int git_tree_add_entry(git_tree_entry **entry_out, git_tree *tree, const git_oid *id, const char *filename, int attributes) -{ - git_tree_entry *entry; - - assert(tree && id && filename); - if (!valid_attributes(attributes)) { - return GIT_ERROR; - } - - if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL) - return GIT_ENOMEM; - - memset(entry, 0x0, sizeof(git_tree_entry)); - - entry->filename = git__strdup(filename); - git_oid_cpy(&entry->oid, id); - entry->attr = attributes; - entry->owner = tree; - - if (git_vector_insert(&tree->entries, entry) < 0) - return GIT_ENOMEM; - - if (entry_out != NULL) - *entry_out = entry; - - tree->object.modified = 1; - return GIT_SUCCESS; -} - -int git_tree_remove_entry_byindex(git_tree *tree, int idx) -{ - git_tree_entry *remove_ptr; - - assert(tree); - - sort_entries(tree); - - remove_ptr = git_vector_get(&tree->entries, (unsigned int)idx); - if (remove_ptr == NULL) - return GIT_ENOTFOUND; - - free(remove_ptr->filename); - free(remove_ptr); - - tree->object.modified = 1; - - return git_vector_remove(&tree->entries, (unsigned int)idx); -} - -int git_tree_remove_entry_byname(git_tree *tree, const char *filename) -{ - int idx; - - assert(tree && filename); - - sort_entries(tree); - - idx = git_vector_bsearch2(&tree->entries, entry_search_cmp, filename); - if (idx == GIT_ENOTFOUND) - return GIT_ENOTFOUND; - - return git_tree_remove_entry_byindex(tree, idx); -} - -int git_tree__writeback(git_tree *tree, git_odb_source *src) -{ - size_t i; - char filemode[MAX_FILEMODE_BYTES + 1 + 1]; - - assert(tree && src); - - if (tree->entries.length == 0) - return GIT_EMISSINGOBJDATA; - - sort_entries(tree); - - for (i = 0; i < tree->entries.length; ++i) { - git_tree_entry *entry; - - entry = git_vector_get(&tree->entries, i); - - snprintf(filemode, sizeof(filemode), "%o ", entry->attr); - - git__source_write(src, filemode, strlen(filemode)); - git__source_write(src, entry->filename, strlen(entry->filename) + 1); - git__source_write(src, entry->oid.id, GIT_OID_RAWSZ); - } - - return GIT_SUCCESS; -} - - static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end) { - static const size_t avg_entry_size = 40; - unsigned int expected_size; int error = GIT_SUCCESS; - expected_size = (tree->object.source.raw.len / avg_entry_size) + 1; - - git_tree_clear_entries(tree); + if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < GIT_SUCCESS) + return GIT_ENOMEM; while (buffer < buffer_end) { git_tree_entry *entry; @@ -309,7 +146,6 @@ static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end) if (git_vector_insert(&tree->entries, entry) < GIT_SUCCESS) return GIT_ENOMEM; - entry->owner = tree; entry->attr = strtol(buffer, &buffer, 8); if (*buffer++ != ' ') { @@ -336,16 +172,9 @@ static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end) return error; } -int git_tree__parse(git_tree *tree) +int git_tree__parse(git_tree *tree, git_odb_object *obj) { - char *buffer, *buffer_end; - - assert(tree && tree->object.source.open); - assert(!tree->object.in_memory); - - buffer = tree->object.source.raw.data; - buffer_end = buffer + tree->object.source.raw.len; - - return tree_parse_buffer(tree, buffer, buffer_end); + assert(tree); + return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len); } diff --git a/src/tree.h b/src/tree.h index 78500c471..b4e910a9f 100644 --- a/src/tree.h +++ b/src/tree.h @@ -3,14 +3,13 @@ #include "git2/tree.h" #include "repository.h" +#include "odb.h" #include "vector.h" struct git_tree_entry { unsigned int attr; char *filename; git_oid oid; - - git_tree *owner; }; struct git_tree { @@ -19,8 +18,6 @@ struct git_tree { }; void git_tree__free(git_tree *tree); -git_tree *git_tree__new(void); -int git_tree__parse(git_tree *tree); -int git_tree__writeback(git_tree *tree, git_odb_source *src); +int git_tree__parse(git_tree *tree, git_odb_object *obj); #endif diff --git a/src/util.h b/src/util.h index e0dfd7b20..653b34d02 100644 --- a/src/util.h +++ b/src/util.h @@ -97,6 +97,8 @@ extern char *git__strtok_keep(char *output, char *src, char *delimit); #define STRLEN(str) (sizeof(str) - 1) +#define GIT_OID_LINE_LENGTH(header) (STRLEN(header) + 1 + GIT_OID_HEXSZ + 1) + /* * Realloc the buffer pointed at by variable 'x' so that it can hold * at least 'nr' entries; the number of entries currently allocated |