diff options
author | Carlos Martín Nieto <carlos@cmartin.tk> | 2011-11-18 22:45:56 +0100 |
---|---|---|
committer | Carlos Martín Nieto <carlos@cmartin.tk> | 2011-11-18 22:45:56 +0100 |
commit | bdd31dd5e832126b2f22fccbe244a1106c241ab0 (patch) | |
tree | b08a2ad0fad57131563aa8c071221bea241128c1 /src | |
parent | 277b7efe493887081ce1dafd91199d0ee9f676c9 (diff) | |
parent | e4c93a392763a006d11e1c1dd01c12f85498dad5 (diff) | |
download | libgit2-error-handling.tar.gz |
Merge branch 'development' into error-handlingerror-handling
The code in this branch has been modified so it works with the global
state introduced in development.
Diffstat (limited to 'src')
-rw-r--r-- | src/blob.c | 2 | ||||
-rw-r--r-- | src/buffer.c | 2 | ||||
-rw-r--r-- | src/cache.c | 2 | ||||
-rw-r--r-- | src/commit.c | 30 | ||||
-rw-r--r-- | src/common.h | 1 | ||||
-rw-r--r-- | src/config.c | 18 | ||||
-rw-r--r-- | src/config.h | 1 | ||||
-rw-r--r-- | src/config_file.c | 76 | ||||
-rw-r--r-- | src/delta-apply.c | 2 | ||||
-rw-r--r-- | src/errors.c | 18 | ||||
-rw-r--r-- | src/fetch.c | 3 | ||||
-rw-r--r-- | src/filebuf.c | 34 | ||||
-rw-r--r-- | src/filebuf.h | 4 | ||||
-rw-r--r-- | src/fileops.c | 50 | ||||
-rw-r--r-- | src/fileops.h | 12 | ||||
-rw-r--r-- | src/global.c | 134 | ||||
-rw-r--r-- | src/global.h | 22 | ||||
-rw-r--r-- | src/hash.c | 2 | ||||
-rw-r--r-- | src/hashtable.c | 10 | ||||
-rw-r--r-- | src/index.c | 26 | ||||
-rw-r--r-- | src/index.h | 3 | ||||
-rw-r--r-- | src/indexer.c | 12 | ||||
-rw-r--r-- | src/mwindow.c | 102 | ||||
-rw-r--r-- | src/mwindow.h | 3 | ||||
-rw-r--r-- | src/netops.c | 2 | ||||
-rw-r--r-- | src/object.c | 2 | ||||
-rw-r--r-- | src/odb.c | 22 | ||||
-rw-r--r-- | src/odb.h | 4 | ||||
-rw-r--r-- | src/odb_loose.c | 35 | ||||
-rw-r--r-- | src/odb_pack.c | 10 | ||||
-rw-r--r-- | src/oid.c | 8 | ||||
-rw-r--r-- | src/pack.c | 24 | ||||
-rw-r--r-- | src/pack.h | 2 | ||||
-rw-r--r-- | src/path.c | 4 | ||||
-rw-r--r-- | src/pkt.c | 6 | ||||
-rw-r--r-- | src/posix.c | 16 | ||||
-rw-r--r-- | src/posix.h | 4 | ||||
-rw-r--r-- | src/pqueue.c | 6 | ||||
-rw-r--r-- | src/reflog.c | 60 | ||||
-rw-r--r-- | src/reflog.h | 2 | ||||
-rw-r--r-- | src/refs.c | 1293 | ||||
-rw-r--r-- | src/refs.h | 13 | ||||
-rw-r--r-- | src/refspec.c | 2 | ||||
-rw-r--r-- | src/remote.c | 20 | ||||
-rw-r--r-- | src/repository.c | 72 | ||||
-rw-r--r-- | src/repository.h | 5 | ||||
-rw-r--r-- | src/revwalk.c | 14 | ||||
-rw-r--r-- | src/signature.c | 6 | ||||
-rw-r--r-- | src/status.c | 139 | ||||
-rw-r--r-- | src/tag.c | 34 | ||||
-rw-r--r-- | src/thread-utils.h | 2 | ||||
-rw-r--r-- | src/transports/git.c | 18 | ||||
-rw-r--r-- | src/transports/http.c | 23 | ||||
-rw-r--r-- | src/transports/local.c | 21 | ||||
-rw-r--r-- | src/tree-cache.c | 4 | ||||
-rw-r--r-- | src/tree.c | 126 | ||||
-rw-r--r-- | src/tsort.c | 4 | ||||
-rw-r--r-- | src/util.c | 4 | ||||
-rw-r--r-- | src/util.h | 2 | ||||
-rw-r--r-- | src/vector.c | 4 | ||||
-rw-r--r-- | src/win32/dir.c | 23 | ||||
-rw-r--r-- | src/win32/posix.h | 15 | ||||
-rw-r--r-- | src/win32/posix_w32.c | 90 | ||||
-rw-r--r-- | src/win32/utf-conv.c (renamed from src/win32/utf8-conv.c) | 34 | ||||
-rw-r--r-- | src/win32/utf-conv.h (renamed from src/win32/utf8-conv.h) | 8 |
65 files changed, 1543 insertions, 1209 deletions
diff --git a/src/blob.c b/src/blob.c index 42564ab50..f13a5be15 100644 --- a/src/blob.c +++ b/src/blob.c @@ -27,7 +27,7 @@ size_t git_blob_rawsize(git_blob *blob) void git_blob__free(git_blob *blob) { git_odb_object_close(blob->odb_object); - free(blob); + git__free(blob); } int git_blob__parse(git_blob *blob, git_odb_object *odb_obj) diff --git a/src/buffer.c b/src/buffer.c index 0eeeecf2f..1fb848e46 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -97,7 +97,7 @@ const char *git_buf_cstr(git_buf *buf) void git_buf_free(git_buf *buf) { - free(buf->ptr); + git__free(buf->ptr); } void git_buf_clear(git_buf *buf) diff --git a/src/cache.c b/src/cache.c index 79f3eaea2..6ba4d212c 100644 --- a/src/cache.c +++ b/src/cache.c @@ -53,7 +53,7 @@ void git_cache_free(git_cache *cache) git_mutex_free(&cache->nodes[i].lock); } - free(cache->nodes); + git__free(cache->nodes); } void *git_cache_get(git_cache *cache, const git_oid *oid) diff --git a/src/commit.c b/src/commit.c index ced457ecc..83bc9fc4c 100644 --- a/src/commit.c +++ b/src/commit.c @@ -32,7 +32,7 @@ static void clear_parents(git_commit *commit) for (i = 0; i < commit->parent_oids.length; ++i) { git_oid *parent = git_vector_get(&commit->parent_oids, i); - free(parent); + git__free(parent); } git_vector_clear(&commit->parent_oids); @@ -46,9 +46,9 @@ void git_commit__free(git_commit *commit) git_signature_free(commit->author); git_signature_free(commit->committer); - free(commit->message); - free(commit->message_encoding); - free(commit); + git__free(commit->message); + git__free(commit->message_encoding); + git__free(commit); } const git_oid *git_commit_id(git_commit *c) @@ -84,7 +84,7 @@ int git_commit_create_v( message_encoding, message, tree, parent_count, parents); - free((void *)parents); + git__free((void *)parents); return error; } @@ -137,15 +137,18 @@ int git_commit_create( if (error == GIT_SUCCESS && update_ref != NULL) { git_reference *head; + git_reference *target; error = git_reference_lookup(&head, repo, update_ref); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to create commit"); - error = git_reference_resolve(&head, head); + error = git_reference_resolve(&target, head); if (error < GIT_SUCCESS) { - if (error != GIT_ENOTFOUND) + if (error != GIT_ENOTFOUND) { + git_reference_free(head); return git__rethrow(error, "Failed to create commit"); + } /* * The target of the reference was not found. This can happen * just after a repository has been initialized (the master @@ -153,10 +156,19 @@ int git_commit_create( * point to) or after an orphan checkout, so if the target * branch doesn't exist yet, create it and return. */ - return git_reference_create_oid(&head, repo, git_reference_target(head), oid, 1); + error = git_reference_create_oid(&target, repo, git_reference_target(head), oid, 1); + + git_reference_free(head); + if (error == GIT_SUCCESS) + git_reference_free(target); + + return error; } - error = git_reference_set_oid(head, oid); + error = git_reference_set_oid(target, oid); + + git_reference_free(head); + git_reference_free(target); } if (error < GIT_SUCCESS) diff --git a/src/common.h b/src/common.h index 475edc518..f4dcc1ccd 100644 --- a/src/common.h +++ b/src/common.h @@ -8,7 +8,6 @@ #define INCLUDE_common_h__ #include "git2/common.h" -#include "git2/thread-utils.h" #include "cc-compat.h" #include <assert.h> diff --git a/src/config.c b/src/config.c index f53afa145..4e48ff7f4 100644 --- a/src/config.c +++ b/src/config.c @@ -35,11 +35,11 @@ void git_config_free(git_config *cfg) internal = git_vector_get(&cfg->files, i); file = internal->file; file->free(file); - free(internal); + git__free(internal); } git_vector_free(&cfg->files); - free(cfg); + git__free(cfg); } static int config_backend_cmp(const void *a, const void *b) @@ -61,7 +61,7 @@ int git_config_new(git_config **out) memset(cfg, 0x0, sizeof(git_config)); if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) { - free(cfg); + git__free(cfg); return GIT_ENOMEM; } @@ -125,7 +125,7 @@ int git_config_add_file(git_config *cfg, git_config_file *file, int priority) internal->priority = priority; if (git_vector_insert(&cfg->files, internal) < 0) { - free(internal); + git__free(internal); return GIT_ENOMEM; } @@ -366,20 +366,20 @@ static int win32_find_system(char *system_config_path) return git__throw(GIT_ERROR, "Failed to expand environment strings"); if (_waccess(apphome_utf16, F_OK) < 0) { - free(apphome_utf16); + git__free(apphome_utf16); return GIT_ENOTFOUND; } - apphome_utf8 = conv_utf16_to_utf8(apphome_utf16); - free(apphome_utf16); + apphome_utf8 = gitwin_from_utf16(apphome_utf16); + git__free(apphome_utf16); if (strlen(apphome_utf8) >= GIT_PATH_MAX) { - free(apphome_utf8); + git__free(apphome_utf8); return git__throw(GIT_ESHORTBUFFER, "Path is too long"); } strcpy(system_config_path, apphome_utf8); - free(apphome_utf8); + git__free(apphome_utf8); return GIT_SUCCESS; } #endif diff --git a/src/config.h b/src/config.h index 7749a9c1a..43574a586 100644 --- a/src/config.h +++ b/src/config.h @@ -14,6 +14,7 @@ #define GIT_CONFIG_FILENAME ".gitconfig" #define GIT_CONFIG_FILENAME_INREPO "config" +#define GIT_CONFIG_FILE_MODE 0666 struct git_config { git_vector files; diff --git a/src/config_file.c b/src/config_file.c index a85ae1578..aec29d4e2 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -90,10 +90,10 @@ static void cvar_free(cvar_t *var) if (var == NULL) return; - free(var->section); - free(var->name); - free(var->value); - free(var); + git__free(var->section); + git__free(var->name); + git__free(var->value); + git__free(var); } static void cvar_list_free(cvar_t_list *list) @@ -188,7 +188,7 @@ static int cvar_normalize_name(cvar_t *var, char **output) if (section_sp == NULL) { ret = p_snprintf(name, len + 1, "%s.%s", var->section, var->name); if (ret < 0) { - free(name); + git__free(name); return git__throw(GIT_EOSERR, "Failed to normalize name. OS err: %s", strerror(errno)); } @@ -281,10 +281,10 @@ static void backend_free(git_config_file *_backend) if (backend == NULL) return; - free(backend->file_path); + git__free(backend->file_path); cvar_list_free(&backend->var_list); - free(backend); + git__free(backend); } static int file_foreach(git_config_file *backend, int (*fn)(const char *, const char *, void *), void *data) @@ -301,7 +301,7 @@ static int file_foreach(git_config_file *backend, int (*fn)(const char *, const return ret; ret = fn(normalized, var->value, data); - free(normalized); + git__free(normalized); if (ret) break; } @@ -326,7 +326,7 @@ static int config_set(git_config_file *cfg, const char *name, const char *value) if (tmp == NULL && value != NULL) return GIT_ENOMEM; - free(existing->value); + git__free(existing->value); existing->value = tmp; return config_write(b, existing); @@ -411,7 +411,7 @@ int git_config_file__ondisk(git_config_file **out, const char *path) backend->file_path = git__strdup(path); if (backend->file_path == NULL) { - free(backend); + git__free(backend); return GIT_ENOMEM; } @@ -653,13 +653,13 @@ static int parse_section_header(diskfile_backend *cfg, char **section_out) /* find the end of the variable's name */ name_end = strchr(line, ']'); if (name_end == NULL) { - free(line); + git__free(line); return git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Can't find header name end"); } name = (char *)git__malloc((size_t)(name_end - line) + 1); if (name == NULL) { - free(line); + git__free(line); return GIT_ENOMEM; } @@ -679,8 +679,8 @@ static int parse_section_header(diskfile_backend *cfg, char **section_out) if (isspace(c)){ name[name_length] = '\0'; error = parse_section_header_ext(line, name, section_out); - free(line); - free(name); + git__free(line); + git__free(name); return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse header"); } @@ -699,14 +699,14 @@ static int parse_section_header(diskfile_backend *cfg, char **section_out) } name[name_length] = 0; - free(line); + git__free(line); git__strtolower(name); *section_out = name; return GIT_SUCCESS; error: - free(line); - free(name); + git__free(line); + git__free(name); return error; } @@ -810,7 +810,7 @@ static int config_parse(diskfile_backend *cfg_file) break; case '[': /* section header, new section begins */ - free(current_section); + git__free(current_section); current_section = NULL; error = parse_section_header(cfg_file, ¤t_section); break; @@ -826,7 +826,7 @@ static int config_parse(diskfile_backend *cfg_file) if (error < GIT_SUCCESS) break; - var = malloc(sizeof(cvar_t)); + var = git__malloc(sizeof(cvar_t)); if (var == NULL) { error = GIT_ENOMEM; break; @@ -837,7 +837,7 @@ static int config_parse(diskfile_backend *cfg_file) var->section = git__strdup(current_section); if (var->section == NULL) { error = GIT_ENOMEM; - free(var); + git__free(var); break; } @@ -851,7 +851,7 @@ static int config_parse(diskfile_backend *cfg_file) } } - free(current_section); + git__free(current_section); return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse config"); } @@ -915,7 +915,7 @@ static int config_write(diskfile_backend *cfg, cvar_t *var) */ pre_end = post_start = cfg->reader.read_ptr; if (current_section) - free(current_section); + git__free(current_section); error = parse_section_header(cfg, ¤t_section); if (error < GIT_SUCCESS) break; @@ -953,8 +953,8 @@ static int config_write(diskfile_backend *cfg, cvar_t *var) if ((error = parse_variable(cfg, &var_name, &var_value)) == GIT_SUCCESS) cmp = strcasecmp(var->name, var_name); - free(var_name); - free(var_value); + git__free(var_name); + git__free(var_value); if (cmp != 0) break; @@ -1029,12 +1029,12 @@ static int config_write(diskfile_backend *cfg, cvar_t *var) git__rethrow(error, "Failed to write new section"); cleanup: - free(current_section); + git__free(current_section); if (error < GIT_SUCCESS) git_filebuf_cleanup(&file); else - error = git_filebuf_commit(&file); + error = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE); git_futils_freebuffer(&cfg->reader.buffer); return error; @@ -1093,7 +1093,7 @@ static int parse_multiline_variable(diskfile_backend *cfg, const char *first, ch ret = p_snprintf(buf, len, "%s %s", first, line); if (ret < 0) { error = git__throw(GIT_EOSERR, "Failed to parse multiline var. Failed to put together two lines. OS err: %s", strerror(errno)); - free(buf); + git__free(buf); goto out; } @@ -1105,14 +1105,14 @@ static int parse_multiline_variable(diskfile_backend *cfg, const char *first, ch if (is_multiline_var(buf)) { char *final_val; error = parse_multiline_variable(cfg, buf, &final_val); - free(buf); + git__free(buf); buf = final_val; } *out = buf; out: - free(line); + git__free(line); return error; } @@ -1158,19 +1158,25 @@ static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_val while (isspace(value_start[0])) value_start++; - if (value_start[0] == '\0') + if (value_start[0] == '\0') { + *var_value = NULL; goto out; + } if (is_multiline_var(value_start)) { error = parse_multiline_variable(cfg, value_start, var_value); - if (error < GIT_SUCCESS) - free(*var_name); + if (error != GIT_SUCCESS) + { + *var_value = NULL; + git__free(*var_name); + } goto out; } - tmp = strdup(value_start); + tmp = git__strdup(value_start); if (tmp == NULL) { - free(*var_name); + git__free(*var_name); + *var_value = NULL; error = GIT_ENOMEM; goto out; } @@ -1182,6 +1188,6 @@ static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_val } out: - free(line); + git__free(line); return error; } diff --git a/src/delta-apply.c b/src/delta-apply.c index e1fb15b9b..3e40bf8cf 100644 --- a/src/delta-apply.c +++ b/src/delta-apply.c @@ -109,7 +109,7 @@ int git__delta_apply( return GIT_SUCCESS; fail: - free(out->data); + git__free(out->data); out->data = NULL; return git__throw(GIT_ERROR, "Failed to apply delta"); } diff --git a/src/errors.c b/src/errors.c index 923cb3507..22b8ae7bf 100644 --- a/src/errors.c +++ b/src/errors.c @@ -7,14 +7,10 @@ #include "common.h" #include "errors.h" -#include "git2/thread-utils.h" /* for GIT_TLS */ -#include "thread-utils.h" /* for GIT_TLS */ #include "posix.h" - +#include "global.h" #include <stdarg.h> -GIT_TLS git_error *git_errno; - static struct { int num; const char *str; @@ -115,11 +111,11 @@ git_error * git_error_createf(const char *file, unsigned int line, int code, va_end(ap); err->code = code; - err->child = git_errno; + err->child = GIT_GLOBAL->git_errno; err->file = file; err->line = line; - git_errno = err; + GIT_GLOBAL->git_errno = err; return err; } @@ -149,13 +145,13 @@ void git_error_free(git_error *err) void git_clearerror(void) { - git_error_free(git_errno); - git_errno = NULL; + git_error_free(GIT_GLOBAL->git_errno); + GIT_GLOBAL->git_errno = NULL; } const char *git_lasterror(void) { - return git_errno == NULL ? NULL : git_errno->msg; + return GIT_GLOBAL->git_errno == NULL ? NULL : GIT_GLOBAL->git_errno->msg; } void git_error_print_stack(git_error *error_in) @@ -163,7 +159,7 @@ void git_error_print_stack(git_error *error_in) git_error *error; if (error_in == NULL) - error_in = git_errno; + error_in = GIT_GLOBAL->git_errno; for (error = error_in; error; error = error->child) fprintf(stderr, "%s:%u %s\n", error->file, error->line, error->msg); diff --git a/src/fetch.c b/src/fetch.c index ac7282819..af7dbaffd 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -14,6 +14,7 @@ #include "transport.h" #include "remote.h" #include "refspec.h" +#include "pack.h" #include "fetch.h" #include "netops.h" @@ -181,7 +182,7 @@ int git_fetch__download_pack(char **out, const char *buffered, size_t buffered_s } /* A bit dodgy, but we need to keep the pack at the temporary path */ - error = git_filebuf_commit_at(&file, file.path_lock); + error = git_filebuf_commit_at(&file, file.path_lock, GIT_PACK_FILE_MODE); cleanup: if (error < GIT_SUCCESS) git_filebuf_cleanup(&file); diff --git a/src/filebuf.c b/src/filebuf.c index 1a98e3f43..199418032 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -10,6 +10,8 @@ #include "filebuf.h" #include "fileops.h" +#define GIT_LOCK_FILE_MODE 0644 + static const size_t WRITE_BUFFER_SIZE = (4096 * 2); static int lock_file(git_filebuf *file, int flags) @@ -23,9 +25,10 @@ static int lock_file(git_filebuf *file, int flags) /* create path to the file buffer is required */ if (flags & GIT_FILEBUF_FORCE) { - file->fd = git_futils_creat_locked_withpath(file->path_lock, 0644); + /* XXX: Should dirmode here be configurable? Or is 0777 always fine? */ + file->fd = git_futils_creat_locked_withpath(file->path_lock, 0777, GIT_LOCK_FILE_MODE); } else { - file->fd = git_futils_creat_locked(file->path_lock, 0644); + file->fd = git_futils_creat_locked(file->path_lock, GIT_LOCK_FILE_MODE); } if (file->fd < 0) @@ -63,13 +66,13 @@ void git_filebuf_cleanup(git_filebuf *file) if (file->digest) git_hash_free_ctx(file->digest); - free(file->buffer); - free(file->z_buf); + git__free(file->buffer); + git__free(file->z_buf); deflateEnd(&file->zs); - free(file->path_original); - free(file->path_lock); + git__free(file->path_original); + git__free(file->path_lock); } GIT_INLINE(int) flush_buffer(git_filebuf *file) @@ -246,17 +249,17 @@ int git_filebuf_hash(git_oid *oid, git_filebuf *file) return GIT_SUCCESS; } -int git_filebuf_commit_at(git_filebuf *file, const char *path) +int git_filebuf_commit_at(git_filebuf *file, const char *path, mode_t mode) { - free(file->path_original); + git__free(file->path_original); file->path_original = git__strdup(path); if (file->path_original == NULL) return GIT_ENOMEM; - return git_filebuf_commit(file); + return git_filebuf_commit(file, mode); } -int git_filebuf_commit(git_filebuf *file) +int git_filebuf_commit(git_filebuf *file, mode_t mode) { int error; @@ -270,7 +273,12 @@ int git_filebuf_commit(git_filebuf *file) p_close(file->fd); file->fd = -1; - error = git_futils_mv_atomic(file->path_lock, file->path_original); + if (p_chmod(file->path_lock, mode)) { + error = git__throw(GIT_EOSERR, "Failed to chmod locked file before committing"); + goto cleanup; + } + + error = p_rename(file->path_lock, file->path_original); cleanup: git_filebuf_cleanup(file); @@ -368,12 +376,12 @@ int git_filebuf_printf(git_filebuf *file, const char *format, ...) va_end(arglist); if (len < 0) { - free(tmp_buffer); + git__free(tmp_buffer); return git__throw(GIT_EOSERR, "Failed to format string"); } error = git_filebuf_write(file, tmp_buffer, len); - free(tmp_buffer); + git__free(tmp_buffer); return error; } diff --git a/src/filebuf.h b/src/filebuf.h index 525ca3c81..d08505e8d 100644 --- a/src/filebuf.h +++ b/src/filebuf.h @@ -49,8 +49,8 @@ int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len); int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3); 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); +int git_filebuf_commit(git_filebuf *lock, mode_t mode); +int git_filebuf_commit_at(git_filebuf *lock, const char *path, mode_t mode); 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 203cce0a4..955bb1bf6 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -8,32 +8,8 @@ #include "fileops.h" #include <ctype.h> -int git_futils_mv_atomic(const char *from, const char *to) +int git_futils_mkpath2file(const char *file_path, const mode_t mode) { -#ifdef GIT_WIN32 - /* - * Win32 POSIX compilance my ass. If the destination - * file exists, the `rename` call fails. This is as - * close as it gets with the Win32 API. - */ - return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR; -#else - /* Don't even try this on Win32 */ - if (!link(from, to)) { - p_unlink(from); - return GIT_SUCCESS; - } - - if (!rename(from, to)) - return GIT_SUCCESS; - - return GIT_ERROR; -#endif -} - -int git_futils_mkpath2file(const char *file_path) -{ - const int mode = 0755; /* or 0777 ? */ int error = GIT_SUCCESS; char target_folder_path[GIT_PATH_MAX]; @@ -67,23 +43,23 @@ int git_futils_mktmp(char *path_out, const char *filename) return fd; } -int git_futils_creat_withpath(const char *path, int mode) +int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode) { - if (git_futils_mkpath2file(path) < GIT_SUCCESS) + if (git_futils_mkpath2file(path, dirmode) < GIT_SUCCESS) return git__throw(GIT_EOSERR, "Failed to create file %s", path); return p_creat(path, mode); } -int git_futils_creat_locked(const char *path, int mode) +int git_futils_creat_locked(const char *path, const mode_t mode) { int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode); return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to create locked file. Could not open %s", path); } -int git_futils_creat_locked_withpath(const char *path, int mode) +int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode) { - if (git_futils_mkpath2file(path) < GIT_SUCCESS) + if (git_futils_mkpath2file(path, dirmode) < GIT_SUCCESS) return git__throw(GIT_EOSERR, "Failed to create locked file %s", path); return git_futils_creat_locked(path, mode); @@ -181,7 +157,7 @@ int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mt if (p_read(fd, buff, len) < 0) { p_close(fd); - free(buff); + git__free(buff); return git__throw(GIT_ERROR, "Failed to read file `%s`", path); } buff[len] = '\0'; @@ -207,17 +183,17 @@ int git_futils_readbuffer(git_fbuffer *obj, const char *path) void git_futils_freebuffer(git_fbuffer *obj) { assert(obj); - free(obj->data); + git__free(obj->data); obj->data = NULL; } -int git_futils_mv_withpath(const char *from, const char *to) +int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode) { - if (git_futils_mkpath2file(to) < GIT_SUCCESS) + if (git_futils_mkpath2file(to, dirmode) < GIT_SUCCESS) return GIT_EOSERR; /* The callee already takes care of setting the correct error message. */ - return git_futils_mv_atomic(from, to); /* The callee already takes care of setting the correct error message. */ + return p_rename(from, to); /* The callee already takes care of setting the correct error message. */ } int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len) @@ -289,7 +265,7 @@ int git_futils_direach( return GIT_SUCCESS; } -int git_futils_mkdir_r(const char *path, int mode) +int git_futils_mkdir_r(const char *path, const mode_t mode) { int error, root_path_offset; char *pp, *sp; @@ -326,7 +302,7 @@ int git_futils_mkdir_r(const char *path, int mode) error = GIT_SUCCESS; } - free(path_copy); + git__free(path_copy); if (error < GIT_SUCCESS) return git__throw(error, "Failed to recursively create `%s` tree structure", path); diff --git a/src/fileops.h b/src/fileops.h index 5b69199d2..56c4770cb 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -48,18 +48,18 @@ extern int git_futils_exists(const char *path); * Create and open a file, while also * creating all the folders in its path */ -extern int git_futils_creat_withpath(const char *path, int mode); +extern int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode); /** * Create an open a process-locked file */ -extern int git_futils_creat_locked(const char *path, int mode); +extern int git_futils_creat_locked(const char *path, const mode_t mode); /** * Create an open a process-locked file, while * also creating all the folders in its path */ -extern int git_futils_creat_locked_withpath(const char *path, int mode); +extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode); /** * Check if the given path points to a directory @@ -74,13 +74,13 @@ extern int git_futils_isfile(const char *path); /** * Create a path recursively */ -extern int git_futils_mkdir_r(const char *path, int mode); +extern int git_futils_mkdir_r(const char *path, const mode_t mode); /** * Create all the folders required to contain * the full path of a file */ -extern int git_futils_mkpath2file(const char *path); +extern int git_futils_mkpath2file(const char *path, const mode_t mode); extern int git_futils_rmdir_r(const char *path, int force); @@ -98,7 +98,7 @@ extern int git_futils_mv_atomic(const char *from, const char *to); * Move a file on the filesystem, create the * destination path if it doesn't exist */ -extern int git_futils_mv_withpath(const char *from, const char *to); +extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode); /** diff --git a/src/global.c b/src/global.c new file mode 100644 index 000000000..8ef286ef0 --- /dev/null +++ b/src/global.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2009-2011 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#include "common.h" +#include "global.h" +#include "git2/threads.h" +#include "thread-utils.h" + +/** + * Handle the global state with TLS + * + * If libgit2 is built with GIT_THREADS enabled, + * the `git_threads_init()` function must be called + * before calling any other function of the library. + * + * This function allocates a TLS index (using pthreads + * or the native Win32 API) to store the global state + * on a per-thread basis. + * + * Any internal method that requires global state will + * then call `git__global_state()` which returns a pointer + * to the global state structure; this pointer is lazily + * allocated on each thread. + * + * Before shutting down the library, the + * `git_threads_shutdown` method must be called to free + * the previously reserved TLS index. + * + * If libgit2 is built without threading support, the + * `git__global_statestate()` call returns a pointer to a single, + * statically allocated global state. The `git_thread_` + * functions are not available in that case. + */ + +#if defined(GIT_THREADS) && defined(GIT_WIN32) + +static DWORD _tls_index; +static int _tls_init = 0; + +void git_threads_init(void) +{ + if (_tls_init) + return; + + _tls_index = TlsAlloc(); + _tls_init = 1; +} + +void git_threads_shutdown(void) +{ + TlsFree(_tls_index); + _tls_init = 0; +} + +git_global_st *git__global_state(void) +{ + void *ptr; + + if ((ptr = TlsGetValue(_tls_index)) != NULL) + return ptr; + + ptr = malloc(sizeof(git_global_st)); + if (!ptr) + return NULL; + + memset(ptr, 0x0, sizeof(git_global_st)); + TlsSetValue(_tls_index, ptr); + return ptr; +} + +#elif defined(GIT_THREADS) && defined(_POSIX_THREADS) + +static pthread_key_t _tls_key; +static int _tls_init = 0; + +static void cb__free_status(void *st) +{ + free(st); +} + +void git_threads_init(void) +{ + if (_tls_init) + return; + + pthread_key_create(&_tls_key, &cb__free_status); + _tls_init = 1; +} + +void git_threads_shutdown(void) +{ + pthread_key_delete(_tls_key); + _tls_init = 0; +} + +git_global_st *git__global_state(void) +{ + void *ptr; + + if ((ptr = pthread_getspecific(_tls_key)) != NULL) + return ptr; + + ptr = malloc(sizeof(git_global_st)); + if (!ptr) + return NULL; + + memset(ptr, 0x0, sizeof(git_global_st)); + pthread_setspecific(_tls_key, ptr); + return ptr; +} + +#else + +static git_global_st __state; + +void git_threads_init(void) +{ + /* noop */ +} + +void git_threads_shutdown(void) +{ + /* noop */ +} + +git_global_st *git__global_state(void) +{ + return &__state; +} + +#endif /* GIT_THREADS */ diff --git a/src/global.h b/src/global.h new file mode 100644 index 000000000..6a15a8d02 --- /dev/null +++ b/src/global.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2009-2011 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_global_h__ +#define INCLUDE_global_h__ + +#include "mwindow.h" +#include "git2/types.h" + +typedef struct { + git_error *git_errno; + git_mwindow_ctl mem_ctl; +} git_global_st; + +git_global_st *git__global_state(void); + +#define GIT_GLOBAL (git__global_state()) + +#endif diff --git a/src/hash.c b/src/hash.c index ff85ca957..56063cc0b 100644 --- a/src/hash.c +++ b/src/hash.c @@ -32,7 +32,7 @@ git_hash_ctx *git_hash_new_ctx(void) void git_hash_free_ctx(git_hash_ctx *ctx) { - free(ctx); + git__free(ctx); } void git_hash_init(git_hash_ctx *ctx) diff --git a/src/hashtable.c b/src/hashtable.c index 1382eabaa..15d173992 100644 --- a/src/hashtable.c +++ b/src/hashtable.c @@ -39,17 +39,17 @@ static int resize_to(git_hashtable *self, size_t new_size) self->is_resizing = 0; else { new_size *= 2; - free(self->nodes); + git__free(self->nodes); } } while(self->is_resizing); - free(old_nodes); + git__free(old_nodes); return GIT_SUCCESS; } static int set_size(git_hashtable *self, size_t new_size) { - self->nodes = realloc(self->nodes, new_size * sizeof(git_hashtable_node)); + self->nodes = git__realloc(self->nodes, new_size * sizeof(git_hashtable_node)); if (self->nodes == NULL) return GIT_ENOMEM; @@ -156,8 +156,8 @@ void git_hashtable_free(git_hashtable *self) { assert(self); - free(self->nodes); - free(self); + git__free(self->nodes); + git__free(self); } diff --git a/src/index.c b/src/index.c index 7bf5daf2c..1a9745a2c 100644 --- a/src/index.c +++ b/src/index.c @@ -138,7 +138,7 @@ static int index_initialize(git_index **index_out, git_repository *owner, const index->index_file_path = git__strdup(index_path); if (index->index_file_path == NULL) { - free(index); + git__free(index); return GIT_ENOMEM; } @@ -179,8 +179,8 @@ void git_index_free(git_index *index) git_vector_free(&index->entries); git_vector_free(&index->unmerged); - free(index->index_file_path); - free(index); + git__free(index->index_file_path); + git__free(index); } void git_index_clear(git_index *index) @@ -192,15 +192,15 @@ void git_index_clear(git_index *index) for (i = 0; i < index->entries.length; ++i) { git_index_entry *e; e = git_vector_get(&index->entries, i); - free(e->path); - free(e); + git__free(e->path); + git__free(e); } for (i = 0; i < index->unmerged.length; ++i) { git_index_entry_unmerged *e; e = git_vector_get(&index->unmerged, i); - free(e->path); - free(e); + git__free(e->path); + git__free(e); } git_vector_clear(&index->entries); @@ -262,7 +262,7 @@ int git_index_write(git_index *index) return git__rethrow(error, "Failed to write index"); } - if ((error = git_filebuf_commit(&file)) < GIT_SUCCESS) + if ((error = git_filebuf_commit(&file, GIT_INDEX_FILE_MODE)) < GIT_SUCCESS) return git__rethrow(error, "Failed to write index"); if (p_stat(index->index_file_path, &indexst) == 0) { @@ -334,7 +334,7 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT); entry->path = git__strdup(rel_path); if (entry->path == NULL) { - free(entry); + git__free(entry); return GIT_ENOMEM; } @@ -364,8 +364,8 @@ static void index_entry_free(git_index_entry *entry) { if (!entry) return; - free(entry->path); - free(entry); + git__free(entry->path); + git__free(entry); } static int index_insert(git_index *index, git_index_entry *entry, int replace) @@ -416,8 +416,8 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace) /* exists, replace it */ entry_array = (git_index_entry **) index->entries.contents; - free(entry_array[position]->path); - free(entry_array[position]); + git__free(entry_array[position]->path); + git__free(entry_array[position]); entry_array[position] = entry; return GIT_SUCCESS; diff --git a/src/index.h b/src/index.h index e912770b7..a1cd3403e 100644 --- a/src/index.h +++ b/src/index.h @@ -14,6 +14,9 @@ #include "git2/odb.h" #include "git2/index.h" +#define GIT_INDEX_FILE "index" +#define GIT_INDEX_FILE_MODE 0666 + struct git_index { git_repository *repository; char *index_file_path; diff --git a/src/indexer.c b/src/indexer.c index d5f605fdb..a69ab850c 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -272,7 +272,7 @@ int git_indexer_write(git_indexer *idx) /* Figure out what the final name should be */ index_path(filename, idx); /* Commit file */ - error = git_filebuf_commit_at(&idx->file, filename); + error = git_filebuf_commit_at(&idx->file, filename, GIT_PACK_FILE_MODE); cleanup: git_mwindow_free_all(&idx->pack->mwf); @@ -367,7 +367,7 @@ int git_indexer_run(git_indexer *idx, git_indexer_stats *stats) idx->fanout[i]++; } - free(obj.data); + git__free(obj.data); stats->processed = ++processed; } @@ -390,12 +390,12 @@ void git_indexer_free(git_indexer *idx) p_close(idx->pack->mwf.fd); git_vector_foreach(&idx->objects, i, e) - free(e); + git__free(e); git_vector_free(&idx->objects); git_vector_foreach(&idx->pack->cache, i, pe) - free(pe); + git__free(pe); git_vector_free(&idx->pack->cache); - free(idx->pack); - free(idx); + git__free(idx->pack); + git__free(idx); } diff --git a/src/mwindow.c b/src/mwindow.c index e53477e98..8dc4573b4 100644 --- a/src/mwindow.c +++ b/src/mwindow.c @@ -10,6 +10,7 @@ #include "vector.h" #include "fileops.h" #include "map.h" +#include "global.h" #define DEFAULT_WINDOW_SIZE \ (sizeof(void*) >= 8 \ @@ -20,21 +21,15 @@ ((1024 * 1024) * (sizeof(void*) >= 8 ? 8192ULL : 256UL)) /* - * We need this because each process is only allowed a specific amount - * of memory. Making it writable should generate one instance per - * process, but we still need to set a couple of variables. + * These are the global options for mmmap limits. + * TODO: allow the user to change these */ - -static git_mwindow_ctl ctl = { - 0, - 0, +static struct { + size_t window_size; + size_t mapped_limit; +} _mw_options = { DEFAULT_WINDOW_SIZE, DEFAULT_MAPPED_LIMIT, - 0, - 0, - 0, - 0, - {0, 0, 0, 0, 0} }; /* @@ -43,33 +38,34 @@ static git_mwindow_ctl ctl = { */ void git_mwindow_free_all(git_mwindow_file *mwf) { + git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl; unsigned int i; /* * Remove these windows from the global list */ - for (i = 0; i < ctl.windowfiles.length; ++i){ - if (git_vector_get(&ctl.windowfiles, i) == mwf) { - git_vector_remove(&ctl.windowfiles, i); + for (i = 0; i < ctl->windowfiles.length; ++i){ + if (git_vector_get(&ctl->windowfiles, i) == mwf) { + git_vector_remove(&ctl->windowfiles, i); break; } } - if (ctl.windowfiles.length == 0) { - git_vector_free(&ctl.windowfiles); - ctl.windowfiles.contents = NULL; + if (ctl->windowfiles.length == 0) { + git_vector_free(&ctl->windowfiles); + ctl->windowfiles.contents = NULL; } while (mwf->windows) { git_mwindow *w = mwf->windows; assert(w->inuse_cnt == 0); - ctl.mapped -= w->window_map.len; - ctl.open_windows--; + ctl->mapped -= w->window_map.len; + ctl->open_windows--; git_futils_mmap_free(&w->window_map); mwf->windows = w->next; - free(w); + git__free(w); } } @@ -115,6 +111,7 @@ void git_mwindow_scan_lru( */ static int git_mwindow_close_lru(git_mwindow_file *mwf) { + git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl; unsigned int i; git_mwindow *lru_w = NULL, *lru_l = NULL, **list = &mwf->windows; @@ -122,16 +119,16 @@ static int git_mwindow_close_lru(git_mwindow_file *mwf) if(mwf->windows) git_mwindow_scan_lru(mwf, &lru_w, &lru_l); - for (i = 0; i < ctl.windowfiles.length; ++i) { + for (i = 0; i < ctl->windowfiles.length; ++i) { git_mwindow *last = lru_w; - git_mwindow_file *cur = git_vector_get(&ctl.windowfiles, i); + git_mwindow_file *cur = git_vector_get(&ctl->windowfiles, i); git_mwindow_scan_lru(cur, &lru_w, &lru_l); if (lru_w != last) list = &cur->windows; } if (lru_w) { - ctl.mapped -= lru_w->window_map.len; + ctl->mapped -= lru_w->window_map.len; git_futils_mmap_free(&lru_w->window_map); if (lru_l) @@ -139,8 +136,8 @@ static int git_mwindow_close_lru(git_mwindow_file *mwf) else *list = lru_w->next; - free(lru_w); - ctl.open_windows--; + git__free(lru_w); + ctl->open_windows--; return GIT_SUCCESS; } @@ -148,9 +145,14 @@ static int git_mwindow_close_lru(git_mwindow_file *mwf) return git__throw(GIT_ERROR, "Failed to close memory window. Couln't find LRU"); } -static git_mwindow *new_window(git_mwindow_file *mwf, git_file fd, git_off_t size, git_off_t offset) +static git_mwindow *new_window( + git_mwindow_file *mwf, + git_file fd, + git_off_t size, + git_off_t offset) { - size_t walign = ctl.window_size / 2; + git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl; + size_t walign = _mw_options.window_size / 2; git_off_t len; git_mwindow *w; @@ -162,16 +164,16 @@ static git_mwindow *new_window(git_mwindow_file *mwf, git_file fd, git_off_t siz w->offset = (offset / walign) * walign; len = size - w->offset; - if (len > (git_off_t)ctl.window_size) - len = (git_off_t)ctl.window_size; + if (len > (git_off_t)_mw_options.window_size) + len = (git_off_t)_mw_options.window_size; - ctl.mapped += (size_t)len; + ctl->mapped += (size_t)len; - while(ctl.mapped_limit < ctl.mapped && - git_mwindow_close_lru(mwf) == GIT_SUCCESS) {} + while (_mw_options.mapped_limit < ctl->mapped && + git_mwindow_close_lru(mwf) == GIT_SUCCESS) /* nop */; /* - * We treat ctl.mapped_limit as a soft limit. If we can't find a + * We treat _mw_options.mapped_limit as a soft limit. If we can't find a * window to close and are above the limit, we still mmap the new * window. */ @@ -179,19 +181,19 @@ static git_mwindow *new_window(git_mwindow_file *mwf, git_file fd, git_off_t siz if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < GIT_SUCCESS) goto cleanup; - ctl.mmap_calls++; - ctl.open_windows++; + ctl->mmap_calls++; + ctl->open_windows++; - if (ctl.mapped > ctl.peak_mapped) - ctl.peak_mapped = ctl.mapped; + if (ctl->mapped > ctl->peak_mapped) + ctl->peak_mapped = ctl->mapped; - if (ctl.open_windows > ctl.peak_open_windows) - ctl.peak_open_windows = ctl.open_windows; + if (ctl->open_windows > ctl->peak_open_windows) + ctl->peak_open_windows = ctl->open_windows; return w; cleanup: - free(w); + git__free(w); return NULL; } @@ -199,9 +201,14 @@ cleanup: * Open a new window, closing the least recenty used until we have * enough space. Don't forget to add it to your list */ -unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, - git_off_t offset, int extra, unsigned int *left) +unsigned char *git_mwindow_open( + git_mwindow_file *mwf, + git_mwindow **cursor, + git_off_t offset, + int extra, + unsigned int *left) { + git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl; git_mwindow *w = *cursor; if (!w || !git_mwindow_contains(w, offset + extra)) { @@ -229,7 +236,7 @@ unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, /* If we changed w, store it in the cursor */ if (w != *cursor) { - w->last_used = ctl.used_ctr++; + w->last_used = ctl->used_ctr++; w->inuse_cnt++; *cursor = w; } @@ -245,13 +252,14 @@ unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, int git_mwindow_file_register(git_mwindow_file *mwf) { + git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl; int error; - if (ctl.windowfiles.length == 0 && - (error = git_vector_init(&ctl.windowfiles, 8, NULL)) < GIT_SUCCESS) + if (ctl->windowfiles.length == 0 && + (error = git_vector_init(&ctl->windowfiles, 8, NULL)) < GIT_SUCCESS) return error; - return git_vector_insert(&ctl.windowfiles, mwf); + return git_vector_insert(&ctl->windowfiles, mwf); } void git_mwindow_close(git_mwindow **window) diff --git a/src/mwindow.h b/src/mwindow.h index ec75f901f..11c3aa840 100644 --- a/src/mwindow.h +++ b/src/mwindow.h @@ -10,7 +10,6 @@ #include "map.h" #include "vector.h" -#include "fileops.h" typedef struct git_mwindow { struct git_mwindow *next; @@ -29,8 +28,6 @@ typedef struct git_mwindow_file { typedef struct git_mwindow_ctl { size_t mapped; unsigned int open_windows; - size_t window_size; /* needs default value */ - size_t mapped_limit; /* needs default value */ unsigned int mmap_calls; unsigned int peak_open_windows; size_t peak_mapped; diff --git a/src/netops.c b/src/netops.c index dad296a94..73375d725 100644 --- a/src/netops.c +++ b/src/netops.c @@ -190,7 +190,7 @@ int gitno_extract_host_and_port(char **host, char **port, const char *url, const delim = colon == NULL ? slash : colon; *host = git__strndup(url, delim - url); if (*host == NULL) { - free(*port); + git__free(*port); error = GIT_ENOMEM; } diff --git a/src/object.c b/src/object.c index edc2d80fa..c84e94b05 100644 --- a/src/object.c +++ b/src/object.c @@ -213,7 +213,7 @@ void git_object__free(void *_obj) break; default: - free(object); + git__free(object); break; } } @@ -83,8 +83,8 @@ static void free_odb_object(void *o) git_odb_object *object = (git_odb_object *)o; if (object != NULL) { - free(object->raw.data); - free(object); + git__free(object->raw.data); + git__free(object); } } @@ -205,8 +205,8 @@ static void fake_wstream__free(git_odb_stream *_stream) { fake_wstream *stream = (fake_wstream *)_stream; - free(stream->buffer); - free(stream); + git__free(stream->buffer); + git__free(stream); } static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend, size_t size, git_otype type) @@ -221,7 +221,7 @@ static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend stream->type = type; stream->buffer = git__malloc(size); if (stream->buffer == NULL) { - free(stream); + git__free(stream); return GIT_ENOMEM; } @@ -265,12 +265,12 @@ int git_odb_new(git_odb **out) error = git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object); if (error < GIT_SUCCESS) { - free(db); + git__free(db); return git__rethrow(error, "Failed to create object database"); } if ((error = git_vector_init(&db->backends, 4, backend_sort_cmp)) < GIT_SUCCESS) { - free(db); + git__free(db); return git__rethrow(error, "Failed to create object database"); } @@ -296,7 +296,7 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio internal->is_alternate = is_alternate; if (git_vector_insert(&odb->backends, internal) < 0) { - free(internal); + git__free(internal); return GIT_ENOMEM; } @@ -421,14 +421,14 @@ void git_odb_close(git_odb *db) git_odb_backend *backend = internal->backend; if (backend->free) backend->free(backend); - else free(backend); + else git__free(backend); - free(internal); + git__free(internal); } git_vector_free(&db->backends); git_cache_free(&db->cache); - free(db); + git__free(db); } int git_odb_exists(git_odb *db, const git_oid *id) @@ -14,6 +14,10 @@ #include "vector.h" #include "cache.h" +#define GIT_OBJECTS_DIR "objects/" +#define GIT_OBJECT_DIR_MODE 0777 +#define GIT_OBJECT_FILE_MODE 0444 + /* DO NOT EXPORT */ typedef struct { void *data; /**< Raw, decompressed object data. */ diff --git a/src/odb_loose.c b/src/odb_loose.c index dbfe18b43..57a0b0a8e 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -277,7 +277,7 @@ static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr) else { set_stream_output(s, buf + used, hdr->size - used); if (finish_inflate(s)) { - free(buf); + git__free(buf); return NULL; } } @@ -317,7 +317,7 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj) in = ((unsigned char *)obj->data) + used; len = obj->len - used; if (inflate_buffer(in, len, buf, hdr.size)) { - free(buf); + git__free(buf); return git__throw(GIT_ERROR, "Failed to inflate loose object. Could not inflate buffer"); } buf[hdr.size] = '\0'; @@ -666,11 +666,22 @@ static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream) if (object_file_name(final_path, sizeof(final_path), backend->objects_dir, oid)) return GIT_ENOMEM; - if ((error = git_futils_mkpath2file(final_path)) < GIT_SUCCESS) + if ((error = git_futils_mkpath2file(final_path, GIT_OBJECT_DIR_MODE)) < GIT_SUCCESS) return git__rethrow(error, "Failed to write loose backend"); stream->finished = 1; - return git_filebuf_commit_at(&stream->fbuf, final_path); + + /* + * Don't try to add an existing object to the repository. This + * is what git does and allows us to sidestep the fact that + * we're not allowed to overwrite a read-only file on Windows. + */ + if (git_futils_exists(final_path) == GIT_SUCCESS) { + git_filebuf_cleanup(&stream->fbuf); + return GIT_SUCCESS; + } + + return git_filebuf_commit_at(&stream->fbuf, final_path, GIT_OBJECT_FILE_MODE); } static int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len) @@ -686,7 +697,7 @@ static void loose_backend__stream_free(git_odb_stream *_stream) if (!stream->finished) git_filebuf_cleanup(&stream->fbuf); - free(stream); + git__free(stream); } static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type) @@ -739,14 +750,14 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_ (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)); if (error < GIT_SUCCESS) { - free(stream); + git__free(stream); return git__rethrow(error, "Failed to create loose backend stream"); } error = stream->stream.write((git_odb_stream *)stream, hdr, hdrlen); if (error < GIT_SUCCESS) { git_filebuf_cleanup(&stream->fbuf); - free(stream); + git__free(stream); return git__rethrow(error, "Failed to create loose backend stream"); } @@ -787,10 +798,10 @@ static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const v if ((error = object_file_name(final_path, sizeof(final_path), backend->objects_dir, oid)) < GIT_SUCCESS) goto cleanup; - if ((error = git_futils_mkpath2file(final_path)) < GIT_SUCCESS) + if ((error = git_futils_mkpath2file(final_path, GIT_OBJECT_DIR_MODE)) < GIT_SUCCESS) goto cleanup; - return git_filebuf_commit_at(&fbuf, final_path); + return git_filebuf_commit_at(&fbuf, final_path, GIT_OBJECT_FILE_MODE); cleanup: git_filebuf_cleanup(&fbuf); @@ -803,8 +814,8 @@ static void loose_backend__free(git_odb_backend *_backend) assert(_backend); backend = (loose_backend *)_backend; - free(backend->objects_dir); - free(backend); + git__free(backend->objects_dir); + git__free(backend); } int git_odb_backend_loose( @@ -821,7 +832,7 @@ int git_odb_backend_loose( backend->objects_dir = git__strdup(objects_dir); if (backend->objects_dir == NULL) { - free(backend); + git__free(backend); return GIT_ENOMEM; } diff --git a/src/odb_pack.c b/src/odb_pack.c index a8f854236..800e7b0da 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -231,7 +231,7 @@ static int packfile_load__cb(void *_data, char *path) return git__rethrow(error, "Failed to load packfile"); if (git_vector_insert(&backend->packs, pack) < GIT_SUCCESS) { - free(pack); + git__free(pack); return GIT_ENOMEM; } @@ -445,8 +445,8 @@ static void pack_backend__free(git_odb_backend *_backend) } git_vector_free(&backend->packs); - free(backend->pack_folder); - free(backend); + git__free(backend->pack_folder); + git__free(backend); } int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) @@ -459,7 +459,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) return GIT_ENOMEM; if (git_vector_init(&backend->packs, 8, packfile_sort__cb) < GIT_SUCCESS) { - free(backend); + git__free(backend); return GIT_ENOMEM; } @@ -469,7 +469,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) backend->pack_folder_mtime = 0; if (backend->pack_folder == NULL) { - free(backend); + git__free(backend); return GIT_ENOMEM; } } @@ -223,7 +223,7 @@ struct git_oid_shorten { static int resize_trie(git_oid_shorten *self, size_t new_size) { - self->nodes = realloc(self->nodes, new_size * sizeof(trie_node)); + self->nodes = git__realloc(self->nodes, new_size * sizeof(trie_node)); if (self->nodes == NULL) return GIT_ENOMEM; @@ -270,7 +270,7 @@ git_oid_shorten *git_oid_shorten_new(size_t min_length) memset(os, 0x0, sizeof(git_oid_shorten)); if (resize_trie(os, 16) < GIT_SUCCESS) { - free(os); + git__free(os); return NULL; } @@ -282,8 +282,8 @@ git_oid_shorten *git_oid_shorten_new(size_t min_length) void git_oid_shorten_free(git_oid_shorten *os) { - free(os->nodes); - free(os); + git__free(os->nodes); + git__free(os); } diff --git a/src/pack.c b/src/pack.c index 2516bea93..ae954b988 100644 --- a/src/pack.c +++ b/src/pack.c @@ -5,11 +5,13 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "mwindow.h" +#include "common.h" #include "odb.h" #include "pack.h" #include "delta-apply.h" #include "sha1_lookup.h" +#include "mwindow.h" +#include "fileops.h" #include "git2/oid.h" #include "git2/zlib.h" @@ -181,7 +183,7 @@ static int pack_index_open(struct git_pack_file *p) strcpy(idx_name + strlen(idx_name) - strlen(".pack"), ".idx"); error = pack_index_check(idx_name, p); - free(idx_name); + git__free(idx_name); return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to open index"); } @@ -297,7 +299,7 @@ static int packfile_unpack_delta( error = packfile_unpack_compressed(&delta, p, w_curs, curpos, delta_size, delta_type); if (error < GIT_SUCCESS) { - free(base.data); + git__free(base.data); return git__rethrow(error, "Corrupted delta"); } @@ -306,8 +308,8 @@ static int packfile_unpack_delta( base.data, base.len, delta.data, delta.len); - free(base.data); - free(delta.data); + git__free(base.data); + git__free(delta.data); /* TODO: we might want to cache this shit. eventually */ //add_delta_base_cache(p, base_offset, base, base_size, *type); @@ -390,7 +392,7 @@ int packfile_unpack_compressed( st = inflateInit(&stream); if (st != Z_OK) { - free(buffer); + git__free(buffer); return git__throw(GIT_EZLIB, "Error in zlib"); } @@ -408,7 +410,7 @@ int packfile_unpack_compressed( inflateEnd(&stream); if ((st != Z_STREAM_END) || stream.total_out != size) { - free(buffer); + git__free(buffer); return git__throw(GIT_EZLIB, "Error in zlib"); } @@ -504,8 +506,8 @@ void packfile_free(struct git_pack_file *p) pack_index_free(p); - free(p->bad_object_sha1); - free(p); + git__free(p->bad_object_sha1); + git__free(p); } static int packfile_open(struct git_pack_file *p) @@ -598,7 +600,7 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path) */ path_len -= strlen(".idx"); if (path_len < 1) { - free(p); + git__free(p); return git__throw(GIT_ENOTFOUND, "Failed to check packfile. Wrong path name"); } @@ -610,7 +612,7 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path) strcpy(p->pack_name + path_len, ".pack"); if (p_stat(p->pack_name, &st) < GIT_SUCCESS || !S_ISREG(st.st_mode)) { - free(p); + git__free(p); return git__throw(GIT_ENOTFOUND, "Failed to check packfile. File not found"); } diff --git a/src/pack.h b/src/pack.h index 0fddd9dc8..aecf580e9 100644 --- a/src/pack.h +++ b/src/pack.h @@ -15,6 +15,8 @@ #include "mwindow.h" #include "odb.h" +#define GIT_PACK_FILE_MODE 0444 + #define PACK_SIGNATURE 0x5041434b /* "PACK" */ #define PACK_VERSION 2 #define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3)) diff --git a/src/path.c b/src/path.c index 2c6b76dd0..a8851dfdc 100644 --- a/src/path.c +++ b/src/path.c @@ -144,7 +144,7 @@ char *git_path_dirname(const char *path) return NULL; if (git_path_dirname_r(dname, len, path) < GIT_SUCCESS) { - free(dname); + git__free(dname); return NULL; } @@ -162,7 +162,7 @@ char *git_path_basename(const char *path) return NULL; if (git_path_basename_r(bname, len, path) < GIT_SUCCESS) { - free(bname); + git__free(bname); return NULL; } @@ -149,7 +149,7 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len) out: if (error < GIT_SUCCESS) - free(pkt); + git__free(pkt); else *out = (git_pkt *)pkt; @@ -260,10 +260,10 @@ void git_pkt_free(git_pkt *pkt) { if(pkt->type == GIT_PKT_REF) { git_pkt_ref *p = (git_pkt_ref *) pkt; - free(p->head.name); + git__free(p->head.name); } - free(pkt); + git__free(pkt); } int git_pkt_buffer_flush(git_buf *buf) diff --git a/src/posix.c b/src/posix.c index 1b85b053d..8c19588ee 100644 --- a/src/posix.c +++ b/src/posix.c @@ -17,7 +17,7 @@ int p_open(const char *path, int flags) return open(path, flags | O_BINARY); } -int p_creat(const char *path, int mode) +int p_creat(const char *path, mode_t mode) { return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode); } @@ -39,6 +39,20 @@ int p_getcwd(char *buffer_out, size_t size) return GIT_SUCCESS; } +int p_rename(const char *from, const char *to) +{ + if (!link(from, to)) { + p_unlink(from); + return GIT_SUCCESS; + } + + if (!rename(from, to)) + return GIT_SUCCESS; + + return GIT_ERROR; + +} + #endif int p_read(git_file fd, void *buf, size_t cnt) diff --git a/src/posix.h b/src/posix.h index 59bec2794..c12b41364 100644 --- a/src/posix.h +++ b/src/posix.h @@ -40,10 +40,12 @@ extern int p_write(git_file fd, const void *buf, size_t cnt); #define p_fstat(f,b) fstat(f, b) #define p_lseek(f,n,w) lseek(f, n, w) #define p_close(fd) close(fd) +#define p_umask(m) umask(m) extern int p_open(const char *path, int flags); -extern int p_creat(const char *path, int mode); +extern int p_creat(const char *path, mode_t mode); extern int p_getcwd(char *buffer_out, size_t size); +extern int p_rename(const char *from, const char *to); #ifndef GIT_WIN32 diff --git a/src/pqueue.c b/src/pqueue.c index b5ddab835..80713fbba 100644 --- a/src/pqueue.c +++ b/src/pqueue.c @@ -17,7 +17,7 @@ int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri) assert(q); /* Need to allocate n+1 elements since element 0 isn't used. */ - if ((q->d = malloc((n + 1) * sizeof(void *))) == NULL) + if ((q->d = git__malloc((n + 1) * sizeof(void *))) == NULL) return GIT_ENOMEM; q->size = 1; @@ -30,7 +30,7 @@ int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri) void git_pqueue_free(git_pqueue *q) { - free(q->d); + git__free(q->d); q->d = NULL; } @@ -102,7 +102,7 @@ int git_pqueue_insert(git_pqueue *q, void *d) /* allocate more memory if necessary */ if (q->size >= q->avail) { newsize = q->size + q->step; - if ((tmp = realloc(q->d, sizeof(void *) * newsize)) == NULL) + if ((tmp = git__realloc(q->d, sizeof(void *) * newsize)) == NULL) return GIT_ENOMEM; q->d = tmp; diff --git a/src/reflog.c b/src/reflog.c index 594963c03..e0fa7a060 100644 --- a/src/reflog.c +++ b/src/reflog.c @@ -25,8 +25,8 @@ static int reflog_init(git_reflog **reflog, git_reference *ref) log->ref_name = git__strdup(ref->name); if (git_vector_init(&log->entries, 0, NULL) < 0) { - free(log->ref_name); - free(log); + git__free(log->ref_name); + git__free(log); return GIT_ENOMEM; } @@ -71,7 +71,7 @@ static int reflog_write(const char *log_path, const char *oid_old, } git_filebuf_write(&fbuf, log.ptr, log.size); - error = git_filebuf_commit(&fbuf); + error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE); git_buf_free(&log); return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write reflog"); @@ -86,8 +86,8 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) #define seek_forward(_increase) { \ if (_increase >= buf_size) { \ if (entry->committer) \ - free(entry->committer); \ - free(entry); \ + git__free(entry->committer); \ + git__free(entry); \ return git__throw(GIT_ERROR, "Failed to seek forward. Buffer size exceeded"); \ } \ buf += _increase; \ @@ -101,13 +101,13 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) entry->committer = NULL; if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < GIT_SUCCESS) { - free(entry); + git__free(entry); return GIT_ERROR; } seek_forward(GIT_OID_HEXSZ + 1); if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < GIT_SUCCESS) { - free(entry); + git__free(entry); return GIT_ERROR; } seek_forward(GIT_OID_HEXSZ + 1); @@ -120,13 +120,13 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) entry->committer = git__malloc(sizeof(git_signature)); if (entry->committer == NULL) { - free(entry); + git__free(entry); return GIT_ENOMEM; } if ((error = git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf)) < GIT_SUCCESS) { - free(entry->committer); - free(entry); + git__free(entry->committer); + git__free(entry); return git__rethrow(error, "Failed to parse reflog. Could not parse signature"); } @@ -164,13 +164,13 @@ void git_reflog_free(git_reflog *reflog) git_signature_free(entry->committer); - free(entry->msg); - free(entry); + git__free(entry->msg); + git__free(entry); } git_vector_free(&reflog->entries); - free(reflog->ref_name); - free(reflog); + git__free(reflog->ref_name); + git__free(reflog); } int git_reflog_read(git_reflog **reflog, git_reference *ref) @@ -215,23 +215,37 @@ int git_reflog_write(git_reference *ref, const git_oid *oid_old, const git_oid *oid; if ((error = git_reference_resolve(&r, ref)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to write reflog. Cannot resolve reference `%s`", ref->name); + return git__rethrow(error, + "Failed to write reflog. Cannot resolve reference `%s`", ref->name); oid = git_reference_oid(r); - if (oid == NULL) - return git__throw(GIT_ERROR, "Failed to write reflog. Cannot resolve reference `%s`", r->name); + if (oid == NULL) { + git_reference_free(r); + return git__throw(GIT_ERROR, + "Failed to write reflog. Cannot resolve reference `%s`", r->name); + } git_oid_to_string(new, GIT_OID_HEXSZ+1, oid); - git_path_join_n(log_path, 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name); + git_path_join_n(log_path, 3, + ref->owner->path_repository, GIT_REFLOG_DIR, ref->name); + + git_reference_free(r); if (git_futils_exists(log_path)) { - if ((error = git_futils_mkpath2file(log_path)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to write reflog. Cannot create reflog directory"); + error = git_futils_mkpath2file(log_path, GIT_REFLOG_DIR_MODE); + if (error < GIT_SUCCESS) + return git__rethrow(error, + "Failed to write reflog. Cannot create reflog directory"); + } else if (git_futils_isfile(log_path)) { - return git__throw(GIT_ERROR, "Failed to write reflog. `%s` is directory", log_path); - } else if (oid_old == NULL) - return git__throw(GIT_ERROR, "Failed to write reflog. Old OID cannot be NULL for existing reference"); + return git__throw(GIT_ERROR, + "Failed to write reflog. `%s` is directory", log_path); + + } else if (oid_old == NULL) { + return git__throw(GIT_ERROR, + "Failed to write reflog. Old OID cannot be NULL for existing reference"); + } if (oid_old) git_oid_to_string(old, GIT_OID_HEXSZ+1, oid_old); diff --git a/src/reflog.h b/src/reflog.h index 093874e51..44b063700 100644 --- a/src/reflog.h +++ b/src/reflog.h @@ -12,6 +12,8 @@ #include "vector.h" #define GIT_REFLOG_DIR "logs/" +#define GIT_REFLOG_DIR_MODE 0777 +#define GIT_REFLOG_FILE_MODE 0666 #define GIT_REFLOG_SIZE_MIN (2*GIT_OID_HEXSZ+2+17) diff --git a/src/refs.c b/src/refs.c index fcf771b5e..569efbf78 100644 --- a/src/refs.c +++ b/src/refs.c @@ -9,22 +9,24 @@ #include "hash.h" #include "repository.h" #include "fileops.h" +#include "pack.h" #include <git2/tag.h> #include <git2/object.h> #define MAX_NESTING_LEVEL 5 -typedef struct { - git_reference ref; - git_oid oid; - git_oid peel_target; -} reference_oid; +enum { + GIT_PACKREF_HAS_PEEL = 1, + GIT_PACKREF_WAS_LOOSE = 2 +}; -typedef struct { - git_reference ref; - char *target; -} reference_symbolic; +struct packref { + git_oid oid; + git_oid peel; + char flags; + char name[GIT_FLEX_ARRAY]; +}; static const int default_table_size = 32; @@ -39,97 +41,83 @@ static uint32_t reftable_hash(const void *key, int hash_id) return git__hash(key, strlen((const char *)key), hash_seeds[hash_id]); } -static void reference_free(git_reference *reference); -static int reference_create(git_reference **ref_out, git_repository *repo, const char *name, git_rtype type); -static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated); +static int reference_read( + git_fbuffer *file_content, + time_t *mtime, + const char *repo_path, + const char *ref_name, + int *updated); /* loose refs */ static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content); -static int loose_parse_oid(git_reference *ref, git_fbuffer *file_content); -static int loose_lookup(git_reference **ref_out, git_repository *repo, const char *name, int skip_symbolic); +static int loose_parse_oid(git_oid *ref, git_fbuffer *file_content); +static int loose_lookup(git_reference *ref); +static int loose_lookup_to_packfile(struct packref **ref_out, + git_repository *repo, const char *name); static int loose_write(git_reference *ref); -static int loose_update(git_reference *ref); /* packed refs */ -static int packed_parse_peel(reference_oid *tag_ref, const char **buffer_out, const char *buffer_end); -static int packed_parse_oid(reference_oid **ref_out, git_repository *repo, const char **buffer_out, const char *buffer_end); +static int packed_parse_peel(struct packref *tag_ref, + const char **buffer_out, const char *buffer_end); +static int packed_parse_oid(struct packref **ref_out, + const char **buffer_out, const char *buffer_end); static int packed_load(git_repository *repo); static int packed_loadloose(git_repository *repository); -static int packed_write_ref(reference_oid *ref, git_filebuf *file); -static int packed_find_peel(reference_oid *ref); +static int packed_write_ref(struct packref *ref, git_filebuf *file); +static int packed_find_peel(git_repository *repo, struct packref *ref); static int packed_remove_loose(git_repository *repo, git_vector *packing_list); static int packed_sort(const void *a, const void *b); +static int packed_lookup(git_reference *ref); static int packed_write(git_repository *repo); /* internal helpers */ -static int reference_available(git_repository *repo, const char *ref, const char *old_ref); +static int reference_available(git_repository *repo, + const char *ref, const char *old_ref); +static int reference_delete(git_reference *ref); +static int reference_lookup(git_reference *ref); /* name normalization */ -static int check_valid_ref_char(char ch); -static int normalize_name(char *buffer_out, size_t out_size, const char *name, int is_oid_ref); +static int normalize_name(char *buffer_out, size_t out_size, + const char *name, int is_oid_ref); -/***************************************** - * Internal methods - Constructor/destructor - *****************************************/ -static void reference_free(git_reference *reference) + +void git_reference_free(git_reference *reference) { if (reference == NULL) return; - if (reference->name) - free(reference->name); + git__free(reference->name); - if (reference->type == GIT_REF_SYMBOLIC) - free(((reference_symbolic *)reference)->target); + if (reference->flags & GIT_REF_SYMBOLIC) + git__free(reference->target.symbolic); - free(reference); + git__free(reference); } static int reference_create( git_reference **ref_out, git_repository *repo, - const char *name, - git_rtype type) + const char *name) { - char normalized[GIT_REFNAME_MAX]; - int error = GIT_SUCCESS, size; git_reference *reference = NULL; assert(ref_out && repo && name); - if (type == GIT_REF_SYMBOLIC) - size = sizeof(reference_symbolic); - else if (type == GIT_REF_OID) - size = sizeof(reference_oid); - else - return git__throw(GIT_EINVALIDARGS, - "Invalid reference type. Use either GIT_REF_OID or GIT_REF_SYMBOLIC as type specifier"); - - reference = git__malloc(size); + reference = git__malloc(sizeof(git_reference)); if (reference == NULL) return GIT_ENOMEM; - memset(reference, 0x0, size); + memset(reference, 0x0, sizeof(git_reference)); reference->owner = repo; - reference->type = type; - - error = normalize_name(normalized, sizeof(normalized), name, (type & GIT_REF_OID)); - if (error < GIT_SUCCESS) - goto cleanup; - reference->name = git__strdup(normalized); + reference->name = git__strdup(name); if (reference->name == NULL) { - error = GIT_ENOMEM; - goto cleanup; + free(reference); + return GIT_ENOMEM; } *ref_out = reference; - - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference"); - -cleanup: - reference_free(reference); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference"); + return GIT_SUCCESS; } static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated) @@ -144,62 +132,13 @@ static int reference_read(git_fbuffer *file_content, time_t *mtime, const char * return git_futils_readbuffer_updated(file_content, path, mtime, updated); } - - - -/***************************************** - * Internal methods - Loose references - *****************************************/ -static int loose_update(git_reference *ref) -{ - int error, updated; - git_fbuffer ref_file = GIT_FBUFFER_INIT; - - if (ref->type & GIT_REF_PACKED) - return packed_load(ref->owner); - -/* error = reference_read(NULL, &ref_time, ref->owner->path_repository, ref->name); - if (error < GIT_SUCCESS) - goto cleanup; - - if (ref_time == ref->mtime) - return GIT_SUCCESS; -*/ - error = reference_read(&ref_file, &ref->mtime, ref->owner->path_repository, ref->name, &updated); - if (error < GIT_SUCCESS) - goto cleanup; - - if (!updated) - goto cleanup; - - if (ref->type == GIT_REF_SYMBOLIC) - error = loose_parse_symbolic(ref, &ref_file); - else if (ref->type == GIT_REF_OID) - error = loose_parse_oid(ref, &ref_file); - else - error = git__throw(GIT_EOBJCORRUPTED, - "Invalid reference type (%d) for loose reference", ref->type); - - -cleanup: - git_futils_freebuffer(&ref_file); - if (error != GIT_SUCCESS) { - reference_free(ref); - git_hashtable_remove(ref->owner->references.loose_cache, ref->name); - } - - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to update loose reference"); -} - static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content) { const unsigned int header_len = strlen(GIT_SYMREF); const char *refname_start; char *eol; - reference_symbolic *ref_sym; refname_start = (const char *)file_content->data; - ref_sym = (reference_symbolic *)ref; if (file_content->len < (header_len + 1)) return git__throw(GIT_EOBJCORRUPTED, @@ -212,13 +151,12 @@ static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content) refname_start += header_len; - free(ref_sym->target); - ref_sym->target = git__strdup(refname_start); - if (ref_sym->target == NULL) + ref->target.symbolic = git__strdup(refname_start); + if (ref->target.symbolic == NULL) return GIT_ENOMEM; /* remove newline at the end of file */ - eol = strchr(ref_sym->target, '\n'); + eol = strchr(ref->target.symbolic, '\n'); if (eol == NULL) return git__throw(GIT_EOBJCORRUPTED, "Failed to parse loose reference. Missing EOL"); @@ -230,21 +168,19 @@ static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content) return GIT_SUCCESS; } -static int loose_parse_oid(git_reference *ref, git_fbuffer *file_content) +static int loose_parse_oid(git_oid *oid, git_fbuffer *file_content) { int error; - reference_oid *ref_oid; char *buffer; buffer = (char *)file_content->data; - ref_oid = (reference_oid *)ref; /* File format: 40 chars (OID) + newline */ if (file_content->len < GIT_OID_HEXSZ + 1) return git__throw(GIT_EOBJCORRUPTED, "Failed to parse loose reference. Reference too short"); - if ((error = git_oid_fromstr(&ref_oid->oid, buffer)) < GIT_SUCCESS) + if ((error = git_oid_fromstr(oid, buffer)) < GIT_SUCCESS) return git__rethrow(GIT_EOBJCORRUPTED, "Failed to parse loose reference."); buffer = buffer + GIT_OID_HEXSZ; @@ -258,7 +194,6 @@ static int loose_parse_oid(git_reference *ref, git_fbuffer *file_content) return GIT_SUCCESS; } - static git_rtype loose_guess_rtype(const char *full_path) { git_fbuffer ref_file = GIT_FBUFFER_INIT; @@ -277,52 +212,75 @@ static git_rtype loose_guess_rtype(const char *full_path) return type; } -static int loose_lookup( - git_reference **ref_out, +static int loose_lookup(git_reference *ref) +{ + int error = GIT_SUCCESS, updated; + git_fbuffer ref_file = GIT_FBUFFER_INIT; + + if (reference_read(&ref_file, &ref->mtime, + ref->owner->path_repository, ref->name, &updated) < GIT_SUCCESS) + return git__throw(GIT_ENOTFOUND, "Failed to lookup loose reference"); + + if (!updated) + return GIT_SUCCESS; + + if (ref->flags & GIT_REF_SYMBOLIC) + free(ref->target.symbolic); + + ref->flags = 0; + + if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0) { + ref->flags |= GIT_REF_SYMBOLIC; + error = loose_parse_symbolic(ref, &ref_file); + } else { + ref->flags |= GIT_REF_OID; + error = loose_parse_oid(&ref->target.oid, &ref_file); + } + + git_futils_freebuffer(&ref_file); + + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to lookup loose reference"); + + return GIT_SUCCESS; +} + +static int loose_lookup_to_packfile( + struct packref **ref_out, git_repository *repo, - const char *name, - int skip_symbolic) + const char *name) { int error = GIT_SUCCESS; git_fbuffer ref_file = GIT_FBUFFER_INIT; - git_reference *ref = NULL; - time_t ref_time = 0; + struct packref *ref = NULL; + size_t name_len; *ref_out = NULL; - error = reference_read(&ref_file, &ref_time, repo->path_repository, name, NULL); + error = reference_read(&ref_file, NULL, repo->path_repository, name, NULL); if (error < GIT_SUCCESS) goto cleanup; - if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0) { - if (skip_symbolic) - return GIT_SUCCESS; - - error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC); - if (error < GIT_SUCCESS) - goto cleanup; - - error = loose_parse_symbolic(ref, &ref_file); - } else { - error = reference_create(&ref, repo, name, GIT_REF_OID); - if (error < GIT_SUCCESS) - goto cleanup; + name_len = strlen(name); + ref = git__malloc(sizeof(struct packref) + name_len + 1); - error = loose_parse_oid(ref, &ref_file); - } + memcpy(ref->name, name, name_len); + ref->name[name_len] = 0; + error = loose_parse_oid(&ref->oid, &ref_file); if (error < GIT_SUCCESS) goto cleanup; - ref->mtime = ref_time; + ref->flags = GIT_PACKREF_WAS_LOOSE; + *ref_out = ref; git_futils_freebuffer(&ref_file); return GIT_SUCCESS; cleanup: git_futils_freebuffer(&ref_file); - reference_free(ref); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to lookup loose reference"); + free(ref); + return git__rethrow(error, "Failed to lookup loose reference"); } static int loose_write(git_reference *ref) @@ -337,49 +295,36 @@ static int loose_write(git_reference *ref) if ((error = git_filebuf_open(&file, ref_path, GIT_FILEBUF_FORCE)) < GIT_SUCCESS) return git__rethrow(error, "Failed to write loose reference"); - if (ref->type & GIT_REF_OID) { - reference_oid *ref_oid = (reference_oid *)ref; + if (ref->flags & GIT_REF_OID) { char oid[GIT_OID_HEXSZ + 1]; - memset(oid, 0x0, sizeof(oid)); + git_oid_fmt(oid, &ref->target.oid); + oid[GIT_OID_HEXSZ] = '\0'; - git_oid_fmt(oid, &ref_oid->oid); error = git_filebuf_printf(&file, "%s\n", oid); if (error < GIT_SUCCESS) goto unlock; - } else if (ref->type & GIT_REF_SYMBOLIC) { /* GIT_REF_SYMBOLIC */ - reference_symbolic *ref_sym = (reference_symbolic *)ref; - - error = git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref_sym->target); + } else if (ref->flags & GIT_REF_SYMBOLIC) { /* GIT_REF_SYMBOLIC */ + error = git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic); } else { - error = git__throw(GIT_EOBJCORRUPTED, "Failed to write reference. Invalid reference type"); + error = git__throw(GIT_EOBJCORRUPTED, + "Failed to write reference. Invalid reference type"); goto unlock; } - error = git_filebuf_commit(&file); - if (p_stat(ref_path, &st) == GIT_SUCCESS) ref->mtime = st.st_mtime; - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write loose reference"); + return git_filebuf_commit(&file, GIT_REFS_FILE_MODE); unlock: git_filebuf_cleanup(&file); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write loose reference"); + return git__rethrow(error, "Failed to write loose reference"); } - - - - - -/***************************************** - * Internal methods - Packed references - *****************************************/ - static int packed_parse_peel( - reference_oid *tag_ref, + struct packref *tag_ref, const char **buffer_out, const char *buffer_end) { @@ -389,47 +334,48 @@ static int packed_parse_peel( /* Ensure it's not the first entry of the file */ if (tag_ref == NULL) - return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Reference is the first entry of the file"); + return git__throw(GIT_EPACKEDREFSCORRUPTED, + "Failed to parse packed reference. " + "Reference is the first entry of the file"); /* Ensure reference is a tag */ - if (git__prefixcmp(tag_ref->ref.name, GIT_REFS_TAGS_DIR) != 0) - return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Reference is not a tag"); + if (git__prefixcmp(tag_ref->name, GIT_REFS_TAGS_DIR) != 0) + return git__throw(GIT_EPACKEDREFSCORRUPTED, + "Failed to parse packed reference. Reference is not a tag"); if (buffer + GIT_OID_HEXSZ >= buffer_end) - return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Buffer too small"); + return git__throw(GIT_EPACKEDREFSCORRUPTED, + "Failed to parse packed reference. Buffer too small"); /* Is this a valid object id? */ - if (git_oid_fromstr(&tag_ref->peel_target, buffer) < GIT_SUCCESS) - return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Not a valid object ID"); + if (git_oid_fromstr(&tag_ref->peel, buffer) < GIT_SUCCESS) + return git__throw(GIT_EPACKEDREFSCORRUPTED, + "Failed to parse packed reference. Not a valid object ID"); buffer = buffer + GIT_OID_HEXSZ; if (*buffer == '\r') buffer++; if (*buffer != '\n') - return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Buffer not terminated correctly"); + return git__throw(GIT_EPACKEDREFSCORRUPTED, + "Failed to parse packed reference. Buffer not terminated correctly"); *buffer_out = buffer + 1; - tag_ref->ref.type |= GIT_REF_HAS_PEEL; - return GIT_SUCCESS; } static int packed_parse_oid( - reference_oid **ref_out, - git_repository *repo, + struct packref **ref_out, const char **buffer_out, const char *buffer_end) { - git_reference *_ref = NULL; - reference_oid *ref = NULL; + struct packref *ref = NULL; const char *buffer = *buffer_out; const char *refname_begin, *refname_end; int error = GIT_SUCCESS; - int refname_len; - char refname[GIT_REFNAME_MAX]; + size_t refname_len; git_oid id; refname_begin = (buffer + GIT_OID_HEXSZ + 1); @@ -449,22 +395,19 @@ static int packed_parse_oid( goto cleanup; } - refname_len = refname_end - refname_begin; - - memcpy(refname, refname_begin, refname_len); - refname[refname_len] = 0; + if (refname_end[-1] == '\r') + refname_end--; - if (refname[refname_len - 1] == '\r') - refname[refname_len - 1] = 0; + refname_len = refname_end - refname_begin; - error = reference_create(&_ref, repo, refname, GIT_REF_OID); - if (error < GIT_SUCCESS) - goto cleanup; + ref = git__malloc(sizeof(struct packref) + refname_len + 1); - ref = (reference_oid *)_ref; + memcpy(ref->name, refname_begin, refname_len); + ref->name[refname_len] = 0; git_oid_cpy(&ref->oid, &id); - ref->ref.type |= GIT_REF_PACKED; + + ref->flags = 0; *ref_out = ref; *buffer_out = refname_end + 1; @@ -472,8 +415,8 @@ static int packed_parse_oid( return GIT_SUCCESS; cleanup: - reference_free((git_reference *)ref); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse OID of packed reference"); + free(ref); + return git__rethrow(error, "Failed to parse OID of packed reference"); } static int packed_load(git_repository *repo) @@ -488,7 +431,7 @@ static int packed_load(git_repository *repo) ref_cache->packfile = git_hashtable_alloc( default_table_size, reftable_hash, - (git_hash_keyeq_ptr)(&git__strcmp_cb)); + (git_hash_keyeq_ptr)&git__strcmp_cb); if (ref_cache->packfile == NULL) { error = GIT_ENOMEM; @@ -497,7 +440,7 @@ static int packed_load(git_repository *repo) } error = reference_read(&packfile, &ref_cache->packfile_time, - repo->path_repository, GIT_PACKEDREFS_FILE, &updated); + repo->path_repository, GIT_PACKEDREFS_FILE, &updated); /* * If we couldn't find the file, we need to clear the table and @@ -535,9 +478,9 @@ static int packed_load(git_repository *repo) } while (buffer_start < buffer_end) { - reference_oid *ref = NULL; + struct packref *ref = NULL; - error = packed_parse_oid(&ref, repo, &buffer_start, buffer_end); + error = packed_parse_oid(&ref, &buffer_start, buffer_end); if (error < GIT_SUCCESS) goto cleanup; @@ -547,9 +490,9 @@ static int packed_load(git_repository *repo) goto cleanup; } - error = git_hashtable_insert(ref_cache->packfile, ref->ref.name, ref); + error = git_hashtable_insert(ref_cache->packfile, ref->name, ref); if (error < GIT_SUCCESS) { - reference_free((git_reference *)ref); + free(ref); goto cleanup; } } @@ -561,12 +504,10 @@ cleanup: git_hashtable_free(ref_cache->packfile); ref_cache->packfile = NULL; git_futils_freebuffer(&packfile); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to load packed references"); + return git__rethrow(error, "Failed to load packed references"); } - - struct dirent_list_data { git_repository *repo; size_t repo_path_len; @@ -582,7 +523,8 @@ static int _dirent_loose_listall(void *_data, char *full_path) char *file_path = full_path + data->repo_path_len; if (git_futils_isdir(full_path) == GIT_SUCCESS) - return git_futils_direach(full_path, GIT_PATH_MAX, _dirent_loose_listall, _data); + return git_futils_direach(full_path, GIT_PATH_MAX, + _dirent_loose_listall, _data); /* do not add twice a reference that exists already in the packfile */ if ((data->list_flags & GIT_REF_PACKED) != 0 && @@ -600,29 +542,35 @@ static int _dirent_loose_listall(void *_data, char *full_path) static int _dirent_loose_load(void *data, char *full_path) { git_repository *repository = (git_repository *)data; - git_reference *reference; void *old_ref = NULL; + struct packref *ref; char *file_path; int error; if (git_futils_isdir(full_path) == GIT_SUCCESS) - return git_futils_direach(full_path, GIT_PATH_MAX, _dirent_loose_load, repository); + return git_futils_direach( + full_path, GIT_PATH_MAX, + _dirent_loose_load, repository); file_path = full_path + strlen(repository->path_repository); - error = loose_lookup(&reference, repository, file_path, 1); - if (error == GIT_SUCCESS && reference != NULL) { - reference->type |= GIT_REF_PACKED; + error = loose_lookup_to_packfile(&ref, repository, file_path); - if (git_hashtable_insert2(repository->references.packfile, reference->name, reference, &old_ref) < GIT_SUCCESS) { - reference_free(reference); + if (error == GIT_SUCCESS) { + + if (git_hashtable_insert2( + repository->references.packfile, + ref->name, ref, &old_ref) < GIT_SUCCESS) { + free(ref); return GIT_ENOMEM; } if (old_ref != NULL) - reference_free((git_reference *)old_ref); + free(old_ref); } - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to load loose dirent"); + return error == GIT_SUCCESS ? + GIT_SUCCESS : + git__rethrow(error, "Failed to load loose references into packfile"); } /* @@ -640,30 +588,20 @@ static int packed_loadloose(git_repository *repository) git_path_join(refs_path, repository->path_repository, GIT_REFS_DIR); - /* Remove any loose references from the cache */ - { - const void *GIT_UNUSED(_unused); - git_reference *reference; - - GIT_HASHTABLE_FOREACH(repository->references.loose_cache, _unused, reference, - reference_free(reference); - ); - } - - git_hashtable_clear(repository->references.loose_cache); - /* * Load all the loose files from disk into the Packfile table. * This will overwrite any old packed entries with their * updated loose versions */ - return git_futils_direach(refs_path, GIT_PATH_MAX, _dirent_loose_load, repository); + return git_futils_direach( + refs_path, GIT_PATH_MAX, + _dirent_loose_load, repository); } /* * Write a single reference into a packfile */ -static int packed_write_ref(reference_oid *ref, git_filebuf *file) +static int packed_write_ref(struct packref *ref, git_filebuf *file) { int error; char oid[GIT_OID_HEXSZ + 1]; @@ -681,17 +619,19 @@ static int packed_write_ref(reference_oid *ref, git_filebuf *file) * This obviously only applies to tags. * The required peels have already been loaded into `ref->peel_target`. */ - if (ref->ref.type & GIT_REF_HAS_PEEL) { + if (ref->flags & GIT_PACKREF_HAS_PEEL) { char peel[GIT_OID_HEXSZ + 1]; - git_oid_fmt(peel, &ref->peel_target); + git_oid_fmt(peel, &ref->peel); peel[GIT_OID_HEXSZ] = 0; - error = git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->ref.name, peel); + error = git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel); } else { - error = git_filebuf_printf(file, "%s %s\n", oid, ref->ref.name); + error = git_filebuf_printf(file, "%s %s\n", oid, ref->name); } - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write packed reference"); + return error == GIT_SUCCESS ? + GIT_SUCCESS : + git__rethrow(error, "Failed to write packed reference"); } /* @@ -702,25 +642,25 @@ static int packed_write_ref(reference_oid *ref, git_filebuf *file) * cache on the packfile the OID of the object to * which that 'big tag' is pointing to. */ -static int packed_find_peel(reference_oid *ref) +static int packed_find_peel(git_repository *repo, struct packref *ref) { git_object *object; int error; - if (ref->ref.type & GIT_REF_HAS_PEEL) + if (ref->flags & GIT_PACKREF_HAS_PEEL) return GIT_SUCCESS; /* * Only applies to tags, i.e. references * in the /refs/tags folder */ - if (git__prefixcmp(ref->ref.name, GIT_REFS_TAGS_DIR) != 0) + if (git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) != 0) return GIT_SUCCESS; /* * Find the tagged object in the repository */ - error = git_object_lookup(&object, ref->ref.owner, &ref->oid, GIT_OBJ_ANY); + error = git_object_lookup(&object, repo, &ref->oid, GIT_OBJ_ANY); if (error < GIT_SUCCESS) return git__throw(GIT_EOBJCORRUPTED, "Failed to find packed reference"); @@ -735,8 +675,8 @@ static int packed_find_peel(reference_oid *ref) /* * Find the object pointed at by this tag */ - git_oid_cpy(&ref->peel_target, git_tag_target_oid(tag)); - ref->ref.type |= GIT_REF_HAS_PEEL; + git_oid_cpy(&ref->peel, git_tag_target_oid(tag)); + ref->flags |= GIT_PACKREF_HAS_PEEL; /* * The reference has now cached the resolved OID, and is @@ -746,7 +686,6 @@ static int packed_find_peel(reference_oid *ref) } git_object_close(object); - return GIT_SUCCESS; } @@ -766,16 +705,11 @@ static int packed_remove_loose(git_repository *repo, git_vector *packing_list) unsigned int i; char full_path[GIT_PATH_MAX]; int error = GIT_SUCCESS; - git_reference *reference; for (i = 0; i < packing_list->length; ++i) { - git_reference *ref = git_vector_get(packing_list, i); + struct packref *ref = git_vector_get(packing_list, i); - /* Ensure the packed reference doesn't exist - * in a (more up-to-date?) state as a loose reference - */ - reference = git_hashtable_lookup(ref->owner->references.loose_cache, ref->name); - if (reference != NULL) + if ((ref->flags & GIT_PACKREF_WAS_LOOSE) == 0) continue; git_path_join(full_path, repo->path_repository, ref->name); @@ -789,19 +723,18 @@ static int packed_remove_loose(git_repository *repo, git_vector *packing_list) * but we should keep going and remove as many as possible. * After we've removed as many files as possible, we return * the error code anyway. - * - * TODO: mark this with a very special error code? - * GIT_EFAILTORMLOOSE */ } - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to remove loose packed reference"); + return error == GIT_SUCCESS ? + GIT_SUCCESS : + git__rethrow(error, "Failed to remove loose packed reference"); } static int packed_sort(const void *a, const void *b) { - const git_reference *ref_a = (const git_reference *)a; - const git_reference *ref_b = (const git_reference *)b; + const struct packref *ref_a = (const struct packref *)a; + const struct packref *ref_b = (const struct packref *)b; return strcmp(ref_a->name, ref_b->name); } @@ -822,16 +755,18 @@ static int packed_write(git_repository *repo) assert(repo && repo->references.packfile); total_refs = repo->references.packfile->key_count; - if ((error = git_vector_init(&packing_list, total_refs, packed_sort)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to write packed reference"); + if ((error = + git_vector_init(&packing_list, total_refs, packed_sort)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to init packed refernces list"); /* Load all the packfile into a vector */ { - git_reference *reference; + struct packref *reference; const void *GIT_UNUSED(_unused); GIT_HASHTABLE_FOREACH(repo->references.packfile, _unused, reference, - git_vector_insert(&packing_list, reference); /* cannot fail: vector already has the right size */ + /* cannot fail: vector already has the right size */ + git_vector_insert(&packing_list, reference); ); } @@ -841,27 +776,24 @@ static int packed_write(git_repository *repo) /* Now we can open the file! */ git_path_join(pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE); if ((error = git_filebuf_open(&pack_file, pack_file_path, 0)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to write packed reference"); + return git__rethrow(error, "Failed to write open packed references file"); /* Packfiles have a header... apparently * This is in fact not required, but we might as well print it * just for kicks */ - if ((error = git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to write packed reference"); + if ((error = + git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to write packed references file header"); for (i = 0; i < packing_list.length; ++i) { - reference_oid *ref = (reference_oid *)git_vector_get(&packing_list, i); - - /* only direct references go to the packfile; otherwise - * this is a disaster */ - assert(ref->ref.type & GIT_REF_OID); + struct packref *ref = (struct packref *)git_vector_get(&packing_list, i); - if ((error = packed_find_peel(ref)) < GIT_SUCCESS) { - error = git__throw(GIT_EOBJCORRUPTED, "A reference cannot be peeled"); + if ((error = packed_find_peel(repo, ref)) < GIT_SUCCESS) { + error = git__throw(GIT_EOBJCORRUPTED, + "A reference cannot be peeled"); goto cleanup; } - if ((error = packed_write_ref(ref, &pack_file)) < GIT_SUCCESS) goto cleanup; } @@ -870,7 +802,7 @@ cleanup: /* if we've written all the references properly, we can commit * the packfile to make the changes effective */ if (error == GIT_SUCCESS) { - error = git_filebuf_commit(&pack_file); + error = git_filebuf_commit(&pack_file, GIT_PACKEDREFS_FILE_MODE); /* when and only when the packfile has been properly written, * we can go ahead and remove the loose refs */ @@ -887,20 +819,22 @@ cleanup: git_vector_free(&packing_list); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write packed reference"); + return error == GIT_SUCCESS ? + GIT_SUCCESS : + git__rethrow(error, "Failed to write packed references file"); } static int _reference_available_cb(const char *ref, void *data) { const char *new, *old; - git_vector *refs; + const char **refs; assert(ref && data); - refs = (git_vector *)data; + refs = (const char **)data; - new = (const char *)git_vector_get(refs, 0); - old = (const char *)git_vector_get(refs, 1); + new = (const char *)refs[0]; + old = (const char *)refs[1]; if (!old || strcmp(old, ref)) { int reflen = strlen(ref); @@ -916,35 +850,168 @@ static int _reference_available_cb(const char *ref, void *data) return GIT_SUCCESS; } -static int reference_available(git_repository *repo, const char *ref, const char* old_ref) +static int reference_available( + git_repository *repo, + const char *ref, + const char* old_ref) +{ + const char *refs[2]; + + refs[0] = ref; + refs[1] = old_ref; + + if (git_reference_foreach(repo, GIT_REF_LISTALL, + _reference_available_cb, (void *)refs) < 0) { + return git__throw(GIT_EEXISTS, + "Reference name `%s` conflicts with existing reference", ref); + } + + return GIT_SUCCESS; +} + +static int reference_exists(int *exists, git_repository *repo, const char *ref_name) { int error; - git_vector refs; + char ref_path[GIT_PATH_MAX]; - if (git_vector_init(&refs, 2, NULL) < GIT_SUCCESS) - return GIT_ENOMEM; + error = packed_load(repo); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Cannot resolve if a reference exists"); + + git_path_join(ref_path, repo->path_repository, ref_name); - git_vector_insert(&refs, (void *)ref); - git_vector_insert(&refs, (void *)old_ref); + if (git_futils_isfile(ref_path) == GIT_SUCCESS || + git_hashtable_lookup(repo->references.packfile, ref_path) != NULL) { + *exists = 1; + } else { + *exists = 0; + } + + return GIT_SUCCESS; +} - error = git_reference_foreach(repo, GIT_REF_LISTALL, _reference_available_cb, (void *)&refs); +static int packed_lookup(git_reference *ref) +{ + int error; + struct packref *pack_ref = NULL; + + error = packed_load(ref->owner); + if (error < GIT_SUCCESS) + return git__rethrow(error, + "Failed to lookup reference from packfile"); + + if (ref->flags & GIT_REF_PACKED && + ref->mtime == ref->owner->references.packfile_time) + return GIT_SUCCESS; - git_vector_free(&refs); + if (ref->flags & GIT_REF_SYMBOLIC) + free(ref->target.symbolic); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__throw(GIT_EEXISTS, "Reference name `%s` conflicts with existing reference", ref); + /* Look up on the packfile */ + pack_ref = git_hashtable_lookup(ref->owner->references.packfile, ref->name); + if (pack_ref == NULL) + return git__throw(GIT_ENOTFOUND, + "Failed to lookup reference from packfile"); + + ref->flags = GIT_REF_OID | GIT_REF_PACKED; + ref->mtime = ref->owner->references.packfile_time; + git_oid_cpy(&ref->target.oid, &pack_ref->oid); + + return GIT_SUCCESS; } -/***************************************** - * External Library API - *****************************************/ +static int reference_lookup(git_reference *ref) +{ + int error_loose, error_packed; -/** - * Constructors + error_loose = loose_lookup(ref); + if (error_loose == GIT_SUCCESS) + return GIT_SUCCESS; + + error_packed = packed_lookup(ref); + if (error_packed == GIT_SUCCESS) + return GIT_SUCCESS; + + git_reference_free(ref); + + if (error_loose != GIT_ENOTFOUND) + return git__rethrow(error_loose, "Failed to lookup reference"); + + if (error_packed != GIT_ENOTFOUND) + return git__rethrow(error_packed, "Failed to lookup reference"); + + return git__throw(GIT_ENOTFOUND, "Reference not found"); +} + +/* + * Delete a reference. + * This is an internal method; the reference is removed + * from disk or the packfile, but the pointer is not freed */ -int git_reference_lookup(git_reference **ref_out, git_repository *repo, const char *name) +static int reference_delete(git_reference *ref) +{ + int error; + + assert(ref); + + /* If the reference is packed, this is an expensive operation. + * We need to reload the packfile, remove the reference from the + * packing list, and repack */ + if (ref->flags & GIT_REF_PACKED) { + /* load the existing packfile */ + if ((error = packed_load(ref->owner)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to delete reference"); + + if (git_hashtable_remove(ref->owner->references.packfile, + ref->name) < GIT_SUCCESS) + return git__throw(GIT_ENOTFOUND, "Reference not found"); + + error = packed_write(ref->owner); + + /* If the reference is loose, we can just remove the reference + * from the filesystem */ + } else { + char full_path[GIT_PATH_MAX]; + git_reference *ref_in_pack; + + git_path_join(full_path, ref->owner->path_repository, ref->name); + + error = p_unlink(full_path); + if (error < GIT_SUCCESS) + goto cleanup; + + /* When deleting a loose reference, we have to ensure that an older + * packed version of it doesn't exist */ + if (git_reference_lookup(&ref_in_pack, ref->owner, + ref->name) == GIT_SUCCESS) { + assert((ref_in_pack->flags & GIT_REF_PACKED) != 0); + error = git_reference_delete(ref_in_pack); + } + } + +cleanup: + return error == GIT_SUCCESS ? + GIT_SUCCESS : + git__rethrow(error, "Failed to delete reference"); +} + +int git_reference_delete(git_reference *ref) +{ + int error = reference_delete(ref); + if (error < GIT_SUCCESS) + return error; + + git_reference_free(ref); + return GIT_SUCCESS; +} + + +int git_reference_lookup(git_reference **ref_out, + git_repository *repo, const char *name) { int error; char normalized_name[GIT_REFNAME_MAX]; + git_reference *ref = NULL; assert(ref_out && repo && name); @@ -954,39 +1021,16 @@ int git_reference_lookup(git_reference **ref_out, git_repository *repo, const ch if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to lookup reference"); - /* First, check has been previously loaded and cached */ - *ref_out = git_hashtable_lookup(repo->references.loose_cache, normalized_name); - if (*ref_out != NULL) - return loose_update(*ref_out); - - /* Then check if there is a loose file for that reference.*/ - error = loose_lookup(ref_out, repo, normalized_name, 0); - - /* If the file exists, we store it on the cache */ - if (error == GIT_SUCCESS) - return git_hashtable_insert(repo->references.loose_cache, (*ref_out)->name, (*ref_out)); - - /* The loose lookup has failed, but not because the reference wasn't found; - * probably the loose reference is corrupted. this is bad. */ - if (error != GIT_ENOTFOUND) + error = reference_create(&ref, repo, normalized_name); + if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to lookup reference"); - /* - * If we cannot find a loose reference, we look into the packfile - * Load the packfile first if it hasn't been loaded - */ - /* load all the packed references */ - error = packed_load(repo); + error = reference_lookup(ref); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to lookup reference"); - /* Look up on the packfile */ - *ref_out = git_hashtable_lookup(repo->references.packfile, normalized_name); - if (*ref_out != NULL) - return GIT_SUCCESS; - - /* The reference doesn't exist anywhere */ - return git__throw(GIT_ENOTFOUND, "Failed to lookup reference. Reference doesn't exist"); + *ref_out = ref; + return GIT_SUCCESS; } /** @@ -996,15 +1040,21 @@ git_rtype git_reference_type(git_reference *ref) { assert(ref); - if (ref->type & GIT_REF_OID) + if (ref->flags & GIT_REF_OID) return GIT_REF_OID; - if (ref->type & GIT_REF_SYMBOLIC) + if (ref->flags & GIT_REF_SYMBOLIC) return GIT_REF_SYMBOLIC; return GIT_REF_INVALID; } +int git_reference_is_packed(git_reference *ref) +{ + assert(ref); + return !!(ref->flags & GIT_REF_PACKED); +} + const char *git_reference_name(git_reference *ref) { assert(ref); @@ -1021,219 +1071,154 @@ const git_oid *git_reference_oid(git_reference *ref) { assert(ref); - if ((ref->type & GIT_REF_OID) == 0) + if ((ref->flags & GIT_REF_OID) == 0) return NULL; - if (loose_update(ref) < GIT_SUCCESS) - return NULL; - - return &((reference_oid *)ref)->oid; + return &ref->target.oid; } const char *git_reference_target(git_reference *ref) { assert(ref); - if ((ref->type & GIT_REF_SYMBOLIC) == 0) - return NULL; - - if (loose_update(ref) < GIT_SUCCESS) + if ((ref->flags & GIT_REF_SYMBOLIC) == 0) return NULL; - return ((reference_symbolic *)ref)->target; + return ref->target.symbolic; } -int git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force) +int git_reference_create_symbolic( + git_reference **ref_out, + git_repository *repo, + const char *name, + const char *target, + int force) { char normalized[GIT_REFNAME_MAX]; - int error = GIT_SUCCESS, updated = 0; + int ref_exists, error = GIT_SUCCESS; git_reference *ref = NULL; - void *old_ref = NULL; - if (git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force) - return git__throw(GIT_EEXISTS, "Failed to create symbolic reference. Reference already exists"); + error = normalize_name(normalized, sizeof(normalized), name, 0); + if (error < GIT_SUCCESS) + goto cleanup; - /* - * If they old ref was of the same type, then we can just update - * it (once we've checked that the target is valid). Otherwise we - * need a new reference because we can't make a symbolic ref out - * of an oid one. - * If if didn't exist, then we need to create a new one anyway. - */ - if (ref && ref->type & GIT_REF_SYMBOLIC){ - updated = 1; - } else { - ref = NULL; - error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC); - if (error < GIT_SUCCESS) - goto cleanup; - } + if ((error = reference_exists(&ref_exists, repo, normalized) < GIT_SUCCESS)) + return git__rethrow(error, "Failed to create symbolic reference"); - /* The target can aither be the name of an object id reference or the name of another symbolic reference */ - error = normalize_name(normalized, sizeof(normalized), target, 0); + if (ref_exists && !force) + return git__throw(GIT_EEXISTS, + "Failed to create symbolic reference. Reference already exists"); + + error = reference_create(&ref, repo, normalized); if (error < GIT_SUCCESS) goto cleanup; - /* set the target; this will write the reference on disk */ - error = git_reference_set_target(ref, normalized); + ref->flags |= GIT_REF_SYMBOLIC; + + /* set the target; this will normalize the name automatically + * and write the reference on disk */ + error = git_reference_set_target(ref, target); if (error < GIT_SUCCESS) goto cleanup; - /* - * If we didn't update the ref, then we need to insert or replace - * it in the loose cache. If we replaced a ref, free it. - */ - if (!updated){ - error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, &old_ref); - if (error < GIT_SUCCESS) - goto cleanup; - - if (old_ref != NULL) - reference_free((git_reference *)old_ref); + if (ref_out == NULL) { + git_reference_free(ref); + } else { + *ref_out = ref; } - *ref_out = ref; - - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create symbolic reference"); + return GIT_SUCCESS; cleanup: - reference_free(ref); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create symbolic reference"); + git_reference_free(ref); + return git__rethrow(error, "Failed to create symbolic reference"); } -int git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force) +int git_reference_create_oid( + git_reference **ref_out, + git_repository *repo, + const char *name, + const git_oid *id, + int force) { - int error = GIT_SUCCESS, updated = 0; + int error = GIT_SUCCESS, ref_exists; git_reference *ref = NULL; - void *old_ref = NULL; + char normalized[GIT_REFNAME_MAX]; + + error = normalize_name(normalized, sizeof(normalized), name, 1); + if (error < GIT_SUCCESS) + goto cleanup; + + if ((error = reference_exists(&ref_exists, repo, normalized) < GIT_SUCCESS)) + return git__rethrow(error, "Failed to create OID reference"); - if(git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force) - return git__throw(GIT_EEXISTS, "Failed to create reference OID. Reference already exists"); + if (ref_exists && !force) + return git__throw(GIT_EEXISTS, + "Failed to create OID reference. Reference already exists"); if ((error = reference_available(repo, name, NULL)) < GIT_SUCCESS) return git__rethrow(error, "Failed to create reference"); - /* - * If they old ref was of the same type, then we can just update - * it (once we've checked that the target is valid). Otherwise we - * need a new reference because we can't make a symbolic ref out - * of an oid one. - * If if didn't exist, then we need to create a new one anyway. - */ - if (ref && ref-> type & GIT_REF_OID){ - updated = 1; - } else { - ref = NULL; - error = reference_create(&ref, repo, name, GIT_REF_OID); - if (error < GIT_SUCCESS) - goto cleanup; - } + error = reference_create(&ref, repo, name); + if (error < GIT_SUCCESS) + goto cleanup; + + ref->flags |= GIT_REF_OID; /* set the oid; this will write the reference on disk */ error = git_reference_set_oid(ref, id); if (error < GIT_SUCCESS) goto cleanup; - if(!updated){ - error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, &old_ref); - if (error < GIT_SUCCESS) - goto cleanup; - - if (old_ref != NULL) - reference_free((git_reference *)old_ref); + if (ref_out == NULL) { + git_reference_free(ref); + } else { + *ref_out = ref; } - *ref_out = ref; - - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID"); + return GIT_SUCCESS; cleanup: - reference_free(ref); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID"); + git_reference_free(ref); + return git__rethrow(error, "Failed to create reference OID"); } -/** - * Setters - */ - /* * Change the OID target of a reference. * - * For loose references, just change the oid in memory - * and overwrite the file in disk. - * - * For packed files, this is not pretty: - * For performance reasons, we write the new reference - * loose on disk (it replaces the old on the packfile), - * but we cannot invalidate the pointer to the reference, - * and most importantly, the `packfile` object must stay - * consistent with the representation of the packfile - * on disk. This is what we need to: + * For both loose and packed references, just change + * the oid in memory and (over)write the file in disk. * - * 1. Copy the reference - * 2. Change the oid on the original - * 3. Write the original to disk - * 4. Write the original to the loose cache - * 5. Replace the original with the copy (old reference) in the packfile cache + * We do not repack packed references because of performance + * reasons. */ int git_reference_set_oid(git_reference *ref, const git_oid *id) { - reference_oid *ref_oid; - reference_oid *ref_old = NULL; int error = GIT_SUCCESS; - if ((ref->type & GIT_REF_OID) == 0) - return git__throw(GIT_EINVALIDREFSTATE, "Failed to set OID target of reference. Not an OID reference"); - - ref_oid = (reference_oid *)ref; + if ((ref->flags & GIT_REF_OID) == 0) + return git__throw(GIT_EINVALIDREFSTATE, + "Failed to set OID target of reference. Not an OID reference"); assert(ref->owner); /* 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)) - return git__throw(GIT_ENOTFOUND, "Failed to set OID target of reference. OID doesn't exist in ODB"); - - /* duplicate the reference; - * this copy will stay on the packfile cache */ - if (ref->type & GIT_REF_PACKED) { - ref_old = git__malloc(sizeof(reference_oid)); - if (ref_old == NULL) - return GIT_ENOMEM; + return git__throw(GIT_ENOTFOUND, + "Failed to set OID target of reference. OID doesn't exist in ODB"); - ref_old->ref.name = git__strdup(ref->name); - if (ref_old->ref.name == NULL) { - free(ref_old); - return GIT_ENOMEM; - } - } - - git_oid_cpy(&ref_oid->oid, id); - ref->type &= ~GIT_REF_HAS_PEEL; + /* Update the OID value on `ref` */ + git_oid_cpy(&ref->target.oid, id); error = loose_write(ref); if (error < GIT_SUCCESS) goto cleanup; - if (ref->type & GIT_REF_PACKED) { - /* insert the original on the loose cache */ - error = git_hashtable_insert(ref->owner->references.loose_cache, ref->name, ref); - if (error < GIT_SUCCESS) - goto cleanup; - - ref->type &= ~GIT_REF_PACKED; - - /* replace the original in the packfile with the copy */ - error = git_hashtable_insert(ref->owner->references.packfile, ref_old->ref.name, ref_old); - if (error < GIT_SUCCESS) - goto cleanup; - } - return GIT_SUCCESS; cleanup: - reference_free((git_reference *)ref_old); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to set OID target of reference"); + return git__rethrow(error, "Failed to set OID target of reference"); } /* @@ -1245,99 +1230,85 @@ cleanup: */ int git_reference_set_target(git_reference *ref, const char *target) { - reference_symbolic *ref_sym; + int error; + char normalized[GIT_REFNAME_MAX]; - if ((ref->type & GIT_REF_SYMBOLIC) == 0) - return git__throw(GIT_EINVALIDREFSTATE, "Failed to set reference target. Not a symbolic reference"); + if ((ref->flags & GIT_REF_SYMBOLIC) == 0) + return git__throw(GIT_EINVALIDREFSTATE, + "Failed to set reference target. Not a symbolic reference"); - ref_sym = (reference_symbolic *)ref; + error = normalize_name(normalized, sizeof(normalized), target, 0); + if (error < GIT_SUCCESS) + return git__rethrow(error, + "Failed to set reference target. Invalid target name"); - free(ref_sym->target); - ref_sym->target = git__strdup(target); - if (ref_sym->target == NULL) + git__free(ref->target.symbolic); + ref->target.symbolic = git__strdup(normalized); + if (ref->target.symbolic == NULL) return GIT_ENOMEM; return loose_write(ref); } -/** - * Other - */ - int git_reference_rename(git_reference *ref, const char *new_name, int force) { int error; - char *old_name = NULL; char aux_path[GIT_PATH_MAX]; char normalized[GIT_REFNAME_MAX]; - const char *target_ref = NULL; const char *head_target = NULL; - const git_oid *target_oid = NULL; - git_reference *new_ref = NULL, *head = NULL; + git_reference *existing_ref = NULL, *head = NULL; - assert(ref); + error = normalize_name(normalized, sizeof(normalized), + new_name, ref->flags & GIT_REF_OID); - error = normalize_name(normalized, sizeof(normalized), new_name, ref->type & GIT_REF_OID); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to rename reference. Invalid name"); new_name = normalized; - error = git_reference_lookup(&new_ref, ref->owner, new_name); - if (error == GIT_SUCCESS) { - if (!force) - return git__throw(GIT_EEXISTS, "Failed to rename reference. Reference already exists"); - - error = git_reference_delete(new_ref); - } + /* If we are forcing the rename, try to lookup a reference with the + * new one. If the lookup succeeds, we need to delete that ref + * before the renaming can proceed */ + if (force) { + error = git_reference_lookup(&existing_ref, ref->owner, new_name); - if (error < GIT_SUCCESS) { - git_path_join(aux_path, ref->owner->path_repository, new_name); - /* If we couldn't read the reference because it doesn't - * exist it's ok - otherwise return */ - if (git_futils_isfile(aux_path) == GIT_SUCCESS) + if (error == GIT_SUCCESS) { + error = git_reference_delete(existing_ref); + if (error < GIT_SUCCESS) + return git__rethrow(error, + "Failed to rename reference. " + "The existing reference cannot be deleted"); + } else if (error != GIT_ENOTFOUND) goto cleanup; - } - - if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to rename reference. Reference already exists"); - /* - * First, we backup the reference targets. Just keeping the old - * reference won't work, since we may have to remove it to create - * the new reference, e.g. when renaming foo/bar -> foo. - */ - - old_name = git__strdup(ref->name); - - if (ref->type & GIT_REF_SYMBOLIC) { - if ((target_ref = git_reference_target(ref)) == NULL) - goto cleanup; + /* If we're not forcing the rename, check if the reference exists. + * If it does, renaming cannot continue */ } else { - if ((target_oid = git_reference_oid(ref)) == NULL) + int exists; + + error = reference_exists(&exists, ref->owner, normalized); + if (error < GIT_SUCCESS) goto cleanup; + + if (exists) + return git__throw(GIT_EEXISTS, + "Failed to rename reference. Reference already exists"); } + if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS) + return git__rethrow(error, + "Failed to rename reference. Reference already exists"); + /* * Now delete the old ref and remove an possibly existing directory - * named `new_name`. + * named `new_name`. Note that using the internal `reference_delete` + * method deletes the ref from disk but doesn't free the pointer, so + * we can still access the ref's attributes for creating the new one */ - - if (ref->type & GIT_REF_PACKED) { - ref->type &= ~GIT_REF_PACKED; - - git_hashtable_remove(ref->owner->references.packfile, old_name); - if ((error = packed_write(ref->owner)) < GIT_SUCCESS) - goto rollback; - } else { - git_path_join(aux_path, ref->owner->path_repository, old_name); - if ((error = p_unlink(aux_path)) < GIT_SUCCESS) - goto cleanup; - - git_hashtable_remove(ref->owner->references.loose_cache, old_name); - } + if ((error = reference_delete(ref)) < GIT_SUCCESS) + goto cleanup; git_path_join(aux_path, ref->owner->path_repository, new_name); if (git_futils_exists(aux_path) == GIT_SUCCESS) { @@ -1363,8 +1334,7 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force) * TODO * */ - - git_path_join_n(aux_path, 3, ref->owner->path_repository, "logs", old_name); + git_path_join_n(aux_path, 3, ref->owner->path_repository, "logs", ref->name); if (git_futils_isfile(aux_path) == GIT_SUCCESS) { if ((error = p_unlink(aux_path)) < GIT_SUCCESS) goto rollback; @@ -1373,138 +1343,107 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force) /* * Finally we can create the new reference. */ - if (ref->type & GIT_REF_SYMBOLIC) { - if ((error = git_reference_create_symbolic(&new_ref, ref->owner, new_name, target_ref, 0)) < GIT_SUCCESS) - goto rollback; + if (ref->flags & GIT_REF_SYMBOLIC) { + error = git_reference_create_symbolic( + NULL, ref->owner, new_name, ref->target.symbolic, 0); } else { - if ((error = git_reference_create_oid(&new_ref, ref->owner, new_name, target_oid, 0)) < GIT_SUCCESS) - goto rollback; + error = git_reference_create_oid( + NULL, ref->owner, new_name, &ref->target.oid, 0); } - free(ref->name); - ref->name = new_ref->name; - - /* - * No need in new_ref anymore. We created it to fix the change on disk. - * TODO: Refactoring required. - */ - new_ref->name = NULL; - reference_free(new_ref); - - if ((error = git_hashtable_insert(ref->owner->references.loose_cache, ref->name, ref)) < GIT_SUCCESS) - goto rollback; + if (error < GIT_SUCCESS) + goto cleanup; /* * Check if we have to update HEAD. */ - - if ((error = git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE)) < GIT_SUCCESS) + error = git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE); + if (error < GIT_SUCCESS) goto cleanup; head_target = git_reference_target(head); - if (head_target && !strcmp(head_target, old_name)) - if ((error = git_reference_create_symbolic(&head, ref->owner, "HEAD", ref->name, 1)) < GIT_SUCCESS) - goto rollback; + if (head_target && !strcmp(head_target, ref->name)) { + error = git_reference_create_symbolic( + &head, ref->owner, "HEAD", new_name, 1); + + if (error < GIT_SUCCESS) + goto cleanup; + } + + /* + * Change the name of the reference given by the user. + */ + git__free(ref->name); + ref->name = git__strdup(new_name); + + /* The reference is no longer packed */ + ref->flags &= ~GIT_REF_PACKED; cleanup: - free(old_name); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference"); + /* We no longer need the newly created reference nor the head */ + git_reference_free(head); + return error == GIT_SUCCESS ? + GIT_SUCCESS : + git__rethrow(error, "Failed to rename reference"); rollback: /* * Try to create the old reference again. */ - if (ref->type & GIT_REF_SYMBOLIC) - error = git_reference_create_symbolic(&new_ref, ref->owner, old_name, target_ref, 0); + if (ref->flags & GIT_REF_SYMBOLIC) + error = git_reference_create_symbolic( + NULL, ref->owner, ref->name, ref->target.symbolic, 0); else - error = git_reference_create_oid(&new_ref, ref->owner, old_name, target_oid, 0); - - ref->name = old_name; + error = git_reference_create_oid( + NULL, ref->owner, ref->name, &ref->target.oid, 0); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference. Failed to rollback"); + return error == GIT_SUCCESS ? + git__rethrow(GIT_ERROR, "Failed to rename reference. Did rollback") : + git__rethrow(error, "Failed to rename reference. Failed to rollback"); } -/* - * Delete a reference. - * - * If the reference is packed, this is an expensive - * operation. We need to remove the reference from - * the memory cache and then rewrite the whole pack - * - * If the reference is loose, we remove it on - * the filesystem and update the in-memory cache - * accordingly. We also make sure that an older version - * of it doesn't exist as a packed reference. If this - * is the case, this packed reference is removed as well. - * - * This obviously invalidates the `ref` pointer. - */ -int git_reference_delete(git_reference *ref) +int git_reference_resolve(git_reference **ref_out, git_reference *ref) { - int error; - git_reference *reference; + int error, i = 0; + git_repository *repo; assert(ref); - if (ref->type & GIT_REF_PACKED) { - /* load the existing packfile */ - if ((error = packed_load(ref->owner)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to delete reference"); - - if (git_hashtable_remove(ref->owner->references.packfile, ref->name) < GIT_SUCCESS) - return git__throw(GIT_ENOTFOUND, "Reference not found"); - - error = packed_write(ref->owner); - } else { - char full_path[GIT_PATH_MAX]; - git_path_join(full_path, ref->owner->path_repository, ref->name); - git_hashtable_remove(ref->owner->references.loose_cache, ref->name); - error = p_unlink(full_path); - if (error < GIT_SUCCESS) - goto cleanup; - - /* When deleting a loose reference, we have to ensure that an older - * packed version of it doesn't exist - */ - if (!git_reference_lookup(&reference, ref->owner, ref->name)) { - assert((reference->type & GIT_REF_PACKED) != 0); - error = git_reference_delete(reference); - } - } - -cleanup: - reference_free(ref); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to delete reference"); -} - -int git_reference_resolve(git_reference **resolved_ref, git_reference *ref) -{ - git_repository *repo; - int error, i; + *ref_out = NULL; + repo = ref->owner; - assert(resolved_ref && ref); - *resolved_ref = NULL; + /* If the reference is already resolved, we need to return a + * copy. Instead of duplicating `ref`, we look it up again to + * ensure the copy is out to date */ + if (ref->flags & GIT_REF_OID) + return git_reference_lookup(ref_out, ref->owner, ref->name); - if ((error = loose_update(ref)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to resolve reference"); + /* Otherwise, keep iterating until the reference is resolved */ + for (i = 0; i < MAX_NESTING_LEVEL; ++i) { + git_reference *new_ref; - repo = ref->owner; + error = git_reference_lookup(&new_ref, repo, ref->target.symbolic); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to resolve reference"); - for (i = 0; i < MAX_NESTING_LEVEL; ++i) { - reference_symbolic *ref_sym; + /* Free intermediate references, except for the original one + * we've received */ + if (i > 0) + git_reference_free(ref); - *resolved_ref = ref; + ref = new_ref; - if (ref->type & GIT_REF_OID) + /* When the reference we've just looked up is an OID, we've + * successfully resolved the symbolic ref */ + if (ref->flags & GIT_REF_OID) { + *ref_out = ref; return GIT_SUCCESS; - - ref_sym = (reference_symbolic *)ref; - if ((error = git_reference_lookup(&ref, repo, ref_sym->target)) < GIT_SUCCESS) - return error; + } } - return git__throw(GIT_ENOMEM, "Failed to resolve reference. Reference is too nested"); + return git__throw(GIT_ENOMEM, + "Failed to resolve reference. Reference is too nested"); } int git_reference_packall(git_repository *repo) @@ -1523,7 +1462,11 @@ int git_reference_packall(git_repository *repo) return packed_write(repo); } -int git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload) +int git_reference_foreach( + git_repository *repo, + unsigned int list_flags, + int (*callback)(const char *, void *), + void *payload) { int error; struct dirent_list_data data; @@ -1539,7 +1482,8 @@ int git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*c GIT_HASHTABLE_FOREACH(repo->references.packfile, ref_name, _unused, if ((error = callback(ref_name, payload)) < GIT_SUCCESS) - return git__throw(error, "Failed to list references. User callback failed"); + return git__throw(error, + "Failed to list references. User callback failed"); ); } @@ -1552,7 +1496,6 @@ int git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*c data.callback = callback; data.callback_payload = payload; - git_path_join(refs_path, repo->path_repository, GIT_REFS_DIR); return git_futils_direach(refs_path, GIT_PATH_MAX, _dirent_loose_listall, &data); } @@ -1562,7 +1505,10 @@ static int cb__reflist_add(const char *ref, void *data) return git_vector_insert((git_vector *)data, git__strdup(ref)); } -int git_reference_listall(git_strarray *array, git_repository *repo, unsigned int list_flags) +int git_reference_listall( + git_strarray *array, + git_repository *repo, + unsigned int list_flags) { int error; git_vector ref_list; @@ -1575,7 +1521,8 @@ int git_reference_listall(git_strarray *array, git_repository *repo, unsigned in if (git_vector_init(&ref_list, 8, NULL) < GIT_SUCCESS) return GIT_ENOMEM; - error = git_reference_foreach(repo, list_flags, &cb__reflist_add, (void *)&ref_list); + error = git_reference_foreach( + repo, list_flags, &cb__reflist_add, (void *)&ref_list); if (error < GIT_SUCCESS) { git_vector_free(&ref_list); @@ -1587,59 +1534,39 @@ int git_reference_listall(git_strarray *array, git_repository *repo, unsigned in return GIT_SUCCESS; } - - - -/***************************************** - * Init/free (repository API) - *****************************************/ -int git_repository__refcache_init(git_refcache *refs) +int git_reference_reload(git_reference *ref) { - assert(refs); - - refs->loose_cache = git_hashtable_alloc( - default_table_size, - reftable_hash, - (git_hash_keyeq_ptr)(&git__strcmp_cb)); + int error = reference_lookup(ref); - /* packfile loaded lazily */ - refs->packfile = NULL; - refs->packfile_time = 0; + if (error < GIT_SUCCESS) { + git_reference_free(ref); + return git__rethrow(error, "Failed to reload reference"); + } - return (refs->loose_cache) ? GIT_SUCCESS : GIT_ENOMEM; + return GIT_SUCCESS; } + void git_repository__refcache_free(git_refcache *refs) { - git_reference *reference; - const void *GIT_UNUSED(_unused); - assert(refs); - GIT_HASHTABLE_FOREACH(refs->loose_cache, _unused, reference, - reference_free(reference); - ); - - git_hashtable_free(refs->loose_cache); - if (refs->packfile) { + const void *GIT_UNUSED(_unused); + struct packref *reference; + GIT_HASHTABLE_FOREACH(refs->packfile, _unused, reference, - reference_free(reference); + free(reference); ); git_hashtable_free(refs->packfile); } } - - -/***************************************** - * Name normalization - *****************************************/ -static int check_valid_ref_char(char ch) +static int is_valid_ref_char(char ch) { if ((unsigned) ch <= ' ') - return GIT_ERROR; + return 0; switch (ch) { case '~': @@ -1649,13 +1576,17 @@ static int check_valid_ref_char(char ch) case '?': case '[': case '*': - return GIT_ERROR; + return 0; default: - return GIT_SUCCESS; + return 1; } } -static int normalize_name(char *buffer_out, size_t out_size, const char *name, int is_oid_ref) +static int normalize_name( + char *buffer_out, + size_t out_size, + const char *name, + int is_oid_ref) { const char *name_end, *buffer_out_start; const char *current; @@ -1672,26 +1603,33 @@ static int normalize_name(char *buffer_out, size_t out_size, const char *name, i /* A refname can not be empty */ if (name_end == name) - return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name is empty"); + return git__throw(GIT_EINVALIDREFNAME, + "Failed to normalize name. Reference name is empty"); /* A refname can not end with a dot or a slash */ if (*(name_end - 1) == '.' || *(name_end - 1) == '/') - return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name ends with dot or slash"); + return git__throw(GIT_EINVALIDREFNAME, + "Failed to normalize name. Reference name ends with dot or slash"); while (current < name_end && out_size) { - if (check_valid_ref_char(*current)) - return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name contains invalid characters"); + if (!is_valid_ref_char(*current)) + return git__throw(GIT_EINVALIDREFNAME, + "Failed to normalize name. " + "Reference name contains invalid characters"); if (buffer_out > buffer_out_start) { char prev = *(buffer_out - 1); /* A refname can not start with a dot nor contain a double dot */ if (*current == '.' && ((prev == '.') || (prev == '/'))) - return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name starts with a dot or contains a double dot"); + return git__throw(GIT_EINVALIDREFNAME, + "Failed to normalize name. " + "Reference name starts with a dot or contains a double dot"); /* '@{' is forbidden within a refname */ if (*current == '{' && prev == '@') - return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name contains '@{'"); + return git__throw(GIT_EINVALIDREFNAME, + "Failed to normalize name. Reference name contains '@{'"); /* Prevent multiple slashes from being added to the output */ if (*current == '/' && prev == '/') { @@ -1713,13 +1651,18 @@ static int normalize_name(char *buffer_out, size_t out_size, const char *name, i /* Object id refname have to contain at least one slash, except * for HEAD in a detached state or MERGE_HEAD if we're in the * middle of a merge */ - if (is_oid_ref && !contains_a_slash && (strcmp(name, GIT_HEAD_FILE) && strcmp(name, GIT_MERGE_HEAD_FILE) - && strcmp(name, GIT_FETCH_HEAD_FILE))) - return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name contains no slashes"); + if (is_oid_ref && + !contains_a_slash && + strcmp(name, GIT_HEAD_FILE) != 0 && + strcmp(name, GIT_MERGE_HEAD_FILE) != 0 && + strcmp(name, GIT_FETCH_HEAD_FILE) != 0) + return git__throw(GIT_EINVALIDREFNAME, + "Failed to normalize name. Reference name contains no slashes"); /* A refname can not end with ".lock" */ if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION)) - return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name ends with '.lock'"); + return git__throw(GIT_EINVALIDREFNAME, + "Failed to normalize name. Reference name ends with '.lock'"); *buffer_out = '\0'; @@ -1729,17 +1672,25 @@ static int normalize_name(char *buffer_out, size_t out_size, const char *name, i */ if (is_oid_ref && !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) || strcmp(buffer_out_start, GIT_HEAD_FILE))) - return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name does not start with 'refs/'"); + return git__throw(GIT_EINVALIDREFNAME, + "Failed to normalize name. " + "Reference name does not start with 'refs/'"); return GIT_SUCCESS; } -int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name) +int git_reference__normalize_name( + char *buffer_out, + size_t out_size, + const char *name) { return normalize_name(buffer_out, out_size, name, 0); } -int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name) +int git_reference__normalize_name_oid( + char *buffer_out, + size_t out_size, + const char *name) { return normalize_name(buffer_out, out_size, name, 1); } diff --git a/src/refs.h b/src/refs.h index c4b0b0e39..c90f5bcc4 100644 --- a/src/refs.h +++ b/src/refs.h @@ -16,12 +16,15 @@ #define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/" #define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/" #define GIT_REFS_REMOTES_DIR GIT_REFS_DIR "remotes/" +#define GIT_REFS_DIR_MODE 0777 +#define GIT_REFS_FILE_MODE 0666 #define GIT_RENAMED_REF_FILE GIT_REFS_DIR "RENAMED-REF" #define GIT_SYMREF "ref: " #define GIT_PACKEDREFS_FILE "packed-refs" #define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled " +#define GIT_PACKEDREFS_FILE_MODE 0666 #define GIT_HEAD_FILE "HEAD" #define GIT_FETCH_HEAD_FILE "FETCH_HEAD" @@ -31,21 +34,23 @@ #define GIT_REFNAME_MAX 1024 struct git_reference { + unsigned int flags; git_repository *owner; char *name; - unsigned int type; time_t mtime; + + union { + git_oid oid; + char *symbolic; + } target; }; typedef struct { git_hashtable *packfile; - git_hashtable *loose_cache; time_t packfile_time; } git_refcache; - void git_repository__refcache_free(git_refcache *refs); -int git_repository__refcache_init(git_refcache *refs); int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name); int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name); diff --git a/src/refspec.c b/src/refspec.c index ed4b5e6b8..e60e8f5b5 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -32,7 +32,7 @@ int git_refspec_parse(git_refspec *refspec, const char *str) refspec->dst = git__strdup(delim + 1); if (refspec->dst == NULL) { - free(refspec->src); + git__free(refspec->src); refspec->src = NULL; return GIT_ENOMEM; } diff --git a/src/remote.c b/src/remote.c index 10303b467..51e77e584 100644 --- a/src/remote.c +++ b/src/remote.c @@ -37,7 +37,7 @@ static int refspec_parse(git_refspec *refspec, const char *str) refspec->dst = git__strdup(delim + 1); if (refspec->dst == NULL) { - free(refspec->src); + git__free(refspec->src); refspec->src = NULL; return GIT_ENOMEM; } @@ -69,7 +69,7 @@ int git_remote_new(git_remote **out, git_repository *repo, const char *url) remote->repo = repo; remote->url = git__strdup(url); if (remote->url == NULL) { - free(remote); + git__free(remote); return GIT_ENOMEM; } @@ -151,7 +151,7 @@ int git_remote_get(git_remote **out, git_config *cfg, const char *name) *out = remote; cleanup: - free(buf); + git__free(buf); if (error < GIT_SUCCESS) git_remote_free(remote); @@ -261,17 +261,17 @@ void git_remote_free(git_remote *remote) if (remote == NULL) return; - free(remote->fetch.src); - free(remote->fetch.dst); - free(remote->push.src); - free(remote->push.dst); - free(remote->url); - free(remote->name); + git__free(remote->fetch.src); + git__free(remote->fetch.dst); + git__free(remote->push.src); + git__free(remote->push.dst); + git__free(remote->url); + git__free(remote->name); if (remote->transport != NULL) { if (remote->transport->connected) remote->transport->close(remote->transport); remote->transport->free(remote->transport); } - free(remote); + git__free(remote); } diff --git a/src/repository.c b/src/repository.c index 328bc0d57..f8195e2d9 100644 --- a/src/repository.c +++ b/src/repository.c @@ -168,12 +168,7 @@ static git_repository *repository_alloc(void) error = git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free); if (error < GIT_SUCCESS) { - free(repo); - return NULL; - } - - if (git_repository__refcache_init(&repo->references) < GIT_SUCCESS) { - free(repo); + git__free(repo); return NULL; } @@ -467,13 +462,13 @@ static int read_gitfile(char *path_out, const char *file_path, const char *base_ static void git_repository__free_dirs(git_repository *repo) { - free(repo->path_workdir); + git__free(repo->path_workdir); repo->path_workdir = NULL; - free(repo->path_index); + git__free(repo->path_index); repo->path_index = NULL; - free(repo->path_repository); + git__free(repo->path_repository); repo->path_repository = NULL; - free(repo->path_odb); + git__free(repo->path_odb); repo->path_odb = NULL; } @@ -489,7 +484,7 @@ void git_repository_free(git_repository *repo) if (repo->db != NULL) git_odb_close(repo->db); - free(repo); + git__free(repo); } int git_repository_discover(char *repository_path, size_t size, const char *start_path, int across_fs, const char *ceiling_dirs) @@ -603,18 +598,23 @@ static int repo_init_reinit(const char *repository_path, int is_bare) static int repo_init_createhead(git_repository *repo) { + int error; git_reference *head_reference; - return git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE, 0); + + error = git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE, 0); + + git_reference_free(head_reference); + + return error; } static int repo_init_structure(const char *git_dir, int is_bare) { - const int mode = 0755; /* or 0777 ? */ int error; char temp_path[GIT_PATH_MAX]; - if (git_futils_mkdir_r(git_dir, mode)) + if (git_futils_mkdir_r(git_dir, is_bare ? GIT_BARE_DIR_MODE : GIT_DIR_MODE)) return git__throw(GIT_ERROR, "Failed to initialize repository structure. Could not mkdir"); /* Hides the ".git" directory */ @@ -628,25 +628,25 @@ static int repo_init_structure(const char *git_dir, int is_bare) /* Creates the '/objects/info/' directory */ git_path_join(temp_path, git_dir, GIT_OBJECTS_INFO_DIR); - error = git_futils_mkdir_r(temp_path, mode); + error = git_futils_mkdir_r(temp_path, GIT_OBJECT_DIR_MODE); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to initialize repository structure"); /* Creates the '/objects/pack/' directory */ git_path_join(temp_path, git_dir, GIT_OBJECTS_PACK_DIR); - error = p_mkdir(temp_path, mode); + error = p_mkdir(temp_path, GIT_OBJECT_DIR_MODE); if (error < GIT_SUCCESS) return git__throw(error, "Unable to create `%s` folder", temp_path); /* Creates the '/refs/heads/' directory */ git_path_join(temp_path, git_dir, GIT_REFS_HEADS_DIR); - error = git_futils_mkdir_r(temp_path, mode); + error = git_futils_mkdir_r(temp_path, GIT_REFS_DIR_MODE); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to initialize repository structure"); /* Creates the '/refs/tags/' directory */ git_path_join(temp_path, git_dir, GIT_REFS_TAGS_DIR); - error = p_mkdir(temp_path, mode); + error = p_mkdir(temp_path, GIT_REFS_DIR_MODE); if (error < GIT_SUCCESS) return git__throw(error, "Unable to create `%s` folder", temp_path); @@ -716,10 +716,15 @@ int git_repository_head_detached(git_repository *repo) if (error < GIT_SUCCESS) return error; - if (git_reference_type(ref) == GIT_REF_SYMBOLIC) + if (git_reference_type(ref) == GIT_REF_SYMBOLIC) { + git_reference_free(ref); return 0; + } error = git_odb_read_header(&_size, &type, repo->db, git_reference_oid(ref)); + + git_reference_free(ref); + if (error < GIT_SUCCESS) return error; @@ -731,7 +736,7 @@ int git_repository_head_detached(git_repository *repo) int git_repository_head(git_reference **head_out, git_repository *repo) { - git_reference *ref; + git_reference *ref, *resolved_ref; int error; *head_out = NULL; @@ -740,11 +745,15 @@ int git_repository_head(git_reference **head_out, git_repository *repo) if (error < GIT_SUCCESS) return git__rethrow(GIT_ENOTAREPO, "Failed to locate the HEAD"); - error = git_reference_resolve(&ref, ref); - if (error < GIT_SUCCESS) + error = git_reference_resolve(&resolved_ref, ref); + if (error < GIT_SUCCESS) { + git_reference_free(ref); return git__rethrow(error, "Failed to resolve the HEAD"); + } + + git_reference_free(ref); - *head_out = ref; + *head_out = resolved_ref; return GIT_SUCCESS; } @@ -755,25 +764,36 @@ int git_repository_head_orphan(git_repository *repo) error = git_repository_head(&ref, repo); + if (error == GIT_SUCCESS) + git_reference_free(ref); + return error == GIT_ENOTFOUND ? 1 : error; } int git_repository_is_empty(git_repository *repo) { - git_reference *head, *branch; + git_reference *head = NULL, *branch = NULL; int error; error = git_reference_lookup(&head, repo, "HEAD"); if (error < GIT_SUCCESS) return git__throw(error, "Corrupted repository. HEAD does not exist"); - if (git_reference_type(head) != GIT_REF_SYMBOLIC) + if (git_reference_type(head) != GIT_REF_SYMBOLIC) { + git_reference_free(head); return 0; + } - if (strcmp(git_reference_target(head), "refs/heads/master") != 0) + if (strcmp(git_reference_target(head), "refs/heads/master") != 0) { + git_reference_free(head); return 0; + } error = git_reference_resolve(&branch, head); + + git_reference_free(head); + git_reference_free(branch); + return error == GIT_ENOTFOUND ? 1 : error; } diff --git a/src/repository.h b/src/repository.h index 99217e5a4..0c17958fd 100644 --- a/src/repository.h +++ b/src/repository.h @@ -18,11 +18,12 @@ #include "cache.h" #include "refs.h" #include "buffer.h" +#include "odb.h" #define DOT_GIT ".git" #define GIT_DIR DOT_GIT "/" -#define GIT_OBJECTS_DIR "objects/" -#define GIT_INDEX_FILE "index" +#define GIT_DIR_MODE 0755 +#define GIT_BARE_DIR_MODE 0777 struct git_object { git_cached_obj cached; diff --git a/src/revwalk.c b/src/revwalk.c index 2d70d40e9..7e31650ff 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -68,7 +68,7 @@ static void commit_list_free(commit_list **list_p) while (list) { commit_list *temp = list; list = temp->next; - free(temp); + git__free(temp); } *list_p = NULL; @@ -81,7 +81,7 @@ static commit_object *commit_list_pop(commit_list **stack) if (top) { *stack = top->next; - free(top); + git__free(top); } return item; } @@ -156,7 +156,7 @@ static commit_object *commit_lookup(git_revwalk *walk, const git_oid *oid) git_oid_cpy(&commit->oid, oid); if (git_hashtable_insert(walk->commits, &commit->oid, commit) < GIT_SUCCESS) { - free(commit); + git__free(commit); return NULL; } @@ -442,7 +442,7 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo) (git_hash_keyeq_ptr)git_oid_cmp); if (walk->commits == NULL) { - free(walk); + git__free(walk); return GIT_ENOMEM; } @@ -475,17 +475,17 @@ void git_revwalk_free(git_revwalk *walk) * make sure it's being free'd */ GIT_HASHTABLE_FOREACH(walk->commits, _unused, commit, { if (commit->out_degree > PARENTS_PER_COMMIT) - free(commit->parents); + git__free(commit->parents); }); git_hashtable_free(walk->commits); git_pqueue_free(&walk->iterator_time); for (i = 0; i < walk->memory_alloc.length; ++i) - free(git_vector_get(&walk->memory_alloc, i)); + git__free(git_vector_get(&walk->memory_alloc, i)); git_vector_free(&walk->memory_alloc); - free(walk); + git__free(walk); } git_repository *git_revwalk_repository(git_revwalk *walk) diff --git a/src/signature.c b/src/signature.c index 388e8d9c9..832d6439c 100644 --- a/src/signature.c +++ b/src/signature.c @@ -15,9 +15,9 @@ void git_signature_free(git_signature *sig) if (sig == NULL) return; - free(sig->name); - free(sig->email); - free(sig); + git__free(sig->name); + git__free(sig->email); + git__free(sig); } static const char *skip_leading_spaces(const char *buffer, const char *buffer_end) diff --git a/src/status.c b/src/status.c index 1deade9a5..d50199d9a 100644 --- a/src/status.c +++ b/src/status.c @@ -142,16 +142,14 @@ static int retrieve_head_tree(git_tree **tree_out, git_repository *repo) *tree_out = NULL; error = git_repository_head(&resolved_head_ref, repo); - if (error != GIT_SUCCESS && error != GIT_ENOTFOUND) - return git__rethrow(error, "HEAD can't be resolved"); - /* * We assume that a situation where HEAD exists but can not be resolved is valid. * A new repository fits this description for instance. */ - if (error == GIT_ENOTFOUND) return GIT_SUCCESS; + if (error < GIT_SUCCESS) + return git__rethrow(error, "HEAD can't be resolved"); if ((error = git_commit_lookup(&head_commit, repo, git_reference_oid(resolved_head_ref))) < GIT_SUCCESS) return git__rethrow(error, "The tip of HEAD can't be retrieved"); @@ -168,41 +166,45 @@ exit: return error; } -#define GIT_STATUS_PATH_NULL -2 -#define GIT_STATUS_PATH_IGNORE -1 -#define GIT_STATUS_PATH_FILE 0 -#define GIT_STATUS_PATH_FOLDER 1 +enum path_type { + GIT_STATUS_PATH_NULL, + GIT_STATUS_PATH_IGNORE, + GIT_STATUS_PATH_FILE, + GIT_STATUS_PATH_FOLDER, +}; static int dirent_cb(void *state, char *full_path); static int alphasorted_futils_direach( char *path, size_t path_sz, int (*fn)(void *, char *), void *arg); -static int process_folder(struct status_st *st, const git_tree_entry *tree_entry, char *full_path, int path_type) +static int process_folder(struct status_st *st, const git_tree_entry *tree_entry, char *full_path, enum path_type path_type) { git_object *subtree = NULL; git_tree *pushed_tree = NULL; int error, pushed_tree_position = 0; - git_otype tree_entry_type; - - tree_entry_type = git_tree_entry_type(tree_entry); - - switch (tree_entry_type) { - case GIT_OBJ_TREE: - error = git_tree_entry_2object(&subtree, ((git_object *)(st->tree))->repo, tree_entry); - pushed_tree = st->tree; - pushed_tree_position = st->tree_position; - st->tree = (git_tree *)subtree; - st->tree_position = 0; - st->head_tree_relative_path_len += 1 + tree_entry->filename_len; /* path + '/' + name */ - break; - - case GIT_OBJ_BLOB: - /* No op */ - break; - - default: - error = git__throw(GIT_EINVALIDTYPE, "Unexpected tree entry type"); /* TODO: How should we deal with submodules? */ + git_otype tree_entry_type = GIT_OBJ_BAD; + + if (tree_entry != NULL) { + tree_entry_type = git_tree_entry_type(tree_entry); + + switch (tree_entry_type) { + case GIT_OBJ_TREE: + error = git_tree_entry_2object(&subtree, ((git_object *)(st->tree))->repo, tree_entry); + pushed_tree = st->tree; + pushed_tree_position = st->tree_position; + st->tree = (git_tree *)subtree; + st->tree_position = 0; + st->head_tree_relative_path_len += 1 + tree_entry->filename_len; /* path + '/' + name */ + break; + + case GIT_OBJ_BLOB: + /* No op */ + break; + + default: + error = git__throw(GIT_EINVALIDTYPE, "Unexpected tree entry type"); /* TODO: How should we deal with submodules? */ + } } if (full_path != NULL && path_type == GIT_STATUS_PATH_FOLDER) @@ -229,7 +231,7 @@ static int store_if_changed(struct status_st *st, struct status_entry *e) return git__throw(error, "Failed to process the file '%s'. It doesn't exist in the workdir, in the HEAD nor in the index", e->path); if (e->status_flags == GIT_STATUS_CURRENT) { - free(e); + git__free(e); return GIT_SUCCESS; } @@ -242,7 +244,7 @@ static int determine_status(struct status_st *st, const git_index_entry *index_entry, char *full_path, const char *status_path, - int path_type) + enum path_type path_type) { struct status_entry *e; int error = GIT_SUCCESS; @@ -289,7 +291,7 @@ static int path_type_from(char *full_path, int is_dir) if (!is_dir) return GIT_STATUS_PATH_FILE; - if (!git__suffixcmp(full_path, "/" DOT_GIT)) + if (!git__suffixcmp(full_path, "/" DOT_GIT "/")) return GIT_STATUS_PATH_IGNORE; return GIT_STATUS_PATH_FOLDER; @@ -324,30 +326,6 @@ static int compare(const char *left, const char *right) return strcmp(left, right); } -/* - * Convenience method to enumerate a tree. Contrarily to the git_tree_entry_byindex() - * method, it allows the tree to be enumerated to be NULL. In this case, every returned - * tree entry will be NULL as well. - */ -static const git_tree_entry *git_tree_entry_bypos(git_tree *tree, unsigned int idx) -{ - if (tree == NULL) - return NULL; - - return git_vector_get(&tree->entries, idx); -} - -/* - * Convenience method to enumerate the index. This method is not supposed to be exposed - * as part of the index API because it precludes that the index will not be altered - * while the enumeration is being processed. Which wouldn't be very API friendly :) - */ -static const git_index_entry *git_index_entry_bypos(git_index *index, unsigned int idx) -{ - assert(index); - return git_vector_get(&index->entries, idx); -} - /* Greatly inspired from JGit IndexTreeWalker */ /* https://github.com/spearce/jgit/blob/ed47e29c777accfa78c6f50685a5df2b8f5b8ff5/org.spearce.jgit/src/org/spearce/jgit/lib/IndexTreeWalker.java#L88 */ @@ -355,7 +333,7 @@ static int dirent_cb(void *state, char *a) { const git_tree_entry *m; const git_index_entry *entry; - int path_type; + enum path_type path_type; int cmpma, cmpmi, cmpai, error; const char *pm, *pa, *pi; const char *m_name, *i_name, *a_name; @@ -370,15 +348,25 @@ static int dirent_cb(void *state, char *a) a_name = (path_type != GIT_STATUS_PATH_NULL) ? a + st->workdir_path_len : NULL; while (1) { - m = git_tree_entry_bypos(st->tree, st->tree_position); - entry = git_index_entry_bypos(st->index, st->index_position); + if (st->tree == NULL) + m = NULL; + else + m = git_tree_entry_byindex(st->tree, st->tree_position); + + entry = git_index_get(st->index, st->index_position); if ((m == NULL) && (a == NULL) && (entry == NULL)) return GIT_SUCCESS; if (m != NULL) { st->head_tree_relative_path[st->head_tree_relative_path_len] = '\0'; - git_path_join(st->head_tree_relative_path, st->head_tree_relative_path, m->filename); + + /* When the tree entry is a folder, append a forward slash to its name */ + if (git_tree_entry_type(m) == GIT_OBJ_TREE) + git_path_join_n(st->head_tree_relative_path, 3, st->head_tree_relative_path, m->filename, ""); + else + git_path_join(st->head_tree_relative_path, st->head_tree_relative_path, m->filename); + m_name = st->head_tree_relative_path; } else m_name = NULL; @@ -396,7 +384,7 @@ static int dirent_cb(void *state, char *a) if((error = determine_status(st, pm != NULL, pi != NULL, pa != NULL, m, entry, a, status_path(pm, pi, pa), path_type)) < GIT_SUCCESS) return git__rethrow(error, "An error occured while determining the status of '%s'", a); - if (pa != NULL) + if ((pa != NULL) || (path_type == GIT_STATUS_PATH_FOLDER)) return GIT_SUCCESS; } } @@ -466,7 +454,7 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig error = git__rethrow(error, "Failed to determine statuses. User callback failed"); } - free(e); + git__free(e); } exit: @@ -570,7 +558,7 @@ int git_status_file(unsigned int *status_flags, git_repository *repo, const char exit: git_tree_close(tree); - free(e); + git__free(e); return error; } @@ -589,19 +577,32 @@ struct alphasorted_dirent_info { static struct alphasorted_dirent_info *alphasorted_dirent_info_new(const char *path) { - int is_dir; + int is_dir, size; struct alphasorted_dirent_info *di; is_dir = git_futils_isdir(path) == GIT_SUCCESS ? 1 : 0; + size = sizeof(*di) + (is_dir ? GIT_PATH_MAX : strlen(path)) + 2; - di = git__malloc(sizeof(*di) + (is_dir ? GIT_PATH_MAX : strlen(path)) + 1); + di = git__malloc(size); if (di == NULL) return NULL; - memset(di, 0x0, sizeof(*di)); + memset(di, 0x0, size); strcpy(di->path, path); - di->is_dir = is_dir; + + if (is_dir) { + di->is_dir = 1; + + /* + * Append a forward slash to the name to force folders + * to be ordered in a similar way than in a tree + * + * The file "subdir" should appear before the file "subdir.txt" + * The folder "subdir" should appear after the file "subdir.txt" + */ + di->path[strlen(path)] = '/'; + } return di; } @@ -626,7 +627,7 @@ static int alphasorted_dirent_cb(void *state, char *full_path) return GIT_ENOMEM; if (git_vector_insert(entry_names, entry) < GIT_SUCCESS) { - free(entry); + git__free(entry); return GIT_ENOMEM; } @@ -659,7 +660,7 @@ static int alphasorted_futils_direach( error = fn(arg, entry->path); } - free(entry); + git__free(entry); } git_vector_free(&entry_names); @@ -16,9 +16,9 @@ void git_tag__free(git_tag *tag) { git_signature_free(tag->tagger); - free(tag->message); - free(tag->tag_name); - free(tag); + git__free(tag->message); + git__free(tag->tag_name); + git__free(tag); } const git_oid *git_tag_id(git_tag *c) @@ -153,6 +153,8 @@ static int retrieve_tag_reference(git_reference **tag_reference_out, char *ref_n git_reference *tag_ref; int error; + *tag_reference_out = NULL; + git_path_join(ref_name_out, GIT_REFS_TAGS_DIR, tag_name); error = git_reference_lookup(&tag_ref, repo, ref_name_out); if (error < GIT_SUCCESS) @@ -224,6 +226,7 @@ static int git_tag_create__internal( break; default: + git_reference_free(new_ref); return git__rethrow(error, "Failed to create tag"); } @@ -232,6 +235,7 @@ static int git_tag_create__internal( if (new_ref != NULL) { if (!allow_ref_overwrite) { git_oid_cpy(oid, git_reference_oid(new_ref)); + git_reference_free(new_ref); return git__throw(GIT_EEXISTS, "Tag already exists"); } else { should_update_ref = 1; @@ -239,8 +243,10 @@ static int git_tag_create__internal( } if (create_tag_annotation) { - if ((error = write_tag_annotation(oid, repo, tag_name, target, tagger, message)) < GIT_SUCCESS) + if ((error = write_tag_annotation(oid, repo, tag_name, target, tagger, message)) < GIT_SUCCESS) { + git_reference_free(new_ref); return error; + } } else git_oid_cpy(oid, git_object_id(target)); @@ -249,6 +255,8 @@ static int git_tag_create__internal( else error = git_reference_set_oid(new_ref, oid); + git_reference_free(new_ref); + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag"); } @@ -281,7 +289,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu git_odb_stream *stream; git_odb_object *target_obj; - git_reference *new_ref; + git_reference *new_ref = NULL; char ref_name[GIT_REFNAME_MAX]; assert(oid && buffer); @@ -309,6 +317,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu break; default: + git_reference_free(new_ref); return git__rethrow(error, "Failed to create tag"); } @@ -317,6 +326,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu if (new_ref != NULL) { if (!allow_ref_overwrite) { git_oid_cpy(oid, git_reference_oid(new_ref)); + git_reference_free(new_ref); return git__throw(GIT_EEXISTS, "Tag already exists"); } else { should_update_ref = 1; @@ -324,25 +334,31 @@ 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, repo->db, strlen(buffer), GIT_OBJ_TAG)) < GIT_SUCCESS) { + git_reference_free(new_ref); return git__rethrow(error, "Failed to create tag"); + } stream->write(stream, buffer, strlen(buffer)); error = stream->finalize_write(oid, stream); stream->free(stream); - if (error < GIT_SUCCESS) + if (error < GIT_SUCCESS) { + git_reference_free(new_ref); return git__rethrow(error, "Failed to create tag"); + } if (!should_update_ref) error = git_reference_create_oid(&new_ref, repo, ref_name, oid, 0); else error = git_reference_set_oid(new_ref, oid); + git_reference_free(new_ref); + git_signature_free(tag.tagger); - free(tag.tag_name); - free(tag.message); + git__free(tag.tag_name); + git__free(tag.message); return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag"); } diff --git a/src/thread-utils.h b/src/thread-utils.h index 3361ed8bc..c5554799c 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_thread_utils_h__ #define INCLUDE_thread_utils_h__ - +#include "common.h" /* Common operations even if threading has been disabled */ typedef struct { diff --git a/src/transports/git.c b/src/transports/git.c index 489807851..c2014529b 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -109,8 +109,8 @@ static int do_connect(transport_git *t, const char *url) error = send_request(s, NULL, url); t->socket = s; - free(host); - free(port); + git__free(host); + git__free(port); if (error < GIT_SUCCESS && s > 0) close(s); @@ -357,11 +357,11 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, g gitno_consume(buf, line_end); if (pkt->type == GIT_PKT_ACK) { - free(pkt); + git__free(pkt); error = GIT_SUCCESS; goto done; } else if (pkt->type == GIT_PKT_NAK) { - free(pkt); + git__free(pkt); break; } else { error = git__throw(GIT_ERROR, "Got unexpected pkt type"); @@ -424,12 +424,12 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor return error; if (pkt->type == GIT_PKT_PACK) { - free(pkt); + git__free(pkt); return git_fetch__download_pack(out, buf->data, buf->offset, t->socket, repo); } /* For now we don't care about anything */ - free(pkt); + git__free(pkt); gitno_consume(buf, line_end); } @@ -475,9 +475,9 @@ static void git_free(git_transport *transport) } git_vector_free(refs); - free(t->heads); - free(t->parent.url); - free(t); + git__free(t->heads); + git__free(t->parent.url); + git__free(t); } int git_transport_git(git_transport **out) diff --git a/src/transports/http.c b/src/transports/http.c index 680354bae..66b6f252c 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -15,6 +15,7 @@ #include "buffer.h" #include "pkt.h" #include "refs.h" +#include "pack.h" #include "fetch.h" #include "filebuf.h" #include "repository.h" @@ -389,18 +390,18 @@ static int on_body_parse_response(http_parser *parser, const char *str, size_t l git_buf_consume(buf, line_end); if (pkt->type == GIT_PKT_PACK) { - free(pkt); + git__free(pkt); t->pack_ready = 1; return 0; } if (pkt->type == GIT_PKT_NAK) { - free(pkt); + git__free(pkt); return 0; } if (pkt->type != GIT_PKT_ACK) { - free(pkt); + git__free(pkt); continue; } @@ -702,7 +703,7 @@ static int http_download_pack(char **out, git_transport *transport, git_reposito } /* A bit dodgy, but we need to keep the pack at the temporary path */ - error = git_filebuf_commit_at(&file, file.path_lock); + error = git_filebuf_commit_at(&file, file.path_lock, GIT_PACK_FILE_MODE); cleanup: if (error < GIT_SUCCESS) @@ -749,13 +750,13 @@ static void http_free(git_transport *transport) } git_vector_free(common); git_buf_free(&t->buf); - free(t->heads); - free(t->content_type); - free(t->host); - free(t->port); - free(t->service); - free(t->parent.url); - free(t); + git__free(t->heads); + git__free(t->content_type); + git__free(t->host); + git__free(t->port); + git__free(t->service); + git__free(t->parent.url); + git__free(t); } int git_transport_http(git_transport **out) diff --git a/src/transports/local.c b/src/transports/local.c index 3f47e9b89..058ed7e79 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -54,7 +54,7 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec) { const char peeled[] = "^{}"; git_remote_head *head; - git_reference *ref; + git_reference *ref, *resolved_ref; git_object *obj = NULL; int error = GIT_SUCCESS, peel_len, ret; @@ -72,7 +72,7 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec) if (error < GIT_SUCCESS) goto out; - error = git_reference_resolve(&ref, ref); + error = git_reference_resolve(&resolved_ref, ref); if (error < GIT_SUCCESS) goto out; @@ -111,10 +111,13 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec) goto out; out: + git_reference_free(ref); + git_reference_free(resolved_ref); + git_object_close(obj); if (error < GIT_SUCCESS) { - free(head->name); - free(head); + git__free(head->name); + git__free(head); } return error; } @@ -190,16 +193,16 @@ static void local_free(git_transport *transport) if (t->refs != NULL) { git_vector_foreach (vec, i, h) { - free(h->name); - free(h); + git__free(h->name); + git__free(h); } git_vector_free(vec); - free(vec); + git__free(vec); } git_repository_free(t->repo); - free(t->parent.url); - free(t); + git__free(t->parent.url); + git__free(t); } /************** diff --git a/src/tree-cache.c b/src/tree-cache.c index 5a3257520..ea8b7bfb7 100644 --- a/src/tree-cache.c +++ b/src/tree-cache.c @@ -196,6 +196,6 @@ void git_tree_cache_free(git_tree_cache *tree) for (i = 0; i < tree->children_count; ++i) git_tree_cache_free(tree->children[i]); - free(tree->children); - free(tree); + git__free(tree->children); + git__free(tree); } diff --git a/src/tree.c b/src/tree.c index 00aefc295..92ca5ab77 100644 --- a/src/tree.c +++ b/src/tree.c @@ -15,6 +15,8 @@ #define MAX_FILEMODE 0777777 #define MAX_FILEMODE_BYTES 6 +#define ENTRY_IS_TREE(e) ((e)->attr & 040000) + static int valid_attributes(const int attributes) { return attributes >= 0 && attributes <= MAX_FILEMODE; @@ -31,8 +33,8 @@ static int entry_sort_cmp(const void *a, const void *b) const git_tree_entry *entry_b = (const git_tree_entry *)(b); return git_futils_cmp_path( - entry_a->filename, entry_a->filename_len, entry_a->attr & 040000, - entry_b->filename, entry_b->filename_len, entry_b->attr & 040000); + entry_a->filename, entry_a->filename_len, ENTRY_IS_TREE(entry_a), + entry_b->filename, entry_b->filename_len, ENTRY_IS_TREE(entry_b)); } @@ -128,12 +130,12 @@ void git_tree__free(git_tree *tree) git_tree_entry *e; e = git_vector_get(&tree->entries, i); - free(e->filename); - free(e); + git__free(e->filename); + git__free(e); } git_vector_free(&tree->entries); - free(tree); + git__free(tree); } const git_oid *git_tree_id(git_tree *c) @@ -376,7 +378,7 @@ static int write_tree(git_oid *oid, git_index *index, const char *dirname, unsig last_comp = subdir; } error = append_entry(bld, last_comp, &sub_oid, S_IFDIR); - free(subdir); + git__free(subdir); if (error < GIT_SUCCESS) { error = git__rethrow(error, "Failed to insert dir"); goto cleanup; @@ -439,7 +441,7 @@ int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source) source_entries = source->entries.length; if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < GIT_SUCCESS) { - free(bld); + git__free(bld); return GIT_ENOMEM; } @@ -594,8 +596,8 @@ void git_treebuilder_clear(git_treebuilder *bld) for (i = 0; i < bld->entries.length; ++i) { git_tree_entry *e = bld->entries.contents[i]; - free(e->filename); - free(e); + git__free(e->filename); + git__free(e); } git_vector_clear(&bld->entries); @@ -605,10 +607,14 @@ void git_treebuilder_free(git_treebuilder *bld) { git_treebuilder_clear(bld); git_vector_free(&bld->entries); - free(bld); + git__free(bld); } -static int tree_frompath(git_tree **parent_out, git_tree *root, const char *treeentry_path, int offset) +static int tree_frompath( + git_tree **parent_out, + git_tree *root, + const char *treeentry_path, + int offset) { char *slash_pos = NULL; const git_tree_entry* entry; @@ -616,15 +622,21 @@ static int tree_frompath(git_tree **parent_out, git_tree *root, const char *tree git_tree *subtree; if (!*(treeentry_path + offset)) - return git__rethrow(GIT_EINVALIDPATH, "Invalid relative path to a tree entry '%s'.", treeentry_path); + return git__rethrow(GIT_EINVALIDPATH, + "Invalid relative path to a tree entry '%s'.", treeentry_path); slash_pos = (char *)strchr(treeentry_path + offset, '/'); if (slash_pos == NULL) - return git_tree_lookup(parent_out, root->object.repo, git_object_id((const git_object *)root)); + return git_tree_lookup( + parent_out, + root->object.repo, + git_object_id((const git_object *)root) + ); if (slash_pos == treeentry_path + offset) - return git__rethrow(GIT_EINVALIDPATH, "Invalid relative path to a tree entry '%s'.", treeentry_path); + return git__rethrow(GIT_EINVALIDPATH, + "Invalid relative path to a tree entry '%s'.", treeentry_path); *slash_pos = '\0'; @@ -634,23 +646,95 @@ static int tree_frompath(git_tree **parent_out, git_tree *root, const char *tree *slash_pos = '/'; if (entry == NULL) - return git__rethrow(GIT_ENOTFOUND, "No tree entry can be found from the given tree and relative path '%s'.", treeentry_path); + return git__rethrow(GIT_ENOTFOUND, + "No tree entry can be found from " + "the given tree and relative path '%s'.", treeentry_path); + - if ((error = git_tree_lookup(&subtree, root->object.repo, &entry->oid)) < GIT_SUCCESS) + error = git_tree_lookup(&subtree, root->object.repo, &entry->oid); + if (error < GIT_SUCCESS) return error; - error = tree_frompath(parent_out, subtree, treeentry_path, slash_pos - treeentry_path + 1); + error = tree_frompath( + parent_out, + subtree, + treeentry_path, + slash_pos - treeentry_path + 1 + ); git_tree_close(subtree); return error; } -int git_tree_frompath(git_tree **parent_out, git_tree *root, const char *treeentry_path) +int git_tree_get_subtree( + git_tree **subtree, + git_tree *root, + const char *subtree_path) { char buffer[GIT_PATH_MAX]; - assert(root && treeentry_path); + assert(subtree && root && subtree_path); + + strncpy(buffer, subtree_path, GIT_PATH_MAX); + return tree_frompath(subtree, root, buffer, 0); +} + +static int tree_walk_post( + git_tree *tree, + git_treewalk_cb callback, + char *root, + size_t root_len, + void *payload) +{ + int error; + unsigned int i; + + for (i = 0; i < tree->entries.length; ++i) { + git_tree_entry *entry = tree->entries.contents[i]; + + root[root_len] = '\0'; + + if (callback(root, entry, payload) < 0) + continue; + + if (ENTRY_IS_TREE(entry)) { + git_tree *subtree; + + if ((error = git_tree_lookup( + &subtree, tree->object.repo, &entry->oid)) < 0) + return error; + + strcpy(root + root_len, entry->filename); + root[root_len + entry->filename_len] = '/'; + + tree_walk_post(subtree, + callback, root, + root_len + entry->filename_len + 1, + payload + ); - strcpy(buffer, treeentry_path); - return tree_frompath(parent_out, root, buffer, 0); + git_tree_close(subtree); + } + } + + return GIT_SUCCESS; +} + +int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload) +{ + char root_path[GIT_PATH_MAX]; + + root_path[0] = '\0'; + switch (mode) { + case GIT_TREEWALK_POST: + return tree_walk_post(tree, callback, root_path, 0, payload); + + case GIT_TREEWALK_PRE: + return git__throw(GIT_ENOTIMPLEMENTED, + "Preorder tree walking is still not implemented"); + + default: + return git__throw(GIT_EINVALIDARGS, + "Invalid walking mode for tree walk"); + } } diff --git a/src/tsort.c b/src/tsort.c index 5dd99cc6e..df230b59d 100644 --- a/src/tsort.c +++ b/src/tsort.c @@ -178,7 +178,7 @@ static int check_invariant(struct tsort_run *stack, int stack_curr) static int resize(struct tsort_store *store, size_t new_size) { if (store->alloc < new_size) { - void **tempstore = realloc(store->storage, new_size * sizeof(void *)); + void **tempstore = git__realloc(store->storage, new_size * sizeof(void *)); /** * Do not propagate on OOM; this will abort the sort and @@ -319,7 +319,7 @@ static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr, stack_curr--; \ } \ if (store->storage != NULL) {\ - free(store->storage);\ + git__free(store->storage);\ store->storage = NULL;\ }\ return;\ diff --git a/src/util.c b/src/util.c index c81ed2d3a..b3af7ffd8 100644 --- a/src/util.c +++ b/src/util.c @@ -26,9 +26,9 @@ void git_strarray_free(git_strarray *array) { size_t i; for (i = 0; i < array->count; ++i) - free(array->strings[i]); + git__free(array->strings[i]); - free(array->strings); + git__free(array->strings); } int git__fnmatch(const char *pattern, const char *name, int flags) diff --git a/src/util.h b/src/util.h index 4de91b494..fbf9012a3 100644 --- a/src/util.h +++ b/src/util.h @@ -72,6 +72,8 @@ GIT_INLINE(void *) git__realloc(void *ptr, size_t size) return new_ptr; } +#define git__free(ptr) free(ptr) + extern int git__prefixcmp(const char *str, const char *prefix); extern int git__suffixcmp(const char *str, const char *suffix); diff --git a/src/vector.c b/src/vector.c index 8b20bb8ef..123aae8e6 100644 --- a/src/vector.c +++ b/src/vector.c @@ -18,7 +18,7 @@ static int resize_vector(git_vector *v) if (v->_alloc_size < minimum_size) v->_alloc_size = minimum_size; - v->contents = realloc(v->contents, v->_alloc_size * sizeof(void *)); + v->contents = git__realloc(v->contents, v->_alloc_size * sizeof(void *)); if (v->contents == NULL) return GIT_ENOMEM; @@ -29,7 +29,7 @@ static int resize_vector(git_vector *v) void git_vector_free(git_vector *v) { assert(v); - free(v->contents); + git__free(v->contents); } int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp) diff --git a/src/win32/dir.c b/src/win32/dir.c index ab50318e3..01aaaaad3 100644 --- a/src/win32/dir.c +++ b/src/win32/dir.c @@ -6,7 +6,8 @@ */ #define GIT__WIN32_NO_WRAP_DIR #include "dir.h" -#include "utf8-conv.h" +#include "utf-conv.h" +#include "git2/windows.h" static int init_filter(char *filter, size_t n, const char *dir) { @@ -38,18 +39,18 @@ git__DIR *git__opendir(const char *dir) new->dir = git__malloc(strlen(dir)+1); if (!new->dir) { - free(new); + git__free(new); return NULL; } strcpy(new->dir, dir); - filter_w = conv_utf8_to_utf16(filter); + filter_w = gitwin_to_utf16(filter); new->h = FindFirstFileW(filter_w, &new->f); - free(filter_w); + git__free(filter_w); if (new->h == INVALID_HANDLE_VALUE) { - free(new->dir); - free(new); + git__free(new->dir); + git__free(new); return NULL; } new->first = 1; @@ -73,7 +74,7 @@ struct git__dirent *git__readdir(git__DIR *d) return NULL; d->entry.d_ino = 0; - WideCharToMultiByte(CP_UTF8, 0, d->f.cFileName, -1, d->entry.d_name, GIT_PATH_MAX, NULL, NULL); + WideCharToMultiByte(gitwin_get_codepage(), 0, d->f.cFileName, -1, d->entry.d_name, GIT_PATH_MAX, NULL, NULL); return &d->entry; } @@ -90,9 +91,9 @@ void git__rewinddir(git__DIR *d) d->first = 0; if (init_filter(filter, sizeof(filter), d->dir)) { - filter_w = conv_utf8_to_utf16(filter); + filter_w = gitwin_to_utf16(filter); d->h = FindFirstFileW(filter_w, &d->f); - free(filter_w); + git__free(filter_w); if (d->h != INVALID_HANDLE_VALUE) d->first = 1; @@ -106,8 +107,8 @@ int git__closedir(git__DIR *d) if (d->h != INVALID_HANDLE_VALUE) FindClose(d->h); if (d->dir) - free(d->dir); - free(d); + git__free(d->dir); + git__free(d); } return 0; } diff --git a/src/win32/posix.h b/src/win32/posix.h index 442717e42..ae6323679 100644 --- a/src/win32/posix.h +++ b/src/win32/posix.h @@ -9,7 +9,7 @@ #include "common.h" #include "fnmatch.h" -#include "utf8-conv.h" +#include "utf-conv.h" GIT_INLINE(int) p_link(const char *GIT_UNUSED(old), const char *GIT_UNUSED(new)) { @@ -19,14 +19,14 @@ GIT_INLINE(int) p_link(const char *GIT_UNUSED(old), const char *GIT_UNUSED(new)) return -1; } -GIT_INLINE(int) p_mkdir(const char *path, int GIT_UNUSED(mode)) +GIT_INLINE(int) p_mkdir(const char *path, mode_t GIT_UNUSED(mode)) { - wchar_t* buf = conv_utf8_to_utf16(path); + wchar_t* buf = gitwin_to_utf16(path); int ret = _wmkdir(buf); GIT_UNUSED_ARG(mode) - free(buf); + git__free(buf); return ret; } @@ -41,12 +41,13 @@ extern int p_mkstemp(char *tmp_path); extern int p_setenv(const char* name, const char* value, int overwrite); extern int p_stat(const char* path, struct stat* buf); extern int p_chdir(const char* path); -extern int p_chmod(const char* path, int mode); +extern int p_chmod(const char* path, mode_t mode); extern int p_rmdir(const char* path); -extern int p_access(const char* path, int mode); +extern int p_access(const char* path, mode_t mode); extern int p_fsync(int fd); extern int p_open(const char *path, int flags); -extern int p_creat(const char *path, int mode); +extern int p_creat(const char *path, mode_t mode); extern int p_getcwd(char *buffer_out, size_t size); +extern int p_rename(const char *from, const char *to); #endif diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index cc17cc71f..6f722581e 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -6,7 +6,7 @@ */ #include "posix.h" #include "path.h" -#include "utf8-conv.h" +#include "utf-conv.h" #include <errno.h> #include <io.h> #include <fcntl.h> @@ -17,10 +17,10 @@ int p_unlink(const char *path) int ret = 0; wchar_t* buf; - buf = conv_utf8_to_utf16(path); + buf = gitwin_to_utf16(path); _wchmod(buf, 0666); ret = _wunlink(buf); - free(buf); + git__free(buf); return ret; } @@ -59,7 +59,7 @@ GIT_INLINE(time_t) filetime_to_time_t(const FILETIME *ft) static int do_lstat(const char *file_name, struct stat *buf) { WIN32_FILE_ATTRIBUTE_DATA fdata; - wchar_t* fbuf = conv_utf8_to_utf16(file_name); + wchar_t* fbuf = gitwin_to_utf16(file_name); if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) { int fMode = S_IREAD; @@ -86,11 +86,11 @@ static int do_lstat(const char *file_name, struct stat *buf) buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); - free(fbuf); + git__free(fbuf); return GIT_SUCCESS; } - free(fbuf); + git__free(fbuf); switch (GetLastError()) { case ERROR_ACCESS_DENIED: @@ -161,7 +161,7 @@ int p_readlink(const char *link, char *target, size_t target_len) "'GetFinalPathNameByHandleW' is not available in this platform"); } - link_w = conv_utf8_to_utf16(link); + link_w = gitwin_to_utf16(link); hFile = CreateFileW(link_w, // file to open GENERIC_READ, // open for reading @@ -171,7 +171,7 @@ int p_readlink(const char *link, char *target, size_t target_len) FILE_FLAG_BACKUP_SEMANTICS, // normal file NULL); // no attr. template - free(link_w); + git__free(link_w); if (hFile == INVALID_HANDLE_VALUE) return GIT_EOSERR; @@ -184,17 +184,17 @@ int p_readlink(const char *link, char *target, size_t target_len) dwRet = pGetFinalPath(hFile, target_w, target_len, 0x0); if (dwRet >= target_len) { - free(target_w); + git__free(target_w); CloseHandle(hFile); return GIT_ENOMEM; } if (!WideCharToMultiByte(CP_UTF8, 0, target_w, -1, target, target_len * sizeof(char), NULL, NULL)) { - free(target_w); + git__free(target_w); return GIT_EOSERR; } - free(target_w); + git__free(target_w); CloseHandle(hFile); if (dwRet > 4) { @@ -223,20 +223,20 @@ int p_readlink(const char *link, char *target, size_t target_len) int p_open(const char *path, int flags) { int fd; - wchar_t* buf = conv_utf8_to_utf16(path); + wchar_t* buf = gitwin_to_utf16(path); fd = _wopen(buf, flags | _O_BINARY); - free(buf); + git__free(buf); return fd; } -int p_creat(const char *path, int mode) +int p_creat(const char *path, mode_t mode) { int fd; - wchar_t* buf = conv_utf8_to_utf16(path); + wchar_t* buf = gitwin_to_utf16(path); fd = _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode); - free(buf); + git__free(buf); return fd; } @@ -246,11 +246,11 @@ int p_getcwd(char *buffer_out, size_t size) _wgetcwd(buf, (int)size); if (!WideCharToMultiByte(CP_UTF8, 0, buf, -1, buffer_out, size, NULL, NULL)) { - free(buf); + git__free(buf); return GIT_EOSERR; } - free(buf); + git__free(buf); return GIT_SUCCESS; } @@ -261,40 +261,40 @@ int p_stat(const char* path, struct stat* buf) int p_chdir(const char* path) { - wchar_t* buf = conv_utf8_to_utf16(path); + wchar_t* buf = gitwin_to_utf16(path); int ret = _wchdir(buf); - free(buf); + git__free(buf); return ret; } -int p_chmod(const char* path, int mode) +int p_chmod(const char* path, mode_t mode) { - wchar_t* buf = conv_utf8_to_utf16(path); + wchar_t* buf = gitwin_to_utf16(path); int ret = _wchmod(buf, mode); - free(buf); + git__free(buf); return ret; } int p_rmdir(const char* path) { - wchar_t* buf = conv_utf8_to_utf16(path); + wchar_t* buf = gitwin_to_utf16(path); int ret = _wrmdir(buf); - free(buf); + git__free(buf); return ret; } int p_hide_directory__w32(const char *path) { int error; - wchar_t* buf = conv_utf8_to_utf16(path); + wchar_t* buf = gitwin_to_utf16(path); error = SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN) != 0 ? GIT_SUCCESS : GIT_ERROR; /* MSDN states a "non zero" value indicates a success */ - free(buf); + git__free(buf); if (error < GIT_SUCCESS) error = git__throw(GIT_EOSERR, "Failed to hide directory '%s'", path); @@ -305,7 +305,7 @@ int p_hide_directory__w32(const char *path) char *p_realpath(const char *orig_path, char *buffer) { int ret, alloc = 0; - wchar_t* orig_path_w = conv_utf8_to_utf16(orig_path); + wchar_t* orig_path_w = gitwin_to_utf16(orig_path); wchar_t* buffer_w = (wchar_t*)git__malloc(GIT_PATH_MAX * sizeof(wchar_t)); if (buffer == NULL) { @@ -314,21 +314,21 @@ char *p_realpath(const char *orig_path, char *buffer) } ret = GetFullPathNameW(orig_path_w, GIT_PATH_MAX, buffer_w, NULL); - free(orig_path_w); + git__free(orig_path_w); if (!ret || ret > GIT_PATH_MAX) { - free(buffer_w); - if (alloc) free(buffer); + git__free(buffer_w); + if (alloc) git__free(buffer); return NULL; } if (!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL)) { - free(buffer_w); - if (alloc) free(buffer); + git__free(buffer_w); + if (alloc) git__free(buffer); } - free(buffer_w); + git__free(buffer_w); git_path_mkposix(buffer); return buffer; } @@ -355,7 +355,7 @@ int p_snprintf(char *buffer, size_t count, const char *format, ...) return r; } -extern int p_creat(const char *path, int mode); +extern int p_creat(const char *path, mode_t mode); int p_mkstemp(char *tmp_path) { @@ -378,13 +378,27 @@ int p_setenv(const char* name, const char* value, int overwrite) return (SetEnvironmentVariableA(name, value) == 0 ? GIT_EOSERR : GIT_SUCCESS); } -int p_access(const char* path, int mode) +int p_access(const char* path, mode_t mode) { - wchar_t *buf = conv_utf8_to_utf16(path); + wchar_t *buf = gitwin_to_utf16(path); int ret; ret = _waccess(buf, mode); - free(buf); + git__free(buf); + + return ret; +} + +extern int p_rename(const char *from, const char *to) +{ + wchar_t *wfrom = gitwin_to_utf16(from); + wchar_t *wto = gitwin_to_utf16(to); + int ret; + + ret = MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR; + + git__free(wfrom); + git__free(wto); return ret; } diff --git a/src/win32/utf8-conv.c b/src/win32/utf-conv.c index dec6f8e79..b41c78f92 100644 --- a/src/win32/utf8-conv.c +++ b/src/win32/utf-conv.c @@ -6,9 +6,29 @@ */ #include "common.h" -#include "utf8-conv.h" +#include "utf-conv.h" -wchar_t* conv_utf8_to_utf16(const char* str) +/* + * Default codepage value + */ +static int _active_codepage = CP_UTF8; + +void gitwin_set_codepage(unsigned int codepage) +{ + _active_codepage = codepage; +} + +unsigned int gitwin_get_codepage(void) +{ + return _active_codepage; +} + +void gitwin_set_utf8(void) +{ + _active_codepage = CP_UTF8; +} + +wchar_t* gitwin_to_utf16(const char* str) { wchar_t* ret; int cb; @@ -29,15 +49,15 @@ wchar_t* conv_utf8_to_utf16(const char* str) ret = (wchar_t*)git__malloc(cb); - if (MultiByteToWideChar(CP_UTF8, 0, str, -1, ret, cb) == 0) { - free(ret); + if (MultiByteToWideChar(_active_codepage, 0, str, -1, ret, cb) == 0) { + git__free(ret); ret = NULL; } return ret; } -char* conv_utf16_to_utf8(const wchar_t* str) +char* gitwin_from_utf16(const wchar_t* str) { char* ret; int cb; @@ -58,8 +78,8 @@ char* conv_utf16_to_utf8(const wchar_t* str) ret = (char*)git__malloc(cb); - if (WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, cb, NULL, NULL) == 0) { - free(ret); + if (WideCharToMultiByte(_active_codepage, 0, str, -1, ret, cb, NULL, NULL) == 0) { + git__free(ret); ret = NULL; } diff --git a/src/win32/utf8-conv.h b/src/win32/utf-conv.h index 1967ac3a1..da03e3385 100644 --- a/src/win32/utf8-conv.h +++ b/src/win32/utf-conv.h @@ -7,11 +7,11 @@ #include <wchar.h> -#ifndef INCLUDE_git_utf8conv_h__ -#define INCLUDE_git_utf8conv_h__ +#ifndef INCLUDE_git_utfconv_h__ +#define INCLUDE_git_utfconv_h__ -wchar_t* conv_utf8_to_utf16(const char* str); -char* conv_utf16_to_utf8(const wchar_t* str); +wchar_t* gitwin_to_utf16(const char* str); +char* gitwin_from_utf16(const wchar_t* str); #endif |