diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/amiga/map.c | 51 | ||||
-rw-r--r-- | src/attr_file.c | 14 | ||||
-rw-r--r-- | src/branch.c | 56 | ||||
-rw-r--r-- | src/buffer.c | 36 | ||||
-rw-r--r-- | src/buffer.h | 19 | ||||
-rw-r--r-- | src/commit.c | 45 | ||||
-rw-r--r-- | src/config.c | 16 | ||||
-rw-r--r-- | src/config_file.c | 37 | ||||
-rw-r--r-- | src/config_file.h | 17 | ||||
-rw-r--r-- | src/diff_output.c | 2 | ||||
-rw-r--r-- | src/fetch.c | 9 | ||||
-rw-r--r-- | src/fileops.c | 2 | ||||
-rw-r--r-- | src/index.c | 2 | ||||
-rw-r--r-- | src/indexer.c | 52 | ||||
-rw-r--r-- | src/khash.h | 10 | ||||
-rw-r--r-- | src/map.h | 4 | ||||
-rw-r--r-- | src/mwindow.c | 18 | ||||
-rw-r--r-- | src/mwindow.h | 1 | ||||
-rw-r--r-- | src/netops.c | 10 | ||||
-rw-r--r-- | src/notes.c | 22 | ||||
-rw-r--r-- | src/object.c | 4 | ||||
-rw-r--r-- | src/odb.c | 12 | ||||
-rw-r--r-- | src/odb_loose.c | 84 | ||||
-rw-r--r-- | src/odb_pack.c | 20 | ||||
-rw-r--r-- | src/oidmap.h | 2 | ||||
-rw-r--r-- | src/pack.c | 46 | ||||
-rw-r--r-- | src/pack.h | 4 | ||||
-rw-r--r-- | src/path.c | 73 | ||||
-rw-r--r-- | src/path.h | 23 | ||||
-rw-r--r-- | src/pkt.c | 2 | ||||
-rw-r--r-- | src/pool.c | 2 | ||||
-rw-r--r-- | src/posix.c | 93 | ||||
-rw-r--r-- | src/posix.h | 37 | ||||
-rw-r--r-- | src/reflog.c | 48 | ||||
-rw-r--r-- | src/refs.c | 142 | ||||
-rw-r--r-- | src/repository.c | 139 | ||||
-rw-r--r-- | src/revparse.c | 1328 | ||||
-rw-r--r-- | src/revwalk.c | 42 | ||||
-rw-r--r-- | src/signature.c | 24 | ||||
-rw-r--r-- | src/strmap.h | 2 | ||||
-rw-r--r-- | src/submodule.c | 6 | ||||
-rw-r--r-- | src/transports/http.c | 7 | ||||
-rw-r--r-- | src/tree.c | 238 | ||||
-rw-r--r-- | src/tree.h | 6 | ||||
-rw-r--r-- | src/unix/map.c | 2 | ||||
-rw-r--r-- | src/unix/posix.h | 2 | ||||
-rw-r--r-- | src/util.h | 10 | ||||
-rw-r--r-- | src/win32/posix_w32.c | 2 |
48 files changed, 1834 insertions, 989 deletions
diff --git a/src/amiga/map.c b/src/amiga/map.c new file mode 100755 index 000000000..2fb065c8b --- /dev/null +++ b/src/amiga/map.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009-2012 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 <git2/common.h> + +#ifndef GIT_WIN32 + +#include "posix.h" +#include "map.h" +#include <errno.h> + +int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) +{ + GIT_MMAP_VALIDATE(out, len, prot, flags); + + out->data = NULL; + out->len = 0; + + if ((prot & GIT_PROT_WRITE) && ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)) { + giterr_set(GITERR_OS, "Trying to map shared-writeable"); + return -1; + } + + if((out->data = malloc(len))) { + p_lseek(fd, offset, SEEK_SET); + p_read(fd, out->data, len); + } + + if (!out->data || (out->data == MAP_FAILED)) { + giterr_set(GITERR_OS, "Failed to mmap. Could not write data"); + return -1; + } + + out->len = len; + + return 0; +} + +int p_munmap(git_map *map) +{ + assert(map != NULL); + free(map->data); + + return 0; +} + +#endif + diff --git a/src/attr_file.c b/src/attr_file.c index ca2f8fb58..0dad09727 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -5,9 +5,9 @@ #include "git2/tree.h" #include <ctype.h> -const char *git_attr__true = "[internal]__TRUE__"; -const char *git_attr__false = "[internal]__FALSE__"; -const char *git_attr__unset = "[internal]__UNSET__"; +const char *git_l_attr__true = "[internal]__TRUE__"; +const char *git_l_attr__false = "[internal]__FALSE__"; +const char *git_l_attr__unset = "[internal]__UNSET__"; static int sort_by_hash_and_name(const void *a_raw, const void *b_raw); static void git_attr_rule__clear(git_attr_rule *rule); @@ -503,14 +503,14 @@ int git_attr_assignment__parse( } assign->name_hash = 5381; - assign->value = git_attr__true; + assign->value = git_l_attr__true; /* look for magic name prefixes */ if (*scan == '-') { - assign->value = git_attr__false; + assign->value = git_l_attr__false; scan++; } else if (*scan == '!') { - assign->value = git_attr__unset; /* explicit unspecified state */ + assign->value = git_l_attr__unset; /* explicit unspecified state */ scan++; } else if (*scan == '#') /* comment rest of line */ break; @@ -546,7 +546,7 @@ int git_attr_assignment__parse( } /* expand macros (if given a repo with a macro cache) */ - if (repo != NULL && assign->value == git_attr__true) { + if (repo != NULL && assign->value == git_l_attr__true) { git_attr_rule *macro = git_attr_cache__lookup_macro(repo, assign->name); diff --git a/src/branch.c b/src/branch.c index 8b97a8206..671e42051 100644 --- a/src/branch.c +++ b/src/branch.c @@ -141,46 +141,46 @@ on_error: } typedef struct { - git_vector *branchlist; + int (*branch_cb)( + const char *branch_name, + git_branch_t branch_type, + void *payload); + void *callback_payload; unsigned int branch_type; -} branch_filter_data; +} branch_foreach_filter; -static int branch_list_cb(const char *branch_name, void *payload) +static int branch_foreach_cb(const char *branch_name, void *payload) { - branch_filter_data *filter = (branch_filter_data *)payload; + branch_foreach_filter *filter = (branch_foreach_filter *)payload; - if (filter->branch_type & GIT_BRANCH_LOCAL && git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0) { - return git_vector_insert(filter->branchlist, git__strdup(branch_name +strlen(GIT_REFS_HEADS_DIR))); - } else if (filter->branch_type & GIT_BRANCH_REMOTE && git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0) { - return git_vector_insert(filter->branchlist, git__strdup(branch_name+strlen(GIT_REFS_DIR))); - } + if (filter->branch_type & GIT_BRANCH_LOCAL && + git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0) + return filter->branch_cb(branch_name + strlen(GIT_REFS_HEADS_DIR), GIT_BRANCH_LOCAL, filter->callback_payload); + + if (filter->branch_type & GIT_BRANCH_REMOTE && + git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0) + return filter->branch_cb(branch_name + strlen(GIT_REFS_REMOTES_DIR), GIT_BRANCH_REMOTE, filter->callback_payload); return 0; } -int git_branch_list(git_strarray *branch_names, git_repository *repo, unsigned int list_flags) +int git_branch_foreach( + git_repository *repo, + unsigned int list_flags, + int (*branch_cb)( + const char *branch_name, + git_branch_t branch_type, + void *payload), + void *payload +) { - int error; - branch_filter_data filter; - git_vector branchlist; - - assert(branch_names && repo); + branch_foreach_filter filter; - if (git_vector_init(&branchlist, 8, NULL) < 0) - return -1; - - filter.branchlist = &branchlist; + filter.branch_cb = branch_cb; filter.branch_type = list_flags; + filter.callback_payload = payload; - error = git_reference_foreach(repo, GIT_REF_LISTALL, &branch_list_cb, (void *)&filter); - if (error < 0) { - git_vector_free(&branchlist); - return -1; - } - - branch_names->strings = (char **)branchlist.contents; - branch_names->count = branchlist.length; - return 0; + return git_reference_foreach(repo, GIT_REF_LISTALL, &branch_foreach_cb, (void *)&filter); } int git_branch_move(git_repository *repo, const char *old_branch_name, const char *new_branch_name, int force) diff --git a/src/buffer.c b/src/buffer.c index 04aaec3df..5d54ee1a5 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -141,6 +141,42 @@ int git_buf_puts(git_buf *buf, const char *string) return git_buf_put(buf, string, strlen(string)); } +int git_buf_puts_escaped( + git_buf *buf, const char *string, const char *esc_chars, const char *esc_with) +{ + const char *scan = string; + size_t total = 0, esc_with_len = strlen(esc_with); + + while (*scan) { + size_t count = strcspn(scan, esc_chars); + total += count + 1 + esc_with_len; + scan += count + 1; + } + + ENSURE_SIZE(buf, buf->size + total + 1); + + for (scan = string; *scan; ) { + size_t count = strcspn(scan, esc_chars); + + memmove(buf->ptr + buf->size, scan, count); + scan += count; + buf->size += count; + + if (*scan) { + memmove(buf->ptr + buf->size, esc_with, esc_with_len); + buf->size += esc_with_len; + + memmove(buf->ptr + buf->size, scan, 1); + scan += 1; + buf->size += 1; + } + } + + buf->ptr[buf->size] = '\0'; + + return 0; +} + int git_buf_vprintf(git_buf *buf, const char *format, va_list ap) { int len; diff --git a/src/buffer.h b/src/buffer.h index 50c75f64e..75f3b0e4f 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -91,6 +91,18 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...); int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b); /** + * Copy string into buf prefixing every character that is contained in the + * esc_chars string with the esc_with string. + */ +int git_buf_puts_escaped( + git_buf *buf, const char *string, const char *esc_chars, const char *esc_with); + +GIT_INLINE(int) git_buf_puts_escape_regex(git_buf *buf, const char *string) +{ + return git_buf_puts_escaped(buf, string, "^.[]$()|*+?{}\\", "\\"); +} + +/** * Join two strings as paths, inserting a slash between as needed. * @return 0 on success, -1 on failure */ @@ -121,6 +133,13 @@ GIT_INLINE(ssize_t) git_buf_rfind_next(git_buf *buf, char ch) return idx; } +GIT_INLINE(ssize_t) git_buf_rfind(git_buf *buf, char ch) +{ + ssize_t idx = (ssize_t)buf->size - 1; + while (idx >= 0 && buf->ptr[idx] != ch) idx--; + return idx; +} + /* Remove whitespace from the end of the buffer */ void git_buf_rtrim(git_buf *buf); diff --git a/src/commit.c b/src/commit.c index a3baf9d4e..32c47944b 100644 --- a/src/commit.c +++ b/src/commit.c @@ -229,19 +229,25 @@ GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset) GIT_COMMIT_GETTER(unsigned int, parentcount, commit->parent_oids.length) GIT_COMMIT_GETTER(const git_oid *, tree_oid, &commit->tree_oid); - int git_commit_tree(git_tree **tree_out, git_commit *commit) { assert(commit); return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_oid); } +const git_oid *git_commit_parent_oid(git_commit *commit, unsigned int n) +{ + assert(commit); + + return git_vector_get(&commit->parent_oids, n); +} + int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n) { - git_oid *parent_oid; + const git_oid *parent_oid; assert(commit); - parent_oid = git_vector_get(&commit->parent_oids, n); + parent_oid = git_commit_parent_oid(commit, n); if (parent_oid == NULL) { giterr_set(GITERR_INVALID, "Parent %u does not exist", n); return GIT_ENOTFOUND; @@ -250,9 +256,36 @@ int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n) return git_commit_lookup(parent, commit->object.repo, parent_oid); } -const git_oid *git_commit_parent_oid(git_commit *commit, unsigned int n) +int git_commit_nth_gen_ancestor( + git_commit **ancestor, + const git_commit *commit, + unsigned int n) { - assert(commit); + git_commit *current, *parent; + int error; - return git_vector_get(&commit->parent_oids, n); + assert(ancestor && commit); + + current = (git_commit *)commit; + + if (n == 0) + return git_commit_lookup( + ancestor, + commit->object.repo, + git_object_id((const git_object *)commit)); + + while (n--) { + error = git_commit_parent(&parent, (git_commit *)current, 0); + + if (current != commit) + git_commit_free(current); + + if (error < 0) + return error; + + current = parent; + } + + *ancestor = parent; + return 0; } diff --git a/src/config.c b/src/config.c index d18b85c30..98fb3b20d 100644 --- a/src/config.c +++ b/src/config.c @@ -136,17 +136,27 @@ int git_config_add_file(git_config *cfg, git_config_file *file, int priority) * Loop over all the variables */ -int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data) +int git_config_foreach( + git_config *cfg, int (*fn)(const char *, const char *, void *), void *data) +{ + return git_config_foreach_match(cfg, NULL, fn, data); +} + +int git_config_foreach_match( + git_config *cfg, + const char *regexp, + int (*fn)(const char *, const char *, void *), + void *data) { int ret = 0; unsigned int i; file_internal *internal; git_config_file *file; - for(i = 0; i < cfg->files.length && ret == 0; ++i) { + for (i = 0; i < cfg->files.length && ret == 0; ++i) { internal = git_vector_get(&cfg->files, i); file = internal->file; - ret = file->foreach(file, fn, data); + ret = file->foreach(file, regexp, fn, data); } return ret; diff --git a/src/config_file.c b/src/config_file.c index fd1aa8d08..1f3ebfca9 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -188,25 +188,46 @@ static void backend_free(git_config_file *_backend) git__free(backend); } -static int file_foreach(git_config_file *backend, int (*fn)(const char *, const char *, void *), void *data) +static int file_foreach( + git_config_file *backend, + const char *regexp, + int (*fn)(const char *, const char *, void *), + void *data) { diskfile_backend *b = (diskfile_backend *)backend; cvar_t *var; const char *key; + regex_t regex; + int result = 0; if (!b->values) return 0; + if (regexp != NULL) { + if ((result = regcomp(®ex, regexp, REG_EXTENDED)) < 0) { + giterr_set_regex(®ex, result); + regfree(®ex); + return -1; + } + } + git_strmap_foreach(b->values, key, var, - do { - if (fn(key, var->value, data) < 0) - break; + for (; var != NULL; var = CVAR_LIST_NEXT(var)) { + /* skip non-matching keys if regexp was provided */ + if (regexp && regexec(®ex, key, 0, NULL, 0) != 0) + continue; - var = CVAR_LIST_NEXT(var); - } while (var != NULL); + /* abort iterator on non-zero return value */ + if ((result = fn(key, var->value, data)) != 0) + goto cleanup; + } ); - return 0; +cleanup: + if (regexp != NULL) + regfree(®ex); + + return result; } static int config_set(git_config_file *cfg, const char *name, const char *value) @@ -337,6 +358,7 @@ static int config_get_multivar( result = regcomp(®ex, regex_str, REG_EXTENDED); if (result < 0) { giterr_set_regex(®ex, result); + regfree(®ex); return -1; } @@ -396,6 +418,7 @@ static int config_set_multivar( if (result < 0) { git__free(key); giterr_set_regex(&preg, result); + regfree(&preg); return -1; } diff --git a/src/config_file.h b/src/config_file.h index 0080b5713..c31292881 100644 --- a/src/config_file.h +++ b/src/config_file.h @@ -19,12 +19,27 @@ GIT_INLINE(void) git_config_file_free(git_config_file *cfg) cfg->free(cfg); } +GIT_INLINE(int) git_config_file_set_string( + git_config_file *cfg, const char *name, const char *value) +{ + return cfg->set(cfg, name, value); +} + GIT_INLINE(int) git_config_file_foreach( git_config_file *cfg, int (*fn)(const char *key, const char *value, void *data), void *data) { - return cfg->foreach(cfg, fn, data); + return cfg->foreach(cfg, NULL, fn, data); +} + +GIT_INLINE(int) git_config_file_foreach_match( + git_config_file *cfg, + const char *regexp, + int (*fn)(const char *key, const char *value, void *data), + void *data) +{ + return cfg->foreach(cfg, regexp, fn, data); } #endif diff --git a/src/diff_output.c b/src/diff_output.c index 92f7f8f2f..f6650b345 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -212,7 +212,7 @@ static void setup_xdiff_options( cfg->ctxlen = (!opts || !opts->context_lines) ? 3 : opts->context_lines; cfg->interhunkctxlen = - (!opts || !opts->interhunk_lines) ? 3 : opts->interhunk_lines; + (!opts) ? 0 : opts->interhunk_lines; if (!opts) return; diff --git a/src/fetch.c b/src/fetch.c index 96b263faa..603284842 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -118,7 +118,8 @@ int git_fetch__download_pack( int recvd; char buff[1024]; gitno_buffer buf; - git_indexer_stream *idx; + git_buf path = GIT_BUF_INIT; + git_indexer_stream *idx = NULL; gitno_buffer_setup(t, &buf, buff, sizeof(buff)); @@ -127,9 +128,12 @@ int git_fetch__download_pack( return -1; } - if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0) + if (git_buf_joinpath(&path, git_repository_path(repo), "objects/pack") < 0) return -1; + if (git_indexer_stream_new(&idx, git_buf_cstr(&path)) < 0) + goto on_error; + memset(stats, 0, sizeof(git_indexer_stats)); if (git_indexer_stream_add(idx, buffered, buffered_size, stats) < 0) goto on_error; @@ -154,6 +158,7 @@ int git_fetch__download_pack( return 0; on_error: + git_buf_free(&path); git_indexer_stream_free(idx); return -1; } diff --git a/src/fileops.c b/src/fileops.c index 95a65893c..5849b79b2 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -94,7 +94,7 @@ int git_futils_open_ro(const char *path) { int fd = p_open(path, O_RDONLY); if (fd < 0) { - if (errno == ENOENT) + if (errno == ENOENT || errno == ENOTDIR) fd = GIT_ENOTFOUND; giterr_set(GITERR_OS, "Failed to open '%s'", path); } diff --git a/src/index.c b/src/index.c index 3fedcd27a..89d479870 100644 --- a/src/index.c +++ b/src/index.c @@ -985,7 +985,7 @@ int git_index_entry_stage(const git_index_entry *entry) return (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT; } -static int read_tree_cb(const char *root, git_tree_entry *tentry, void *data) +static int read_tree_cb(const char *root, const git_tree_entry *tentry, void *data) { git_index *index = data; git_index_entry *entry = NULL; diff --git a/src/indexer.c b/src/indexer.c index 5ae66c9f1..797a58275 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -142,7 +142,7 @@ int git_indexer_stream_new(git_indexer_stream **out, const char *prefix) { git_indexer_stream *idx; git_buf path = GIT_BUF_INIT; - static const char suff[] = "/objects/pack/pack-received"; + static const char suff[] = "/pack"; int error; idx = git__calloc(1, sizeof(git_indexer_stream)); @@ -169,29 +169,14 @@ cleanup: } /* Try to store the delta so we can try to resolve it later */ -static int store_delta(git_indexer_stream *idx) +static int store_delta(git_indexer_stream *idx, git_off_t entry_start, size_t entry_size, git_otype type) { - git_otype type; git_mwindow *w = NULL; - git_mwindow_file *mwf = &idx->pack->mwf; - git_off_t entry_start = idx->off; struct delta_info *delta; - size_t entry_size; git_rawobj obj; int error; - /* - * ref-delta objects can refer to object that we haven't - * found yet, so give it another opportunity - */ - if (git_packfile_unpack_header(&entry_size, &type, mwf, &w, &idx->off) < 0) - return -1; - - git_mwindow_close(&w); - - /* If it's not a delta, mark it as failure, we can't do anything with it */ - if (type != GIT_OBJ_REF_DELTA && type != GIT_OBJ_OFS_DELTA) - return -1; + assert(type == GIT_OBJ_REF_DELTA || type == GIT_OBJ_OFS_DELTA); if (type == GIT_OBJ_REF_DELTA) { idx->off += GIT_OID_RAWSZ; @@ -313,8 +298,6 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz mwf = &idx->pack->mwf; if (git_mwindow_file_register(&idx->pack->mwf) < 0) return -1; - - return 0; } if (!idx->parsed_header) { @@ -352,27 +335,44 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz while (processed < idx->nr_objects) { git_rawobj obj; git_off_t entry_start = idx->off; + size_t entry_size; + git_otype type; + git_mwindow *w = NULL; if (idx->pack->mwf.size <= idx->off + 20) return 0; - error = git_packfile_unpack(&obj, idx->pack, &idx->off); + error = git_packfile_unpack_header(&entry_size, &type, mwf, &w, &idx->off); if (error == GIT_EBUFS) { idx->off = entry_start; return 0; } + if (error < 0) + return -1; - if (error < 0) { - idx->off = entry_start; - error = store_delta(idx); - if (error == GIT_EBUFS) + git_mwindow_close(&w); + + if (type == GIT_OBJ_REF_DELTA || type == GIT_OBJ_OFS_DELTA) { + error = store_delta(idx, entry_start, entry_size, type); + if (error == GIT_EBUFS) { + idx->off = entry_start; return 0; + } if (error < 0) return error; continue; } + idx->off = entry_start; + error = git_packfile_unpack(&obj, idx->pack, &idx->off); + if (error == GIT_EBUFS) { + idx->off = entry_start; + return 0; + } + if (error < 0) + return -1; + if (hash_and_save(idx, &obj, entry_start) < 0) goto on_error; @@ -775,6 +775,7 @@ int git_indexer_write(git_indexer *idx) cleanup: git_mwindow_free_all(&idx->pack->mwf); + git_mwindow_file_deregister(&idx->pack->mwf); if (error < 0) git_filebuf_cleanup(&idx->file); git_buf_free(&filename); @@ -888,6 +889,7 @@ void git_indexer_free(git_indexer *idx) return; p_close(idx->pack->mwf.fd); + git_mwindow_file_deregister(&idx->pack->mwf); git_vector_foreach(&idx->objects, i, e) git__free(e); git_vector_free(&idx->objects); diff --git a/src/khash.h b/src/khash.h index bd67fe1f7..242204464 100644 --- a/src/khash.h +++ b/src/khash.h @@ -131,7 +131,9 @@ typedef unsigned long long khint64_t; #endif #ifdef _MSC_VER -#define inline __inline +#define kh_inline __inline +#else +#define kh_inline inline #endif typedef khint32_t khint_t; @@ -345,7 +347,7 @@ static const double __ac_HASH_UPPER = 0.77; __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) #define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ - KHASH_INIT2(name, static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) /* --- BEGIN OF HASH FUNCTIONS --- */ @@ -374,7 +376,7 @@ static const double __ac_HASH_UPPER = 0.77; @param s Pointer to a null terminated string @return The hash value */ -static inline khint_t __ac_X31_hash_string(const char *s) +static kh_inline khint_t __ac_X31_hash_string(const char *s) { khint_t h = (khint_t)*s; if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s; @@ -391,7 +393,7 @@ static inline khint_t __ac_X31_hash_string(const char *s) */ #define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) -static inline khint_t __ac_Wang_hash(khint_t key) +static kh_inline khint_t __ac_Wang_hash(khint_t key) { key += ~(key << 15); key ^= (key >> 10); @@ -23,6 +23,10 @@ #define GIT_MAP_TYPE 0xf #define GIT_MAP_FIXED 0x10 +#ifdef __amigaos4__ +#define MAP_FAILED 0 +#endif + typedef struct { /* memory mapped buffer */ void *data; /* data bytes */ size_t len; /* data length */ diff --git a/src/mwindow.c b/src/mwindow.c index 57adabd48..1a5446b9c 100644 --- a/src/mwindow.c +++ b/src/mwindow.c @@ -158,6 +158,7 @@ static git_mwindow *new_window( git_mwindow *w; w = git__malloc(sizeof(*w)); + if (w == NULL) return NULL; @@ -260,6 +261,23 @@ int git_mwindow_file_register(git_mwindow_file *mwf) return git_vector_insert(&ctl->windowfiles, mwf); } +int git_mwindow_file_deregister(git_mwindow_file *mwf) +{ + git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl; + git_mwindow_file *cur; + unsigned int i; + + git_vector_foreach(&ctl->windowfiles, i, cur) { + if (cur == mwf) { + git_vector_remove(&ctl->windowfiles, i); + return 0; + } + } + + giterr_set(GITERR_ODB, "Failed to find the memory window file to deregister"); + return -1; +} + void git_mwindow_close(git_mwindow **window) { git_mwindow *w = *window; diff --git a/src/mwindow.h b/src/mwindow.h index 058027251..d4fd19569 100644 --- a/src/mwindow.h +++ b/src/mwindow.h @@ -40,6 +40,7 @@ void git_mwindow_free_all(git_mwindow_file *mwf); unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, git_off_t offset, size_t extra, unsigned int *left); void git_mwindow_scan_lru(git_mwindow_file *mwf, git_mwindow **lru_w, git_mwindow **lru_l); int git_mwindow_file_register(git_mwindow_file *mwf); +int git_mwindow_file_deregister(git_mwindow_file *mwf); void git_mwindow_close(git_mwindow **w_cursor); #endif diff --git a/src/netops.c b/src/netops.c index 32554743f..b369e5106 100644 --- a/src/netops.c +++ b/src/netops.c @@ -381,16 +381,18 @@ int gitno_connect(git_transport *t, const char *host, const char *port) GIT_SOCKET s = INVALID_SOCKET; memset(&hints, 0x0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; - if ((ret = getaddrinfo(host, port, &hints, &info)) < 0) { - giterr_set(GITERR_NET, "Failed to resolve address for %s: %s", host, gai_strerror(ret)); + if ((ret = p_getaddrinfo(host, port, &hints, &info)) < 0) { + giterr_set(GITERR_NET, + "Failed to resolve address for %s: %s", host, p_gai_strerror(ret)); return -1; } for (p = info; p != NULL; p = p->ai_next) { s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (s == INVALID_SOCKET) { net_set_error("error creating socket"); break; @@ -411,7 +413,7 @@ int gitno_connect(git_transport *t, const char *host, const char *port) } t->socket = s; - freeaddrinfo(info); + p_freeaddrinfo(info); if (t->encrypt && ssl_setup(t, host) < 0) return -1; diff --git a/src/notes.c b/src/notes.c index efbdbabeb..7813e9985 100644 --- a/src/notes.c +++ b/src/notes.c @@ -56,7 +56,7 @@ static int find_subtree_r(git_tree **out, git_tree *root, error = find_subtree_in_current_level(&subtree, repo, root, target, *fanout); if (error == GIT_EEXISTS) { - return git_tree_lookup(out, repo, git_object_id((const git_object *)root)); + return git_tree_lookup(out, repo, git_tree_id(root)); } if (error < 0) @@ -64,13 +64,7 @@ static int find_subtree_r(git_tree **out, git_tree *root, *fanout += 2; error = find_subtree_r(out, subtree, repo, target, fanout); - - /* - * root is not ours to free, and the last subtree is the - * one being returned => we only need to free the subtrees in-between - */ - if (*out != subtree) - git_tree_free(subtree); + git_tree_free(subtree); return error; } @@ -103,7 +97,7 @@ static int tree_write( { int error; git_treebuilder *tb = NULL; - git_tree_entry *entry; + const git_tree_entry *entry; git_oid tree_oid; if ((error = git_treebuilder_create(&tb, source_tree)) < 0) @@ -153,7 +147,7 @@ static int manipulate_note_in_tree_r( int current_error)) { int error = -1; - git_tree *subtree = NULL; + git_tree *subtree = NULL, *new = NULL; char subtree_name[3]; error = find_subtree_in_current_level( @@ -176,7 +170,7 @@ static int manipulate_note_in_tree_r( /* An existing fanout has been found, let's dig deeper */ error = manipulate_note_in_tree_r( - out, repo, subtree, note_oid, annotated_object_sha, + &new, repo, subtree, note_oid, annotated_object_sha, fanout + 2, note_exists_cb, note_notfound_cb); if (error < 0) @@ -185,10 +179,12 @@ static int manipulate_note_in_tree_r( strncpy(subtree_name, annotated_object_sha + fanout, 2); subtree_name[2] = '\0'; - error = tree_write(out, repo, parent, - git_object_id((const git_object *)(*out)), subtree_name, 0040000); + error = tree_write(out, repo, parent, git_tree_id(new), + subtree_name, 0040000); + cleanup: + git_tree_free(new); git_tree_free(subtree); return error; } diff --git a/src/object.c b/src/object.c index d3673eda0..14d64befe 100644 --- a/src/object.c +++ b/src/object.c @@ -156,8 +156,10 @@ int git_object_lookup_prefix( type = odb_obj->raw.type; - if (create_object(&object, type) < 0) + if (create_object(&object, type) < 0) { + git_odb_object_free(odb_obj); return -1; + } /* Initialize parent object */ git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid); @@ -605,6 +605,18 @@ int git_odb_read_prefix( return 0; } +int git_odb_foreach(git_odb *db, int (*cb)(git_oid *oid, void *data), void *data) +{ + unsigned int i; + backend_internal *internal; + git_vector_foreach(&db->backends, i, internal) { + git_odb_backend *b = internal->backend; + b->foreach(b, cb, data); + } + + return 0; +} + int git_odb_write( git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type) { diff --git a/src/odb_loose.c b/src/odb_loose.c index 989b03ab2..2197a4264 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -676,6 +676,89 @@ static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid) return !error; } +struct foreach_state { + size_t dir_len; + int (*cb)(git_oid *oid, void *data); + void *data; +}; + +GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr) +{ + int v, i = 0; + if (strlen(ptr) != 41) + return -1; + + if (ptr[2] != '/') { + return -1; + } + + v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i+1]); + if (v < 0) + return -1; + + oid->id[0] = (unsigned char) v; + + ptr += 3; + for (i = 0; i < 38; i += 2) { + v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i + 1]); + if (v < 0) + return -1; + + oid->id[1 + i/2] = (unsigned char) v; + } + + return 0; +} + +static int foreach_object_dir_cb(void *_state, git_buf *path) +{ + git_oid oid; + struct foreach_state *state = (struct foreach_state *) _state; + + if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0) + return 0; + + if (state->cb(&oid, state->data) < 0) + return -1; + + return 0; +} + +static int foreach_cb(void *_state, git_buf *path) +{ + struct foreach_state *state = (struct foreach_state *) _state; + + if (git_path_direach(path, foreach_object_dir_cb, state) < 0) + return -1; + + return 0; +} + +static int loose_backend__foreach(git_odb_backend *_backend, int (*cb)(git_oid *oid, void *data), void *data) +{ + char *objects_dir; + int error; + git_buf buf = GIT_BUF_INIT; + struct foreach_state state; + loose_backend *backend = (loose_backend *) _backend; + + assert(backend && cb); + + objects_dir = backend->objects_dir; + + git_buf_sets(&buf, objects_dir); + git_path_to_dir(&buf); + + state.cb = cb; + state.data = data; + state.dir_len = git_buf_len(&buf); + + error = git_path_direach(&buf, foreach_cb, &state); + git_buf_free(&buf); + + return error; +} + static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream) { loose_writestream *stream = (loose_writestream *)_stream; @@ -845,6 +928,7 @@ int git_odb_backend_loose( backend->parent.read_header = &loose_backend__read_header; backend->parent.writestream = &loose_backend__stream; backend->parent.exists = &loose_backend__exists; + backend->parent.foreach = &loose_backend__foreach; backend->parent.free = &loose_backend__free; *backend_out = (git_odb_backend *)backend; diff --git a/src/odb_pack.c b/src/odb_pack.c index 458f288d9..4b860e864 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -420,6 +420,25 @@ static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid) return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0; } +static int pack_backend__foreach(git_odb_backend *_backend, int (*cb)(git_oid *oid, void *data), void *data) +{ + struct git_pack_file *p; + struct pack_backend *backend; + unsigned int i; + + assert(_backend && cb); + backend = (struct pack_backend *)_backend; + + /* Make sure we know about the packfiles */ + if (packfile_refresh_all(backend) < 0) + return -1; + + git_vector_foreach(&backend->packs, i, p) { + git_pack_foreach_entry(p, cb, &data); + } + return 0; +} + static void pack_backend__free(git_odb_backend *_backend) { struct pack_backend *backend; @@ -463,6 +482,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) backend->parent.read_prefix = &pack_backend__read_prefix; backend->parent.read_header = NULL; backend->parent.exists = &pack_backend__exists; + backend->parent.foreach = &pack_backend__foreach; backend->parent.free = &pack_backend__free; *backend_out = (git_odb_backend *)backend; diff --git a/src/oidmap.h b/src/oidmap.h index 858268c92..5a0bab6ec 100644 --- a/src/oidmap.h +++ b/src/oidmap.h @@ -34,7 +34,7 @@ GIT_INLINE(int) hash_git_oid_equal(const git_oid *a, const git_oid *b) } #define GIT__USE_OIDMAP \ - __KHASH_IMPL(oid, static inline, const git_oid *, void *, 1, hash_git_oid, hash_git_oid_equal) + __KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, hash_git_oid, hash_git_oid_equal) #define git_oidmap_alloc() kh_init(oid) #define git_oidmap_free(h) kh_destroy(oid,h), h = NULL diff --git a/src/pack.c b/src/pack.c index 0db1069de..1d88eaa7d 100644 --- a/src/pack.c +++ b/src/pack.c @@ -262,7 +262,7 @@ int git_packfile_unpack_header( if (base == NULL) return GIT_EBUFS; - ret = packfile_unpack_header1(&used, size_p, type_p, base, left); + ret = packfile_unpack_header1(&used, size_p, type_p, base, left); git_mwindow_close(w_curs); if (ret == GIT_EBUFS) return ret; @@ -535,6 +535,7 @@ void packfile_free(struct git_pack_file *p) /* clear_delta_base_cache(); */ git_mwindow_free_all(&p->mwf); + git_mwindow_file_deregister(&p->mwf); if (p->mwf.fd != -1) p_close(p->mwf.fd); @@ -685,6 +686,49 @@ static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_ } } +int git_pack_foreach_entry( + struct git_pack_file *p, + int (*cb)(git_oid *oid, void *data), + void *data) + +{ + const unsigned char *index = p->index_map.data, *current; + unsigned stride; + uint32_t i; + + if (index == NULL) { + int error; + + if ((error = pack_index_open(p)) < 0) + return error; + + assert(p->index_map.data); + + index = p->index_map.data; + } + + if (p->index_version > 1) { + index += 8; + } + + index += 4 * 256; + + if (p->index_version > 1) { + stride = 20; + } else { + stride = 24; + index += 4; + } + + current = index; + for (i = 0; i < p->num_objects; i++) { + cb((git_oid *)current, data); + current += stride; + } + + return 0; +} + static int pack_entry_find_offset( git_off_t *offset_out, git_oid *found_oid, diff --git a/src/pack.h b/src/pack.h index cd7a4d2e1..7e1f978b0 100644 --- a/src/pack.h +++ b/src/pack.h @@ -102,5 +102,9 @@ int git_pack_entry_find( struct git_pack_file *p, const git_oid *short_oid, unsigned int len); +int git_pack_foreach_entry( + struct git_pack_file *p, + int (*cb)(git_oid *oid, void *data), + void *data); #endif diff --git a/src/path.c b/src/path.c index ee7e07e45..22391c52b 100644 --- a/src/path.c +++ b/src/path.c @@ -17,9 +17,7 @@ #include <stdio.h> #include <ctype.h> -#ifdef GIT_WIN32 #define LOOKS_LIKE_DRIVE_PREFIX(S) (git__isalpha((S)[0]) && (S)[1] == ':') -#endif /* * Based on the Android implementation, BSD licensed. @@ -172,11 +170,11 @@ int git_path_root(const char *path) { int offset = 0; -#ifdef GIT_WIN32 /* Does the root of the path look like a windows drive ? */ if (LOOKS_LIKE_DRIVE_PREFIX(path)) offset += 2; +#ifdef GIT_WIN32 /* Are we dealing with a windows network path? */ else if ((path[0] == '/' && path[1] == '/') || (path[0] == '\\' && path[1] == '\\')) @@ -527,6 +525,71 @@ int git_path_find_dir(git_buf *dir, const char *path, const char *base) return error; } +int git_path_resolve_relative(git_buf *path, size_t ceiling) +{ + char *base, *to, *from, *next; + size_t len; + + if (!path || git_buf_oom(path)) + return -1; + + if (ceiling > path->size) + ceiling = path->size; + + /* recognize drive prefixes, etc. that should not be backed over */ + if (ceiling == 0) + ceiling = git_path_root(path->ptr) + 1; + + /* recognize URL prefixes that should not be backed over */ + if (ceiling == 0) { + for (next = path->ptr; *next && git__isalpha(*next); ++next); + if (next[0] == ':' && next[1] == '/' && next[2] == '/') + ceiling = (next + 3) - path->ptr; + } + + base = to = from = path->ptr + ceiling; + + while (*from) { + for (next = from; *next && *next != '/'; ++next); + + len = next - from; + + if (len == 1 && from[0] == '.') + /* do nothing with singleton dot */; + + else if (len == 2 && from[0] == '.' && from[1] == '.') { + while (to > base && to[-1] == '/') to--; + while (to > base && to[-1] != '/') to--; + } + + else { + if (*next == '/') + len++; + + if (to != from) + memmove(to, from, len); + + to += len; + } + + from += len; + + while (*from == '/') from++; + } + + *to = '\0'; + + path->size = to - path->ptr; + + return 0; +} + +int git_path_apply_relative(git_buf *target, const char *relpath) +{ + git_buf_joinpath(target, git_buf_cstr(target), relpath); + return git_path_resolve_relative(target, 0); +} + int git_path_cmp( const char *name1, size_t len1, int isdir1, const char *name2, size_t len2, int isdir2) @@ -570,7 +633,7 @@ int git_path_direach( return -1; } -#ifdef __sun +#if defined(__sun) || defined(__GNU__) de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1); #else de_buf = git__malloc(sizeof(struct dirent)); @@ -624,7 +687,7 @@ int git_path_dirload( return -1; } -#ifdef __sun +#if defined(__sun) || defined(__GNU__) de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1); #else de_buf = git__malloc(sizeof(struct dirent)); diff --git a/src/path.h b/src/path.h index a845b3a14..5eecb7261 100644 --- a/src/path.h +++ b/src/path.h @@ -208,6 +208,29 @@ extern int git_path_prettify_dir(git_buf *path_out, const char *path, const char extern int git_path_find_dir(git_buf *dir, const char *path, const char *base); /** + * Resolve relative references within a path. + * + * This eliminates "./" and "../" relative references inside a path, + * as well as condensing multiple slashes into single ones. It will + * not touch the path before the "ceiling" length. + * + * Additionally, this will recognize an "c:/" drive prefix or a "xyz://" URL + * prefix and not touch that part of the path. + */ +extern int git_path_resolve_relative(git_buf *path, size_t ceiling); + +/** + * Apply a relative path to base path. + * + * Note that the base path could be a filename or a URL and this + * should still work. The relative path is walked segment by segment + * with three rules: series of slashes will be condensed to a single + * slash, "." will be eaten with no change, and ".." will remove a + * segment from the base path. + */ +extern int git_path_apply_relative(git_buf *target, const char *relpath); + +/** * Walk each directory entry, except '.' and '..', calling fn(state). * * @param pathbuf buffer the function reads the initial directory @@ -296,7 +296,7 @@ static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps git_buf_grow(buf, git_buf_len(buf) + len); git_oid_fmt(oid, &head->oid); - return git_buf_printf(buf, "%04xwant %s%c%s\n", len, oid, 0, capstr); + return git_buf_printf(buf, "%04xwant %s %s\n", len, oid, capstr); } /* diff --git a/src/pool.c b/src/pool.c index 641292d06..63bf09cee 100644 --- a/src/pool.c +++ b/src/pool.c @@ -275,6 +275,8 @@ uint32_t git_pool__system_page_size(void) SYSTEM_INFO info; GetSystemInfo(&info); size = (uint32_t)info.dwPageSize; +#elif defined(__amigaos4__) + size = (uint32_t)4096; /* 4K as there is no global value we can query */ #else size = (uint32_t)sysconf(_SC_PAGE_SIZE); #endif diff --git a/src/posix.c b/src/posix.c index a9a6af984..985221dd5 100644 --- a/src/posix.c +++ b/src/posix.c @@ -12,6 +12,97 @@ #ifndef GIT_WIN32 +#ifdef NO_ADDRINFO +int p_getaddrinfo( + const char *host, + const char *port, + struct addrinfo *hints, + struct addrinfo **info) +{ + GIT_UNUSED(hints); + + struct addrinfo *ainfo, *ai; + int p = 0; + + if ((ainfo = malloc(sizeof(struct addrinfo))) == NULL) + return -1; + + if ((ainfo->ai_hostent = gethostbyname(host)) == NULL) + return -2; + + ainfo->ai_servent = getservbyname(port, 0); + + if (ainfo->ai_servent) + ainfo->ai_port = ainfo->ai_servent->s_port; + else + ainfo->ai_port = atol(port); + + memcpy(&ainfo->ai_addr_in.sin_addr, + ainfo->ai_hostent->h_addr_list[0], + ainfo->ai_hostent->h_length); + + ainfo->ai_protocol = 0; + ainfo->ai_socktype = hints->ai_socktype; + ainfo->ai_family = ainfo->ai_hostent->h_addrtype; + ainfo->ai_addr_in.sin_family = ainfo->ai_family; + ainfo->ai_addr_in.sin_port = ainfo->ai_port; + ainfo->ai_addr = (struct addrinfo *)&ainfo->ai_addr_in; + ainfo->ai_addrlen = sizeof(struct sockaddr_in); + + *info = ainfo; + + if (ainfo->ai_hostent->h_addr_list[1] == NULL) { + ainfo->ai_next = NULL; + return 0; + } + + ai = ainfo; + + for (p = 1; ainfo->ai_hostent->h_addr_list[p] != NULL; p++) { + ai->ai_next = malloc(sizeof(struct addrinfo)); + memcpy(&ai->ai_next, ainfo, sizeof(struct addrinfo)); + memcpy(&ai->ai_next->ai_addr_in.sin_addr, + ainfo->ai_hostent->h_addr_list[p], + ainfo->ai_hostent->h_length); + ai->ai_next->ai_addr = (struct addrinfo *)&ai->ai_next->ai_addr_in; + ai = ai->ai_next; + } + + ai->ai_next = NULL; + return 0; +} + +void p_freeaddrinfo(struct addrinfo *info) +{ + struct addrinfo *p, *next; + + p = info; + + while(p != NULL) { + next = p->ai_next; + free(p); + p = next; + } +} + +const char *p_gai_strerror(int ret) +{ + switch(ret) { + case -1: + return "Out of memory"; + break; + + case -2: + return "Address lookup failed"; + break; + + default: + return "Unknown error"; + break; + } +} +#endif /* NO_ADDRINFO */ + int p_open(const char *path, int flags, ...) { mode_t mode = 0; @@ -63,7 +154,7 @@ int p_rename(const char *from, const char *to) return -1; } -#endif +#endif /* GIT_WIN32 */ int p_read(git_file fd, void *buf, size_t cnt) { diff --git a/src/posix.h b/src/posix.h index 5799c0499..d35fe08a5 100644 --- a/src/posix.h +++ b/src/posix.h @@ -83,6 +83,41 @@ extern int p_gettimeofday(struct timeval *tv, struct timezone *tz); # include "unix/posix.h" #endif -#define p_readdir_r(d,e,r) readdir_r(d,e,r) +#ifdef NO_READDIR_R +# include <dirent.h> +GIT_INLINE(int) p_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) +{ + GIT_UNUSED(entry); + *result = readdir(dirp); + return 0; +} +#else /* NO_READDIR_R */ +# define p_readdir_r(d,e,r) readdir_r(d,e,r) +#endif + +#ifdef NO_ADDRINFO +# include <netdb.h> +struct addrinfo { + struct hostent *ai_hostent; + struct servent *ai_servent; + struct sockaddr_in ai_addr_in; + struct sockaddr *ai_addr; + size_t ai_addrlen; + int ai_family; + int ai_socktype; + int ai_protocol; + long ai_port; + struct addrinfo *ai_next; +}; + +extern int p_getaddrinfo(const char *host, const char *port, + struct addrinfo *hints, struct addrinfo **info); +extern void p_freeaddrinfo(struct addrinfo *info); +extern const char *p_gai_strerror(int ret); +#else +# define p_getaddrinfo(a, b, c, d) getaddrinfo(a, b, c, d) +# define p_freeaddrinfo(a) freeaddrinfo(a) +# define p_gai_strerror(c) gai_strerror(c) +#endif /* NO_ADDRINFO */ #endif diff --git a/src/reflog.c b/src/reflog.c index 3ea073e65..004ba936d 100644 --- a/src/reflog.c +++ b/src/reflog.c @@ -269,18 +269,50 @@ cleanup: int git_reflog_rename(git_reference *ref, const char *new_name) { - int error; + int error = -1, fd; git_buf old_path = GIT_BUF_INIT; git_buf new_path = GIT_BUF_INIT; + git_buf temp_path = GIT_BUF_INIT; - if (!git_buf_join_n(&old_path, '/', 3, ref->owner->path_repository, - GIT_REFLOG_DIR, ref->name) && - !git_buf_join_n(&new_path, '/', 3, ref->owner->path_repository, - GIT_REFLOG_DIR, new_name)) - error = p_rename(git_buf_cstr(&old_path), git_buf_cstr(&new_path)); - else - error = -1; + assert(ref && new_name); + + if (git_buf_joinpath(&temp_path, ref->owner->path_repository, GIT_REFLOG_DIR) < 0) + return -1; + + if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), ref->name) < 0) + goto cleanup; + + if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), new_name) < 0) + goto cleanup; + + /* + * Move the reflog to a temporary place. This two-phase renaming is required + * in order to cope with funny renaming use cases when one tries to move a reference + * to a partially colliding namespace: + * - a/b -> a/b/c + * - a/b/c/d -> a/b/c + */ + if (git_buf_joinpath(&temp_path, git_buf_cstr(&temp_path), "temp_reflog") < 0) + goto cleanup; + if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path))) < 0) + goto cleanup; + p_close(fd); + + if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) + goto cleanup; + + if (git_path_isdir(git_buf_cstr(&new_path)) && + (git_futils_rmdir_r(git_buf_cstr(&new_path), GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0)) + goto cleanup; + + if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) + goto cleanup; + + error = p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)); + +cleanup: + git_buf_free(&temp_path); git_buf_free(&old_path); git_buf_free(&new_path); diff --git a/src/refs.c b/src/refs.c index 104685793..b3c140bec 100644 --- a/src/refs.c +++ b/src/refs.c @@ -11,6 +11,7 @@ #include "fileops.h" #include "pack.h" #include "reflog.h" +#include "config.h" #include <git2/tag.h> #include <git2/object.h> @@ -1396,6 +1397,9 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force) head_target = git_reference_target(head); if (head_target && !strcmp(head_target, ref->name)) { + git_reference_free(head); + head = NULL; + if (git_reference_create_symbolic(&head, ref->owner, "HEAD", new_name, 1) < 0) { giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference"); @@ -1404,18 +1408,11 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force) } /* - * Rename the reflog file. + * Rename the reflog file, if it exists. */ - if (git_buf_join_n(&aux_path, '/', 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name) < 0) + if ((git_reference_has_log(ref)) && (git_reflog_rename(ref, new_name) < 0)) goto cleanup; - if (git_path_exists(aux_path.ptr) == true) { - if (git_reflog_rename(ref, new_name) < 0) - goto cleanup; - } else { - giterr_clear(); - } - /* * Change the name of the reference given by the user. */ @@ -1764,3 +1761,130 @@ int git_reference__update(git_repository *repo, const git_oid *oid, const char * git_reference_free(ref); return res; } + +struct glob_cb_data { + const char *glob; + int (*callback)(const char *, void *); + void *payload; +}; + +static int fromglob_cb(const char *reference_name, void *payload) +{ + struct glob_cb_data *data = (struct glob_cb_data *)payload; + + if (!p_fnmatch(data->glob, reference_name, 0)) + return data->callback(reference_name, data->payload); + + return 0; +} + +int git_reference_foreach_glob( + git_repository *repo, + const char *glob, + unsigned int list_flags, + int (*callback)( + const char *reference_name, + void *payload), + void *payload) +{ + struct glob_cb_data data; + + assert(repo && glob && callback); + + data.glob = glob; + data.callback = callback; + data.payload = payload; + + return git_reference_foreach( + repo, list_flags, fromglob_cb, &data); +} + +int git_reference_has_log( + git_reference *ref) +{ + git_buf path = GIT_BUF_INIT; + int result; + + assert(ref); + + if (git_buf_join_n(&path, '/', 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name) < 0) + return -1; + + result = git_path_isfile(git_buf_cstr(&path)); + git_buf_free(&path); + + return result; +} + +//TODO: How about also taking care of local tracking branches? +//cf. http://alblue.bandlem.com/2011/07/git-tip-of-week-tracking-branches.html +int git_reference_remote_tracking_from_branch( + git_reference **tracking_ref, + git_reference *branch_ref) +{ + git_config *config = NULL; + const char *name, *remote, *merge; + git_buf buf = GIT_BUF_INIT; + int error = -1; + + assert(tracking_ref && branch_ref); + + name = git_reference_name(branch_ref); + + if (git__prefixcmp(name, GIT_REFS_HEADS_DIR)) { + giterr_set( + GITERR_INVALID, + "Failed to retrieve tracking reference - '%s' is not a branch.", + name); + return -1; + } + + if (git_repository_config(&config, branch_ref->owner) < 0) + return -1; + + if (git_buf_printf( + &buf, + "branch.%s.remote", + name + strlen(GIT_REFS_HEADS_DIR)) < 0) + goto cleanup; + + if ((error = git_config_get_string(&remote, config, git_buf_cstr(&buf))) < 0) + goto cleanup; + + error = -1; + + git_buf_clear(&buf); + + //TODO: Is it ok to fail when no merge target is found? + if (git_buf_printf( + &buf, + "branch.%s.merge", + name + strlen(GIT_REFS_HEADS_DIR)) < 0) + goto cleanup; + + if (git_config_get_string(&merge, config, git_buf_cstr(&buf)) < 0) + goto cleanup; + + //TODO: Should we test this? + if (git__prefixcmp(merge, GIT_REFS_HEADS_DIR)) + goto cleanup; + + git_buf_clear(&buf); + + if (git_buf_printf( + &buf, + "refs/remotes/%s/%s", + remote, + merge + strlen(GIT_REFS_HEADS_DIR)) < 0) + goto cleanup; + + error = git_reference_lookup( + tracking_ref, + branch_ref->owner, + git_buf_cstr(&buf)); + +cleanup: + git_config_free(config); + git_buf_free(&buf); + return error; +} diff --git a/src/repository.c b/src/repository.c index 23a95b23e..a2931713e 100644 --- a/src/repository.c +++ b/src/repository.c @@ -602,14 +602,10 @@ void git_repository_set_index(git_repository *repo, git_index *index) GIT_REFCOUNT_INC(index); } -static int check_repositoryformatversion(git_repository *repo) +static int check_repositoryformatversion(git_config *config) { - git_config *config; int version; - if (git_repository_config__weakptr(&config, repo) < 0) - return -1; - if (git_config_get_int32(&version, config, "core.repositoryformatversion") < 0) return -1; @@ -623,26 +619,6 @@ static int check_repositoryformatversion(git_repository *repo) return 0; } -static int repo_init_reinit(git_repository **repo_out, const char *repository_path, int is_bare) -{ - git_repository *repo = NULL; - - GIT_UNUSED(is_bare); - - if (git_repository_open(&repo, repository_path) < 0) - return -1; - - if (check_repositoryformatversion(repo) < 0) { - git_repository_free(repo); - return -1; - } - - /* TODO: reinitialize the templates */ - - *repo_out = repo; - return 0; -} - static int repo_init_createhead(const char *git_dir) { git_buf ref_path = GIT_BUF_INIT; @@ -717,6 +693,12 @@ static int repo_init_config(const char *git_dir, bool is_bare, bool is_reinit) return -1; } + if (is_reinit && check_repositoryformatversion(config) < 0) { + git_buf_free(&cfg_path); + git_config_free(config); + return -1; + } + SET_REPO_CONFIG(bool, "core.bare", is_bare); SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION); SET_REPO_CONFIG(bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path))); @@ -759,15 +741,24 @@ static int repo_init_config(const char *git_dir, bool is_bare, bool is_reinit) #define GIT_DESC_CONTENT "Unnamed repository; edit this file 'description' to name the repository.\n" static int repo_write_template( - const char *git_dir, const char *file, mode_t mode, const char *content) + const char *git_dir, + bool allow_overwrite, + const char *file, + mode_t mode, + const char *content) { git_buf path = GIT_BUF_INIT; - int fd, error = 0; + int fd, error = 0, flags; if (git_buf_joinpath(&path, git_dir, file) < 0) return -1; - fd = p_open(git_buf_cstr(&path), O_WRONLY | O_CREAT | O_EXCL, mode); + if (allow_overwrite) + flags = O_WRONLY | O_CREAT | O_TRUNC; + else + flags = O_WRONLY | O_CREAT | O_EXCL; + + fd = p_open(git_buf_cstr(&path), flags, mode); if (fd >= 0) { error = p_write(fd, content, strlen(content)); @@ -829,7 +820,7 @@ static int repo_init_structure(const char *git_dir, int is_bare) /* Make template files as needed */ for (i = 0; tmpl[i].file != NULL; ++i) { if (repo_write_template( - git_dir, tmpl[i].file, tmpl[i].mode, tmpl[i].content) < 0) + git_dir, false, tmpl[i].file, tmpl[i].mode, tmpl[i].content) < 0) return -1; } @@ -850,21 +841,18 @@ int git_repository_init(git_repository **repo_out, const char *path, unsigned is is_reinit = git_path_isdir(repository_path.ptr) && valid_repository_path(&repository_path); if (is_reinit) { - if (repo_init_reinit(repo_out, repository_path.ptr, is_bare) < 0) - goto cleanup; + /* TODO: reinitialize the templates */ - result = repo_init_config(repository_path.ptr, is_bare, is_reinit); - goto cleanup; - } + if (repo_init_config(repository_path.ptr, is_bare, is_reinit) < 0) + goto cleanup; - if (repo_init_structure(repository_path.ptr, is_bare) < 0 || + } else if (repo_init_structure(repository_path.ptr, is_bare) < 0 || repo_init_config(repository_path.ptr, is_bare, is_reinit) < 0 || - repo_init_createhead(repository_path.ptr) < 0 || - git_repository_open(repo_out, repository_path.ptr) < 0) { + repo_init_createhead(repository_path.ptr) < 0) { goto cleanup; } - result = 0; + result = git_repository_open(repo_out, repository_path.ptr); cleanup: git_buf_free(&repository_path); @@ -964,8 +952,47 @@ const char *git_repository_workdir(git_repository *repo) return repo->workdir; } -int git_repository_set_workdir(git_repository *repo, const char *workdir) +static int write_gitlink( + const char *in_dir, const char *to_repo) { + int error; + git_buf buf = GIT_BUF_INIT; + struct stat st; + + if (git_path_dirname_r(&buf, to_repo) < 0 || + git_path_to_dir(&buf) < 0) + return -1; + + /* don't write gitlink to natural workdir */ + if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 && + strcmp(in_dir, buf.ptr) == 0) + return GIT_PASSTHROUGH; + + if (git_buf_joinpath(&buf, in_dir, DOT_GIT) < 0) + return -1; + + if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) { + giterr_set(GITERR_REPOSITORY, + "Cannot overwrite gitlink file into path '%s'", in_dir); + return GIT_EEXISTS; + } + + git_buf_clear(&buf); + + if (git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo) < 0) + return -1; + + error = repo_write_template(in_dir, true, DOT_GIT, 0644, buf.ptr); + + git_buf_free(&buf); + + return error; +} + +int git_repository_set_workdir( + git_repository *repo, const char *workdir, int update_gitlink) +{ + int error = 0; git_buf path = GIT_BUF_INIT; assert(repo && workdir); @@ -973,11 +1000,37 @@ int git_repository_set_workdir(git_repository *repo, const char *workdir) if (git_path_prettify_dir(&path, workdir, NULL) < 0) return -1; - git__free(repo->workdir); + if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0) + return 0; - repo->workdir = git_buf_detach(&path); - repo->is_bare = 0; - return 0; + if (update_gitlink) { + git_config *config; + + if (git_repository_config__weakptr(&config, repo) < 0) + return -1; + + error = write_gitlink(path.ptr, git_repository_path(repo)); + + /* passthrough error means gitlink is unnecessary */ + if (error == GIT_PASSTHROUGH) + error = git_config_delete(config, "core.worktree"); + else if (!error) + error = git_config_set_string(config, "core.worktree", path.ptr); + + if (!error) + error = git_config_set_bool(config, "core.bare", false); + } + + if (!error) { + char *old_workdir = repo->workdir; + + repo->workdir = git_buf_detach(&path); + repo->is_bare = 0; + + git__free(old_workdir); + } + + return error; } int git_repository_is_bare(git_repository *repo) diff --git a/src/revparse.c b/src/revparse.c index 3f210d11b..777dee685 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -14,747 +14,717 @@ #include "git2.h" typedef enum { - REVPARSE_STATE_INIT, - REVPARSE_STATE_CARET, - REVPARSE_STATE_LINEAR, - REVPARSE_STATE_COLON, - REVPARSE_STATE_DONE, + REVPARSE_STATE_INIT, + REVPARSE_STATE_CARET, + REVPARSE_STATE_LINEAR, + REVPARSE_STATE_COLON, + REVPARSE_STATE_DONE, } revparse_state; -static void set_invalid_syntax_err(const char *spec) +static int revspec_error(const char *revspec) { - giterr_set(GITERR_INVALID, "Refspec '%s' is not valid.", spec); + giterr_set(GITERR_INVALID, "Failed to parse revision specifier - Invalid pattern '%s'", revspec); + return -1; } static int revparse_lookup_fully_qualifed_ref(git_object **out, git_repository *repo, const char*spec) { - git_oid resolved; + git_oid resolved; + int error; - if (git_reference_name_to_oid(&resolved, repo, spec) < 0) - return GIT_ERROR; + if ((error = git_reference_name_to_oid(&resolved, repo, spec)) < 0) + return error; - return git_object_lookup(out, repo, &resolved, GIT_OBJ_ANY); + return git_object_lookup(out, repo, &resolved, GIT_OBJ_ANY); } /* Returns non-zero if yes */ static int spec_looks_like_describe_output(const char *spec) { - regex_t regex; - int regex_error, retcode; - - regex_error = regcomp(®ex, ".+-[0-9]+-g[0-9a-fA-F]+", REG_EXTENDED); - if (regex_error != 0) { - giterr_set_regex(®ex, regex_error); - return 1; /* To be safe */ - } - retcode = regexec(®ex, spec, 0, NULL, 0); - regfree(®ex); - return retcode == 0; + regex_t regex; + int regex_error, retcode; + + regex_error = regcomp(®ex, ".+-[0-9]+-g[0-9a-fA-F]+", REG_EXTENDED); + if (regex_error != 0) { + giterr_set_regex(®ex, regex_error); + return regex_error; + } + + retcode = regexec(®ex, spec, 0, NULL, 0); + regfree(®ex); + return retcode == 0; } -static int revparse_lookup_object(git_object **out, git_repository *repo, const char *spec) +static int disambiguate_refname(git_reference **out, git_repository *repo, const char *refname) { - size_t speclen = strlen(spec); - git_object *obj = NULL; - git_oid oid; - git_buf refnamebuf = GIT_BUF_INIT; - static const char* formatters[] = { - "refs/%s", - "refs/tags/%s", - "refs/heads/%s", - "refs/remotes/%s", - "refs/remotes/%s/HEAD", - NULL - }; - unsigned int i; - const char *substr; - - /* "git describe" output; snip everything before/including "-g" */ - substr = strstr(spec, "-g"); - if (substr && - spec_looks_like_describe_output(spec) && - !revparse_lookup_object(out, repo, substr+2)) { - return 0; - } - - /* SHA or prefix */ - if (!git_oid_fromstrn(&oid, spec, speclen)) { - if (!git_object_lookup_prefix(&obj, repo, &oid, speclen, GIT_OBJ_ANY)) { - *out = obj; - return 0; - } - } - - /* Fully-named ref */ - if (!revparse_lookup_fully_qualifed_ref(&obj, repo, spec)) { - *out = obj; - return 0; - } - - /* Partially-named ref; match in this order: */ - for (i=0; formatters[i]; i++) { - git_buf_clear(&refnamebuf); - if (git_buf_printf(&refnamebuf, formatters[i], spec) < 0) { - return GIT_ERROR; - } - - if (!revparse_lookup_fully_qualifed_ref(&obj, repo, git_buf_cstr(&refnamebuf))) { - git_buf_free(&refnamebuf); - *out = obj; - return 0; - } - } - git_buf_free(&refnamebuf); - - giterr_set(GITERR_REFERENCE, "Refspec '%s' not found.", spec); - return GIT_ERROR; + int error, i; + bool fallbackmode = true; + git_reference *ref; + git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT; + + static const char* formatters[] = { + "%s", + "refs/%s", + "refs/tags/%s", + "refs/heads/%s", + "refs/remotes/%s", + "refs/remotes/%s/HEAD", + NULL + }; + + if (*refname) + git_buf_puts(&name, refname); + else { + git_buf_puts(&name, "HEAD"); + fallbackmode = false; + } + + for (i = 0; formatters[i] && (fallbackmode || i == 0); i++) { + + git_buf_clear(&refnamebuf); + + if ((error = git_buf_printf(&refnamebuf, formatters[i], git_buf_cstr(&name))) < 0) + goto cleanup; + + error = git_reference_lookup_resolved(&ref, repo, git_buf_cstr(&refnamebuf), -1); + + if (!error) { + *out = ref; + error = 0; + goto cleanup; + } + + if (error != GIT_ENOTFOUND) + goto cleanup; + } + +cleanup: + git_buf_free(&name); + git_buf_free(&refnamebuf); + return error; } +static int maybe_sha_or_abbrev(git_object**out, git_repository *repo, const char *spec) +{ + git_oid oid; + size_t speclen = strlen(spec); + + if (git_oid_fromstrn(&oid, spec, speclen) < 0) + return GIT_ENOTFOUND; -static int all_chars_are_digits(const char *str, size_t len) + return git_object_lookup_prefix(out, repo, &oid, speclen, GIT_OBJ_ANY); +} + +static int maybe_describe(git_object**out, git_repository *repo, const char *spec) { - size_t i=0; - for (i=0; i<len; i++) { - if (str[i] < '0' || str[i] > '9') return 0; - } - return 1; + const char *substr; + int match; + + /* "git describe" output; snip everything before/including "-g" */ + substr = strstr(spec, "-g"); + + if (substr == NULL) + return GIT_ENOTFOUND; + + if ((match = spec_looks_like_describe_output(spec)) < 0) + return match; + + if (!match) + return GIT_ENOTFOUND; + + return maybe_sha_or_abbrev(out, repo, substr+2); } -static void normalize_maybe_empty_refname(git_buf *buf, git_repository *repo, const char *refspec, size_t refspeclen) +static int revparse_lookup_object(git_object **out, git_repository *repo, const char *spec) { - git_reference *ref; - - if (!refspeclen) { - /* Empty refspec means current branch (target of HEAD) */ - git_reference_lookup(&ref, repo, "HEAD"); - git_buf_puts(buf, git_reference_target(ref)); - git_reference_free(ref); - } else if (strstr(refspec, "HEAD")) { - /* Explicit head */ - git_buf_puts(buf, refspec); - }else { - if (git__prefixcmp(refspec, "refs/heads/") != 0) { - git_buf_printf(buf, "refs/heads/%s", refspec); - } else { - git_buf_puts(buf, refspec); - } - } + int error; + git_reference *ref; + + error = maybe_describe(out, repo, spec); + if (!error) + return 0; + + if (error < 0 && error != GIT_ENOTFOUND) + return error; + + error = disambiguate_refname(&ref, repo, spec); + if (!error) { + error = git_object_lookup(out, repo, git_reference_oid(ref), GIT_OBJ_ANY); + git_reference_free(ref); + return error; + } + + if (error < 0 && error != GIT_ENOTFOUND) + return error; + + error = maybe_sha_or_abbrev(out, repo, spec); + if (!error) + return 0; + + if (error < 0 && error != GIT_ENOTFOUND) + return error; + + giterr_set(GITERR_REFERENCE, "Refspec '%s' not found.", spec); + return GIT_ENOTFOUND; +} + +static int all_chars_are_digits(const char *str, size_t len) +{ + size_t i = 0; + + for (i = 0; i < len; i++) + if (!git__isdigit(str[i])) return 0; + + return 1; } static int walk_ref_history(git_object **out, git_repository *repo, const char *refspec, const char *reflogspec) { - git_reference *ref; - git_reflog *reflog = NULL; - int n, retcode = GIT_ERROR; - int i, refloglen; - const git_reflog_entry *entry; - git_buf buf = GIT_BUF_INIT; - size_t refspeclen = strlen(refspec); - size_t reflogspeclen = strlen(reflogspec); - - if (git__prefixcmp(reflogspec, "@{") != 0 || - git__suffixcmp(reflogspec, "}") != 0) { - giterr_set(GITERR_INVALID, "Bad reflogspec '%s'", reflogspec); - return GIT_ERROR; - } - - /* "@{-N}" form means walk back N checkouts. That means the HEAD log. */ - if (refspeclen == 0 && !git__prefixcmp(reflogspec, "@{-")) { - regex_t regex; - int regex_error; - - if (git__strtol32(&n, reflogspec+3, NULL, 0) < 0 || - n < 1) { - giterr_set(GITERR_INVALID, "Invalid reflogspec %s", reflogspec); - return GIT_ERROR; - } - - if (!git_reference_lookup(&ref, repo, "HEAD")) { - if (!git_reflog_read(&reflog, ref)) { - regex_error = regcomp(®ex, "checkout: moving from (.*) to .*", REG_EXTENDED); - if (regex_error != 0) { - giterr_set_regex(®ex, regex_error); - } else { - regmatch_t regexmatches[2]; - - refloglen = git_reflog_entrycount(reflog); - for (i=refloglen-1; i >= 0; i--) { - const char *msg; - entry = git_reflog_entry_byindex(reflog, i); - - msg = git_reflog_entry_msg(entry); - if (!regexec(®ex, msg, 2, regexmatches, 0)) { - n--; - if (!n) { - git_buf_put(&buf, msg+regexmatches[1].rm_so, regexmatches[1].rm_eo - regexmatches[1].rm_so); - retcode = revparse_lookup_object(out, repo, git_buf_cstr(&buf)); - break; - } - } - } - regfree(®ex); - } - } - git_reference_free(ref); - } - } else { - int date_error = 0; - git_time_t timestamp; - git_buf datebuf = GIT_BUF_INIT; - - git_buf_put(&datebuf, reflogspec+2, reflogspeclen-3); - date_error = git__date_parse(×tamp, git_buf_cstr(&datebuf)); - - /* @{u} or @{upstream} -> upstream branch, for a tracking branch. This is stored in the config. */ - if (!strcmp(reflogspec, "@{u}") || !strcmp(reflogspec, "@{upstream}")) { - git_config *cfg; - if (!git_repository_config(&cfg, repo)) { - /* Is the ref a tracking branch? */ - const char *remote; - git_buf_clear(&buf); - git_buf_printf(&buf, "branch.%s.remote", refspec); - if (!git_config_get_string(&remote, cfg, git_buf_cstr(&buf))) { - /* Yes. Find the first merge target name. */ - const char *mergetarget; - git_buf_clear(&buf); - git_buf_printf(&buf, "branch.%s.merge", refspec); - if (!git_config_get_string(&mergetarget, cfg, git_buf_cstr(&buf)) && - !git__prefixcmp(mergetarget, "refs/heads/")) { - /* Success. Look up the target and fetch the object. */ - git_buf_clear(&buf); - git_buf_printf(&buf, "refs/remotes/%s/%s", remote, mergetarget+11); - retcode = revparse_lookup_fully_qualifed_ref(out, repo, git_buf_cstr(&buf)); - } - } - git_config_free(cfg); - } - } - - /* @{N} -> Nth prior value for the ref (from reflog) */ - else if (all_chars_are_digits(reflogspec+2, reflogspeclen-3) && - !git__strtol32(&n, reflogspec+2, NULL, 0) && - n <= 100000000) { /* Allow integer time */ - normalize_maybe_empty_refname(&buf, repo, refspec, refspeclen); - - if (n == 0) { - retcode = revparse_lookup_fully_qualifed_ref(out, repo, git_buf_cstr(&buf)); - } else if (!git_reference_lookup(&ref, repo, git_buf_cstr(&buf))) { - if (!git_reflog_read(&reflog, ref)) { - int numentries = git_reflog_entrycount(reflog); - if (numentries < n) { - giterr_set(GITERR_REFERENCE, "Reflog for '%s' has only %d entries, asked for %d", - git_buf_cstr(&buf), numentries, n); - retcode = GIT_ERROR; - } else { - const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, n); - const git_oid *oid = git_reflog_entry_oidold(entry); - retcode = git_object_lookup(out, repo, oid, GIT_OBJ_ANY); - } - } - git_reference_free(ref); - } - } - - else if (!date_error) { - /* Ref as it was on a certain date */ - normalize_maybe_empty_refname(&buf, repo, refspec, refspeclen); - - if (!git_reference_lookup(&ref, repo, git_buf_cstr(&buf))) { - git_reflog *reflog; - if (!git_reflog_read(&reflog, ref)) { - /* Keep walking until we find an entry older than the given date */ - int numentries = git_reflog_entrycount(reflog); - int i; - - /* TODO: clunky. Factor "now" into a utility */ - git_signature *sig; - git_time as_of; - - git_signature_now(&sig, "blah", "blah"); - as_of = sig->when; - git_signature_free(sig); - - as_of.time = (timestamp > 0) - ? timestamp - : sig->when.time + timestamp; - - for (i=numentries-1; i>0; i--) { - const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, i); - git_time commit_time = git_reflog_entry_committer(entry)->when; - if (git__time_cmp(&commit_time, &as_of) <= 0 ) { - retcode = git_object_lookup(out, repo, git_reflog_entry_oidnew(entry), GIT_OBJ_ANY); - break; - } - } - - if (!i) { - /* Didn't find a match. Use the oldest revision in the reflog. */ - const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, 0); - retcode = git_object_lookup(out, repo, git_reflog_entry_oidnew(entry), GIT_OBJ_ANY); - } - - git_reflog_free(reflog); - } - - git_reference_free(ref); - } - } - - git_buf_free(&datebuf); - } - - if (reflog) git_reflog_free(reflog); - git_buf_free(&buf); - return retcode; + git_reference *disambiguated = NULL; + git_reflog *reflog = NULL; + int n, retcode = GIT_ERROR; + int i, refloglen; + const git_reflog_entry *entry; + git_buf buf = GIT_BUF_INIT; + size_t refspeclen = strlen(refspec); + size_t reflogspeclen = strlen(reflogspec); + + if (git__prefixcmp(reflogspec, "@{") != 0 || + git__suffixcmp(reflogspec, "}") != 0) + return revspec_error(reflogspec); + + /* "@{-N}" form means walk back N checkouts. That means the HEAD log. */ + if (!git__prefixcmp(reflogspec, "@{-")) { + regex_t regex; + int regex_error; + + if (refspeclen > 0) + return revspec_error(reflogspec); + + if (git__strtol32(&n, reflogspec+3, NULL, 10) < 0 || n < 1) + return revspec_error(reflogspec); + + if (!git_reference_lookup(&disambiguated, repo, "HEAD")) { + if (!git_reflog_read(&reflog, disambiguated)) { + regex_error = regcomp(®ex, "checkout: moving from (.*) to .*", REG_EXTENDED); + if (regex_error != 0) { + giterr_set_regex(®ex, regex_error); + } else { + regmatch_t regexmatches[2]; + + retcode = GIT_ENOTFOUND; + + refloglen = git_reflog_entrycount(reflog); + for (i=refloglen-1; i >= 0; i--) { + const char *msg; + entry = git_reflog_entry_byindex(reflog, i); + + msg = git_reflog_entry_msg(entry); + if (!regexec(®ex, msg, 2, regexmatches, 0)) { + n--; + if (!n) { + git_buf_put(&buf, msg+regexmatches[1].rm_so, regexmatches[1].rm_eo - regexmatches[1].rm_so); + retcode = revparse_lookup_object(out, repo, git_buf_cstr(&buf)); + break; + } + } + } + regfree(®ex); + } + } + } + } else { + int date_error = 0, result; + git_time_t timestamp; + git_buf datebuf = GIT_BUF_INIT; + + result = disambiguate_refname(&disambiguated, repo, refspec); + + if (result < 0) { + retcode = result; + goto cleanup; + } + + git_buf_put(&datebuf, reflogspec+2, reflogspeclen-3); + date_error = git__date_parse(×tamp, git_buf_cstr(&datebuf)); + + /* @{u} or @{upstream} -> upstream branch, for a tracking branch. This is stored in the config. */ + if (!strcmp(reflogspec, "@{u}") || !strcmp(reflogspec, "@{upstream}")) { + git_reference *tracking; + + if (!(retcode = git_reference_remote_tracking_from_branch(&tracking, disambiguated))) { + retcode = revparse_lookup_fully_qualifed_ref(out, repo, git_reference_name(tracking)); + git_reference_free(tracking); + } + } + + /* @{N} -> Nth prior value for the ref (from reflog) */ + else if (all_chars_are_digits(reflogspec+2, reflogspeclen-3) && + !git__strtol32(&n, reflogspec+2, NULL, 10) && + n <= 100000000) { /* Allow integer time */ + + git_buf_puts(&buf, git_reference_name(disambiguated)); + + if (n == 0) + retcode = revparse_lookup_fully_qualifed_ref(out, repo, git_buf_cstr(&buf)); + else if (!git_reflog_read(&reflog, disambiguated)) { + int numentries = git_reflog_entrycount(reflog); + if (numentries < n + 1) { + giterr_set(GITERR_REFERENCE, "Reflog for '%s' has only %d entries, asked for %d", + git_buf_cstr(&buf), numentries, n); + retcode = GIT_ENOTFOUND; + } else { + const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, n); + const git_oid *oid = git_reflog_entry_oidold(entry); + retcode = git_object_lookup(out, repo, oid, GIT_OBJ_ANY); + } + } + } + + else if (!date_error) { + /* Ref as it was on a certain date */ + git_reflog *reflog; + if (!git_reflog_read(&reflog, disambiguated)) { + /* Keep walking until we find an entry older than the given date */ + int numentries = git_reflog_entrycount(reflog); + int i; + + for (i = numentries - 1; i >= 0; i--) { + const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, i); + git_time commit_time = git_reflog_entry_committer(entry)->when; + if (commit_time.time - timestamp <= 0) { + retcode = git_object_lookup(out, repo, git_reflog_entry_oidnew(entry), GIT_OBJ_ANY); + break; + } + } + + if (i == -1) { + /* Didn't find a match */ + retcode = GIT_ENOTFOUND; + } + + git_reflog_free(reflog); + } + } + + git_buf_free(&datebuf); + } + +cleanup: + if (reflog) + git_reflog_free(reflog); + git_buf_free(&buf); + git_reference_free(disambiguated); + return retcode; } static git_object* dereference_object(git_object *obj) { - git_otype type = git_object_type(obj); - - switch (type) { - case GIT_OBJ_COMMIT: - { - git_tree *tree = NULL; - if (0 == git_commit_tree(&tree, (git_commit*)obj)) { - return (git_object*)tree; - } - } - break; - case GIT_OBJ_TAG: - { - git_object *newobj = NULL; - if (0 == git_tag_target(&newobj, (git_tag*)obj)) { - return newobj; - } - } - break; - - default: - case GIT_OBJ_TREE: - case GIT_OBJ_BLOB: - case GIT_OBJ_OFS_DELTA: - case GIT_OBJ_REF_DELTA: - break; - } - - /* Can't dereference some types */ - return NULL; + git_otype type = git_object_type(obj); + + switch (type) { + case GIT_OBJ_COMMIT: + { + git_tree *tree = NULL; + if (0 == git_commit_tree(&tree, (git_commit*)obj)) { + return (git_object*)tree; + } + } + break; + case GIT_OBJ_TAG: + { + git_object *newobj = NULL; + if (0 == git_tag_target(&newobj, (git_tag*)obj)) { + return newobj; + } + } + break; + + default: + case GIT_OBJ_TREE: + case GIT_OBJ_BLOB: + case GIT_OBJ_OFS_DELTA: + case GIT_OBJ_REF_DELTA: + break; + } + + /* Can't dereference some types */ + return NULL; } static int dereference_to_type(git_object **out, git_object *obj, git_otype target_type) { - int retcode = 1; - git_object *obj1 = obj, *obj2 = obj; - - while (retcode > 0) { - git_otype this_type = git_object_type(obj1); - - if (this_type == target_type) { - *out = obj1; - retcode = 0; - } else { - /* Dereference once, if possible. */ - obj2 = dereference_object(obj1); - if (!obj2) { - giterr_set(GITERR_REFERENCE, "Can't dereference to type"); - retcode = GIT_ERROR; - } - } - if (obj1 != obj && obj1 != obj2) { - git_object_free(obj1); - } - obj1 = obj2; - } - return retcode; + int retcode = 1; + git_object *obj1 = obj, *obj2 = obj; + + while (retcode > 0) { + git_otype this_type = git_object_type(obj1); + + if (this_type == target_type) { + *out = obj1; + retcode = 0; + } else { + /* Dereference once, if possible. */ + obj2 = dereference_object(obj1); + if (!obj2) { + giterr_set(GITERR_REFERENCE, "Can't dereference to type"); + retcode = GIT_ERROR; + } + } + if (obj1 != obj && obj1 != obj2) { + git_object_free(obj1); + } + obj1 = obj2; + } + return retcode; } static git_otype parse_obj_type(const char *str) { - if (!strcmp(str, "{commit}")) return GIT_OBJ_COMMIT; - if (!strcmp(str, "{tree}")) return GIT_OBJ_TREE; - if (!strcmp(str, "{blob}")) return GIT_OBJ_BLOB; - if (!strcmp(str, "{tag}")) return GIT_OBJ_TAG; - return GIT_OBJ_BAD; + if (!strcmp(str, "{commit}")) return GIT_OBJ_COMMIT; + if (!strcmp(str, "{tree}")) return GIT_OBJ_TREE; + if (!strcmp(str, "{blob}")) return GIT_OBJ_BLOB; + if (!strcmp(str, "{tag}")) return GIT_OBJ_TAG; + return GIT_OBJ_BAD; } static int handle_caret_syntax(git_object **out, git_repository *repo, git_object *obj, const char *movement) { - git_commit *commit; - size_t movementlen = strlen(movement); - int n; - - if (*movement == '{') { - if (movement[movementlen-1] != '}') { - set_invalid_syntax_err(movement); - return GIT_ERROR; - } - - /* {} -> Dereference until we reach an object that isn't a tag. */ - if (movementlen == 2) { - git_object *newobj = obj; - git_object *newobj2 = newobj; - while (git_object_type(newobj2) == GIT_OBJ_TAG) { - newobj2 = dereference_object(newobj); - if (newobj != obj) git_object_free(newobj); - if (!newobj2) { - giterr_set(GITERR_REFERENCE, "Couldn't find object of target type."); - return GIT_ERROR; - } - newobj = newobj2; - } - *out = newobj2; - return 0; - } - - /* {/...} -> Walk all commits until we see a commit msg that matches the phrase. */ - if (movement[1] == '/') { - int retcode = GIT_ERROR; - git_revwalk *walk; - if (!git_revwalk_new(&walk, repo)) { - git_oid oid; - regex_t preg; - int reg_error; - git_buf buf = GIT_BUF_INIT; - - git_revwalk_sorting(walk, GIT_SORT_TIME); - git_revwalk_push(walk, git_object_id(obj)); - - /* Extract the regex from the movement string */ - git_buf_put(&buf, movement+2, strlen(movement)-3); - - reg_error = regcomp(&preg, git_buf_cstr(&buf), REG_EXTENDED); - if (reg_error != 0) { - giterr_set_regex(&preg, reg_error); - } else { - while(!git_revwalk_next(&oid, walk)) { - git_object *walkobj; - - /* Fetch the commit object, and check for matches in the message */ - if (!git_object_lookup(&walkobj, repo, &oid, GIT_OBJ_COMMIT)) { - if (!regexec(&preg, git_commit_message((git_commit*)walkobj), 0, NULL, 0)) { - /* Found it! */ - retcode = 0; - *out = walkobj; - if (obj == walkobj) { - /* Avoid leaking an object */ - git_object_free(walkobj); - } - break; - } - git_object_free(walkobj); - } - } - if (retcode < 0) { - giterr_set(GITERR_REFERENCE, "Couldn't find a match for %s", movement); - } - regfree(&preg); - } - - git_buf_free(&buf); - git_revwalk_free(walk); - } - return retcode; - } - - /* {...} -> Dereference until we reach an object of a certain type. */ - if (dereference_to_type(out, obj, parse_obj_type(movement)) < 0) { - return GIT_ERROR; - } - return 0; - } - - /* Dereference until we reach a commit. */ - if (dereference_to_type(&obj, obj, GIT_OBJ_COMMIT) < 0) { - /* Can't dereference to a commit; fail */ - return GIT_ERROR; - } - - /* "^" is the same as "^1" */ - if (movementlen == 0) { - n = 1; - } else { - git__strtol32(&n, movement, NULL, 0); - } - commit = (git_commit*)obj; - - /* "^0" just returns the input */ - if (n == 0) { - *out = obj; - return 0; - } - - if (git_commit_parent(&commit, commit, n-1) < 0) { - return GIT_ERROR; - } - - *out = (git_object*)commit; - return 0; + git_commit *commit; + size_t movementlen = strlen(movement); + int n; + + if (*movement == '{') { + if (movement[movementlen-1] != '}') + return revspec_error(movement); + + /* {} -> Dereference until we reach an object that isn't a tag. */ + if (movementlen == 2) { + git_object *newobj = obj; + git_object *newobj2 = newobj; + while (git_object_type(newobj2) == GIT_OBJ_TAG) { + newobj2 = dereference_object(newobj); + if (newobj != obj) git_object_free(newobj); + if (!newobj2) { + giterr_set(GITERR_REFERENCE, "Couldn't find object of target type."); + return GIT_ERROR; + } + newobj = newobj2; + } + *out = newobj2; + return 0; + } + + /* {/...} -> Walk all commits until we see a commit msg that matches the phrase. */ + if (movement[1] == '/') { + int retcode = GIT_ERROR; + git_revwalk *walk; + if (!git_revwalk_new(&walk, repo)) { + git_oid oid; + regex_t preg; + int reg_error; + git_buf buf = GIT_BUF_INIT; + + git_revwalk_sorting(walk, GIT_SORT_TIME); + git_revwalk_push(walk, git_object_id(obj)); + + /* Extract the regex from the movement string */ + git_buf_put(&buf, movement+2, strlen(movement)-3); + + reg_error = regcomp(&preg, git_buf_cstr(&buf), REG_EXTENDED); + if (reg_error != 0) { + giterr_set_regex(&preg, reg_error); + } else { + while(!git_revwalk_next(&oid, walk)) { + git_object *walkobj; + + /* Fetch the commit object, and check for matches in the message */ + if (!git_object_lookup(&walkobj, repo, &oid, GIT_OBJ_COMMIT)) { + if (!regexec(&preg, git_commit_message((git_commit*)walkobj), 0, NULL, 0)) { + /* Found it! */ + retcode = 0; + *out = walkobj; + if (obj == walkobj) { + /* Avoid leaking an object */ + git_object_free(walkobj); + } + break; + } + git_object_free(walkobj); + } + } + if (retcode < 0) { + giterr_set(GITERR_REFERENCE, "Couldn't find a match for %s", movement); + } + regfree(&preg); + } + + git_buf_free(&buf); + git_revwalk_free(walk); + } + return retcode; + } + + /* {...} -> Dereference until we reach an object of a certain type. */ + if (dereference_to_type(out, obj, parse_obj_type(movement)) < 0) { + return GIT_ERROR; + } + return 0; + } + + /* Dereference until we reach a commit. */ + if (dereference_to_type(&obj, obj, GIT_OBJ_COMMIT) < 0) { + /* Can't dereference to a commit; fail */ + return GIT_ERROR; + } + + /* "^" is the same as "^1" */ + if (movementlen == 0) { + n = 1; + } else { + git__strtol32(&n, movement, NULL, 10); + } + commit = (git_commit*)obj; + + /* "^0" just returns the input */ + if (n == 0) { + *out = obj; + return 0; + } + + if (git_commit_parent(&commit, commit, n-1) < 0) { + return GIT_ENOTFOUND; + } + + *out = (git_object*)commit; + return 0; } static int handle_linear_syntax(git_object **out, git_object *obj, const char *movement) { - git_commit *commit1, *commit2; - int i, n; - - /* Dereference until we reach a commit. */ - if (dereference_to_type(&obj, obj, GIT_OBJ_COMMIT) < 0) { - /* Can't dereference to a commit; fail */ - return GIT_ERROR; - } - - /* "~" is the same as "~1" */ - if (*movement == '\0') { - n = 1; - } else if (git__strtol32(&n, movement, NULL, 0) < 0) { - return GIT_ERROR; - } - commit1 = (git_commit*)obj; - - /* "~0" just returns the input */ - if (n == 0) { - *out = obj; - return 0; - } - - for (i=0; i<n; i++) { - if (git_commit_parent(&commit2, commit1, 0) < 0) { - return GIT_ERROR; - } - if (commit1 != (git_commit*)obj) { - git_commit_free(commit1); - } - commit1 = commit2; - } - - *out = (git_object*)commit1; - return 0; -} - -static int oid_for_tree_path(git_oid *out, git_tree *tree, git_repository *repo, const char *path) -{ - char *str = git__strdup(path); - char *tok; - void *alloc = str; - git_tree *tree2 = tree; - const git_tree_entry *entry = NULL; - - while ((tok = git__strtok(&str, "/\\")) != NULL) { - entry = git_tree_entry_byname(tree2, tok); - if (tree2 != tree) git_tree_free(tree2); - if (git_tree_entry__is_tree(entry)) { - if (git_tree_lookup(&tree2, repo, &entry->oid) < 0) { - git__free(alloc); - return GIT_ERROR; - } - } - } - - if (!entry) { - giterr_set(GITERR_INVALID, "Invalid tree path '%s'", path); - git__free(alloc); - return GIT_ERROR; - } - - git_oid_cpy(out, git_tree_entry_id(entry)); - git__free(alloc); - return 0; + int n; + + /* Dereference until we reach a commit. */ + if (dereference_to_type(&obj, obj, GIT_OBJ_COMMIT) < 0) { + /* Can't dereference to a commit; fail */ + return GIT_ERROR; + } + + /* "~" is the same as "~1" */ + if (*movement == '\0') { + n = 1; + } else if (git__strtol32(&n, movement, NULL, 10) < 0) { + return GIT_ERROR; + } + + return git_commit_nth_gen_ancestor((git_commit **)out, (git_commit*)obj, n); } static int handle_colon_syntax(git_object **out, - git_repository *repo, - git_object *obj, - const char *path) + git_repository *repo, + git_object *obj, + const char *path) { - git_tree *tree; - git_oid oid; - int error; + git_object *tree = obj; + int error = -1; + git_tree_entry *entry = NULL; + + /* Dereference until we reach a tree. */ + if (dereference_to_type(&tree, obj, GIT_OBJ_TREE) < 0) + return GIT_ERROR; + + if (*path == '\0') + return git_object_lookup(out, repo, git_object_id(tree), GIT_OBJ_TREE); - /* Dereference until we reach a tree. */ - if (dereference_to_type(&obj, obj, GIT_OBJ_TREE) < 0) { - return GIT_ERROR; - } - tree = (git_tree*)obj; + /* + * TODO: Handle the relative path syntax + * (:./relative/path and :../relative/path) + */ + if ((error = git_tree_entry_bypath(&entry, (git_tree *)tree, path)) < 0) + goto cleanup; - /* Find the blob at the given path. */ - error = oid_for_tree_path(&oid, tree, repo, path); - git_tree_free(tree); + error = git_tree_entry_to_object(out, repo, entry); - if (error < 0) - return error; +cleanup: + git_tree_entry_free(entry); + if (tree != obj) + git_object_free(tree); - return git_object_lookup(out, repo, &oid, GIT_OBJ_ANY); + return error; } static int revparse_global_grep(git_object **out, git_repository *repo, const char *pattern) { - git_revwalk *walk; - int retcode = GIT_ERROR; - - if (!pattern[0]) { - giterr_set(GITERR_REGEX, "Empty pattern"); - return GIT_ERROR; - } - - if (!git_revwalk_new(&walk, repo)) { - regex_t preg; - int reg_error; - git_oid oid; - - git_revwalk_sorting(walk, GIT_SORT_TIME); - git_revwalk_push_glob(walk, "refs/heads/*"); - - reg_error = regcomp(&preg, pattern, REG_EXTENDED); - if (reg_error != 0) { - giterr_set_regex(&preg, reg_error); - } else { - git_object *walkobj = NULL, *resultobj = NULL; - while(!git_revwalk_next(&oid, walk)) { - /* Fetch the commit object, and check for matches in the message */ - if (walkobj != resultobj) git_object_free(walkobj); - if (!git_object_lookup(&walkobj, repo, &oid, GIT_OBJ_COMMIT)) { - if (!regexec(&preg, git_commit_message((git_commit*)walkobj), 0, NULL, 0)) { - /* Match! */ - resultobj = walkobj; - retcode = 0; - break; - } - } - } - if (!resultobj) { - giterr_set(GITERR_REFERENCE, "Couldn't find a match for %s", pattern); - git_object_free(walkobj); - } else { - *out = resultobj; - } - regfree(&preg); - git_revwalk_free(walk); - } - } - - return retcode; + git_revwalk *walk; + int retcode = GIT_ERROR; + + if (!pattern[0]) { + giterr_set(GITERR_REGEX, "Empty pattern"); + return GIT_ERROR; + } + + if (!git_revwalk_new(&walk, repo)) { + regex_t preg; + int reg_error; + git_oid oid; + + git_revwalk_sorting(walk, GIT_SORT_TIME); + git_revwalk_push_glob(walk, "refs/heads/*"); + + reg_error = regcomp(&preg, pattern, REG_EXTENDED); + if (reg_error != 0) { + giterr_set_regex(&preg, reg_error); + } else { + git_object *walkobj = NULL, *resultobj = NULL; + while(!git_revwalk_next(&oid, walk)) { + /* Fetch the commit object, and check for matches in the message */ + if (walkobj != resultobj) git_object_free(walkobj); + if (!git_object_lookup(&walkobj, repo, &oid, GIT_OBJ_COMMIT)) { + if (!regexec(&preg, git_commit_message((git_commit*)walkobj), 0, NULL, 0)) { + /* Match! */ + resultobj = walkobj; + retcode = 0; + break; + } + } + } + if (!resultobj) { + giterr_set(GITERR_REFERENCE, "Couldn't find a match for %s", pattern); + retcode = GIT_ENOTFOUND; + git_object_free(walkobj); + } else { + *out = resultobj; + } + regfree(&preg); + git_revwalk_free(walk); + } + } + + return retcode; } int git_revparse_single(git_object **out, git_repository *repo, const char *spec) { - revparse_state current_state = REVPARSE_STATE_INIT, next_state = REVPARSE_STATE_INIT; - const char *spec_cur = spec; - git_object *cur_obj = NULL, *next_obj = NULL; - git_buf specbuffer = GIT_BUF_INIT, stepbuffer = GIT_BUF_INIT; - int retcode = 0; - - assert(out && repo && spec); - - if (spec[0] == ':') { - if (spec[1] == '/') { - return revparse_global_grep(out, repo, spec+2); - } - /* TODO: support merge-stage path lookup (":2:Makefile"). */ - giterr_set(GITERR_INVALID, "Unimplemented"); - return GIT_ERROR; - } - - while (current_state != REVPARSE_STATE_DONE) { - switch (current_state) { - case REVPARSE_STATE_INIT: - if (!*spec_cur) { - /* No operators, just a name. Find it and return. */ - retcode = revparse_lookup_object(out, repo, spec); - next_state = REVPARSE_STATE_DONE; - } else if (*spec_cur == '@') { - /* '@' syntax doesn't allow chaining */ - git_buf_puts(&stepbuffer, spec_cur); - retcode = walk_ref_history(out, repo, git_buf_cstr(&specbuffer), git_buf_cstr(&stepbuffer)); - next_state = REVPARSE_STATE_DONE; - } else if (*spec_cur == '^') { - next_state = REVPARSE_STATE_CARET; - } else if (*spec_cur == '~') { - next_state = REVPARSE_STATE_LINEAR; - } else if (*spec_cur == ':') { - next_state = REVPARSE_STATE_COLON; - } else { - git_buf_putc(&specbuffer, *spec_cur); - } - spec_cur++; - - if (current_state != next_state && next_state != REVPARSE_STATE_DONE) { - /* Leaving INIT state, find the object specified, in case that state needs it */ - if (revparse_lookup_object(&next_obj, repo, git_buf_cstr(&specbuffer)) < 0) { - retcode = GIT_ERROR; - next_state = REVPARSE_STATE_DONE; - } - } - break; - - - case REVPARSE_STATE_CARET: - /* Gather characters until NULL, '~', or '^' */ - if (!*spec_cur) { - retcode = handle_caret_syntax(out, repo, cur_obj, git_buf_cstr(&stepbuffer)); - next_state = REVPARSE_STATE_DONE; - } else if (*spec_cur == '~') { - retcode = handle_caret_syntax(&next_obj, repo, cur_obj, git_buf_cstr(&stepbuffer)); - git_buf_clear(&stepbuffer); - next_state = !retcode ? REVPARSE_STATE_LINEAR : REVPARSE_STATE_DONE; - } else if (*spec_cur == '^') { - retcode = handle_caret_syntax(&next_obj, repo, cur_obj, git_buf_cstr(&stepbuffer)); - git_buf_clear(&stepbuffer); - if (retcode < 0) { - next_state = REVPARSE_STATE_DONE; - } - } else { - git_buf_putc(&stepbuffer, *spec_cur); - } - spec_cur++; - break; - - case REVPARSE_STATE_LINEAR: - if (!*spec_cur) { - retcode = handle_linear_syntax(out, cur_obj, git_buf_cstr(&stepbuffer)); - next_state = REVPARSE_STATE_DONE; - } else if (*spec_cur == '~') { - retcode = handle_linear_syntax(&next_obj, cur_obj, git_buf_cstr(&stepbuffer)); - git_buf_clear(&stepbuffer); - if (retcode < 0) { - next_state = REVPARSE_STATE_DONE; - } - } else if (*spec_cur == '^') { - retcode = handle_linear_syntax(&next_obj, cur_obj, git_buf_cstr(&stepbuffer)); - git_buf_clear(&stepbuffer); - next_state = !retcode ? REVPARSE_STATE_CARET : REVPARSE_STATE_DONE; - } else { - git_buf_putc(&stepbuffer, *spec_cur); - } - spec_cur++; - break; - - case REVPARSE_STATE_COLON: - if (*spec_cur) { - git_buf_putc(&stepbuffer, *spec_cur); - } else { - retcode = handle_colon_syntax(out, repo, cur_obj, git_buf_cstr(&stepbuffer)); - next_state = REVPARSE_STATE_DONE; - } - spec_cur++; - break; - - case REVPARSE_STATE_DONE: - if (cur_obj && *out != cur_obj) git_object_free(cur_obj); - if (next_obj && *out != next_obj) git_object_free(next_obj); - break; - } - - current_state = next_state; - if (cur_obj != next_obj) { - if (cur_obj) git_object_free(cur_obj); - cur_obj = next_obj; - } - } - - if (*out != cur_obj) git_object_free(cur_obj); - if (*out != next_obj && next_obj != cur_obj) git_object_free(next_obj); - - git_buf_free(&specbuffer); - git_buf_free(&stepbuffer); - return retcode; + revparse_state current_state = REVPARSE_STATE_INIT, next_state = REVPARSE_STATE_INIT; + const char *spec_cur = spec; + git_object *cur_obj = NULL, *next_obj = NULL; + git_buf specbuffer = GIT_BUF_INIT, stepbuffer = GIT_BUF_INIT; + int retcode = 0; + + assert(out && repo && spec); + + if (spec[0] == ':') { + if (spec[1] == '/') { + return revparse_global_grep(out, repo, spec+2); + } + /* TODO: support merge-stage path lookup (":2:Makefile"). */ + giterr_set(GITERR_INVALID, "Unimplemented"); + return GIT_ERROR; + } + + while (current_state != REVPARSE_STATE_DONE) { + switch (current_state) { + case REVPARSE_STATE_INIT: + if (!*spec_cur) { + /* No operators, just a name. Find it and return. */ + retcode = revparse_lookup_object(out, repo, spec); + next_state = REVPARSE_STATE_DONE; + } else if (*spec_cur == '@') { + /* '@' syntax doesn't allow chaining */ + git_buf_puts(&stepbuffer, spec_cur); + retcode = walk_ref_history(out, repo, git_buf_cstr(&specbuffer), git_buf_cstr(&stepbuffer)); + next_state = REVPARSE_STATE_DONE; + } else if (*spec_cur == '^') { + next_state = REVPARSE_STATE_CARET; + } else if (*spec_cur == '~') { + next_state = REVPARSE_STATE_LINEAR; + } else if (*spec_cur == ':') { + next_state = REVPARSE_STATE_COLON; + } else { + git_buf_putc(&specbuffer, *spec_cur); + } + spec_cur++; + + if (current_state != next_state && next_state != REVPARSE_STATE_DONE) { + /* Leaving INIT state, find the object specified, in case that state needs it */ + if ((retcode = revparse_lookup_object(&next_obj, repo, git_buf_cstr(&specbuffer))) < 0) + next_state = REVPARSE_STATE_DONE; + } + break; + + + case REVPARSE_STATE_CARET: + /* Gather characters until NULL, '~', or '^' */ + if (!*spec_cur) { + retcode = handle_caret_syntax(out, repo, cur_obj, git_buf_cstr(&stepbuffer)); + next_state = REVPARSE_STATE_DONE; + } else if (*spec_cur == '~') { + retcode = handle_caret_syntax(&next_obj, repo, cur_obj, git_buf_cstr(&stepbuffer)); + git_buf_clear(&stepbuffer); + next_state = !retcode ? REVPARSE_STATE_LINEAR : REVPARSE_STATE_DONE; + } else if (*spec_cur == '^') { + retcode = handle_caret_syntax(&next_obj, repo, cur_obj, git_buf_cstr(&stepbuffer)); + git_buf_clear(&stepbuffer); + if (retcode < 0) { + next_state = REVPARSE_STATE_DONE; + } + } else if (*spec_cur == ':') { + retcode = handle_caret_syntax(&next_obj, repo, cur_obj, git_buf_cstr(&stepbuffer)); + git_buf_clear(&stepbuffer); + next_state = !retcode ? REVPARSE_STATE_COLON : REVPARSE_STATE_DONE; + } else { + git_buf_putc(&stepbuffer, *spec_cur); + } + spec_cur++; + break; + + case REVPARSE_STATE_LINEAR: + if (!*spec_cur) { + retcode = handle_linear_syntax(out, cur_obj, git_buf_cstr(&stepbuffer)); + next_state = REVPARSE_STATE_DONE; + } else if (*spec_cur == '~') { + retcode = handle_linear_syntax(&next_obj, cur_obj, git_buf_cstr(&stepbuffer)); + git_buf_clear(&stepbuffer); + if (retcode < 0) { + next_state = REVPARSE_STATE_DONE; + } + } else if (*spec_cur == '^') { + retcode = handle_linear_syntax(&next_obj, cur_obj, git_buf_cstr(&stepbuffer)); + git_buf_clear(&stepbuffer); + next_state = !retcode ? REVPARSE_STATE_CARET : REVPARSE_STATE_DONE; + } else { + git_buf_putc(&stepbuffer, *spec_cur); + } + spec_cur++; + break; + + case REVPARSE_STATE_COLON: + if (*spec_cur) { + git_buf_putc(&stepbuffer, *spec_cur); + } else { + retcode = handle_colon_syntax(out, repo, cur_obj, git_buf_cstr(&stepbuffer)); + next_state = REVPARSE_STATE_DONE; + } + spec_cur++; + break; + + case REVPARSE_STATE_DONE: + if (cur_obj && *out != cur_obj) git_object_free(cur_obj); + if (next_obj && *out != next_obj) git_object_free(next_obj); + break; + } + + current_state = next_state; + if (cur_obj != next_obj) { + if (cur_obj) git_object_free(cur_obj); + cur_obj = next_obj; + } + } + + if (*out != cur_obj) git_object_free(cur_obj); + if (*out != next_obj && next_obj != cur_obj) git_object_free(next_obj); + + git_buf_free(&specbuffer); + git_buf_free(&stepbuffer); + return retcode; } diff --git a/src/revwalk.c b/src/revwalk.c index 13d54b725..9dff283f5 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -188,7 +188,7 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1; unsigned char *buffer = raw->data; unsigned char *buffer_end = buffer + raw->len; - unsigned char *parents_start; + unsigned char *parents_start, *committer_start; int i, parents = 0; int commit_time; @@ -219,17 +219,34 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo commit->out_degree = (unsigned short)parents; + if ((committer_start = buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL) + return commit_error(commit, "object is corrupted"); + + buffer++; + if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL) return commit_error(commit, "object is corrupted"); - if ((buffer = memchr(buffer, '<', buffer_end - buffer)) == NULL || - (buffer = memchr(buffer, '>', buffer_end - buffer)) == NULL) - return commit_error(commit, "malformed author information"); + /* Skip trailing spaces */ + while (buffer > committer_start && git__isspace(*buffer)) + buffer--; + + /* Seek for the begining of the pack of digits */ + while (buffer > committer_start && git__isdigit(*buffer)) + buffer--; + + /* Skip potential timezone offset */ + if ((buffer > committer_start) && (*buffer == '+' || *buffer == '-')) { + buffer--; - while (*buffer == '>' || git__isspace(*buffer)) - buffer++; + while (buffer > committer_start && git__isspace(*buffer)) + buffer--; - if (git__strtol32(&commit_time, (char *)buffer, NULL, 10) < 0) + while (buffer > committer_start && git__isdigit(*buffer)) + buffer--; + } + + if ((buffer == committer_start) || (git__strtol32(&commit_time, (char *)(buffer + 1), NULL, 10) < 0)) return commit_error(commit, "cannot parse commit time"); commit->time = (time_t)commit_time; @@ -540,7 +557,6 @@ static int push_ref(git_revwalk *walk, const char *refname, int hide) struct push_cb_data { git_revwalk *walk; - const char *glob; int hide; }; @@ -548,10 +564,7 @@ static int push_glob_cb(const char *refname, void *data_) { struct push_cb_data *data = (struct push_cb_data *)data_; - if (!p_fnmatch(data->glob, refname, 0)) - return push_ref(data->walk, refname, data->hide); - - return 0; + return push_ref(data->walk, refname, data->hide); } static int push_glob(git_revwalk *walk, const char *glob, int hide) @@ -584,11 +597,10 @@ static int push_glob(git_revwalk *walk, const char *glob, int hide) goto on_error; data.walk = walk; - data.glob = git_buf_cstr(&buf); data.hide = hide; - if (git_reference_foreach( - walk->repo, GIT_REF_LISTALL, push_glob_cb, &data) < 0) + if (git_reference_foreach_glob( + walk->repo, git_buf_cstr(&buf), GIT_REF_LISTALL, push_glob_cb, &data) < 0) goto on_error; regfree(&preg); diff --git a/src/signature.c b/src/signature.c index 332bdf65f..1f788356b 100644 --- a/src/signature.c +++ b/src/signature.c @@ -40,7 +40,7 @@ static const char *skip_trailing_spaces(const char *buffer_start, const char *bu static int signature_error(const char *msg) { - giterr_set(GITERR_INVALID, "Failed to parse signature - %s", msg); + giterr_set(GITERR_INVALID, "Failed to process signature - %s", msg); return -1; } @@ -72,9 +72,16 @@ static int process_trimming(const char *input, char **storage, const char *input return 0; } +static bool contains_angle_brackets(const char *input) +{ + if (strchr(input, '<') != NULL) + return true; + + return strchr(input, '>') != NULL; +} + int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset) { - int error; git_signature *p = NULL; assert(name && email); @@ -84,11 +91,18 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema p = git__calloc(1, sizeof(git_signature)); GITERR_CHECK_ALLOC(p); - if ((error = process_trimming(name, &p->name, name + strlen(name), 1)) < 0 || - (error = process_trimming(email, &p->email, email + strlen(email), 1)) < 0) + if (process_trimming(name, &p->name, name + strlen(name), 1) < 0 || + process_trimming(email, &p->email, email + strlen(email), 1) < 0) { git_signature_free(p); - return error; + return -1; + } + + if (contains_angle_brackets(p->email) || + contains_angle_brackets(p->name)) + { + git_signature_free(p); + return signature_error("Neither `name` nor `email` should contain angle brackets chars."); } p->when.time = time; diff --git a/src/strmap.h b/src/strmap.h index da5ca0dba..9972039a0 100644 --- a/src/strmap.h +++ b/src/strmap.h @@ -19,7 +19,7 @@ __KHASH_TYPE(str, const char *, void *); typedef khash_t(str) git_strmap; #define GIT__USE_STRMAP \ - __KHASH_IMPL(str, static inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal) + __KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal) #define git_strmap_alloc() kh_init(str) #define git_strmap_free(h) kh_destroy(str, h), h = NULL diff --git a/src/submodule.c b/src/submodule.c index 3c07e657d..b8537cb8c 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -31,7 +31,7 @@ static git_cvar_map _sm_ignore_map[] = { {GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE} }; -static inline khint_t str_hash_no_trailing_slash(const char *s) +static kh_inline khint_t str_hash_no_trailing_slash(const char *s) { khint_t h; @@ -42,7 +42,7 @@ static inline khint_t str_hash_no_trailing_slash(const char *s) return h; } -static inline int str_equal_no_trailing_slash(const char *a, const char *b) +static kh_inline int str_equal_no_trailing_slash(const char *a, const char *b) { size_t alen = a ? strlen(a) : 0; size_t blen = b ? strlen(b) : 0; @@ -55,7 +55,7 @@ static inline int str_equal_no_trailing_slash(const char *a, const char *b) return (alen == blen && strncmp(a, b, alen) == 0); } -__KHASH_IMPL(str, static inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash); +__KHASH_IMPL(str, static kh_inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash); static git_submodule *submodule_alloc(const char *name) { diff --git a/src/transports/http.c b/src/transports/http.c index 4139a2fa6..f25d639f3 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -545,6 +545,7 @@ static int http_download_pack(git_transport *transport, git_repository *repo, gi http_parser_settings settings; char buffer[1024]; gitno_buffer buf; + git_buf path = GIT_BUF_INIT; git_indexer_stream *idx = NULL; download_pack_cbdata data; @@ -555,7 +556,10 @@ static int http_download_pack(git_transport *transport, git_repository *repo, gi return -1; } - if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0) + if (git_buf_joinpath(&path, git_repository_path(repo), "objects/pack") < 0) + return -1; + + if (git_indexer_stream_new(&idx, git_buf_cstr(&path)) < 0) return -1; /* @@ -600,6 +604,7 @@ static int http_download_pack(git_transport *transport, git_repository *repo, gi on_error: git_indexer_stream_free(idx); + git_buf_free(&path); return -1; } diff --git a/src/tree.c b/src/tree.c index 9bdc2180c..31a581cdb 100644 --- a/src/tree.c +++ b/src/tree.c @@ -35,6 +35,22 @@ static int entry_sort_cmp(const void *a, const void *b) entry_b->filename, entry_b->filename_len, git_tree_entry__is_tree(entry_b)); } +static git_tree_entry *alloc_entry(const char *filename) +{ + git_tree_entry *entry = NULL; + size_t filename_len = strlen(filename); + + entry = git__malloc(sizeof(git_tree_entry) + filename_len + 1); + if (!entry) + return NULL; + + memset(entry, 0x0, sizeof(git_tree_entry)); + memcpy(entry->filename, filename, filename_len); + entry->filename[filename_len] = 0; + entry->filename_len = filename_len; + + return entry; +} struct tree_key_search { const char *filename; @@ -76,7 +92,7 @@ static int homing_search_cmp(const void *key, const void *array_member) * ambiguous because of folder vs file sorting, we look linearly * around the area for our target file. */ -static int tree_key_search(git_vector *entries, const char *filename) +static int tree_key_search(git_vector *entries, const char *filename, size_t filename_len) { struct tree_key_search ksearch; const git_tree_entry *entry; @@ -84,7 +100,7 @@ static int tree_key_search(git_vector *entries, const char *filename) int homing, i; ksearch.filename = filename; - ksearch.filename_len = strlen(filename); + ksearch.filename_len = filename_len; /* Initial homing search; find an entry on the tree with * the same prefix as the filename we're looking for */ @@ -100,7 +116,8 @@ static int tree_key_search(git_vector *entries, const char *filename) if (homing_search_cmp(&ksearch, entry) < 0) break; - if (strcmp(filename, entry->filename) == 0) + if (entry->filename_len == filename_len && + memcmp(filename, entry->filename, filename_len) == 0) return i; } @@ -112,7 +129,8 @@ static int tree_key_search(git_vector *entries, const char *filename) if (homing_search_cmp(&ksearch, entry) > 0) break; - if (strcmp(filename, entry->filename) == 0) + if (entry->filename_len == filename_len && + memcmp(filename, entry->filename, filename_len) == 0) return i; } @@ -120,16 +138,38 @@ static int tree_key_search(git_vector *entries, const char *filename) return GIT_ENOTFOUND; } +void git_tree_entry_free(git_tree_entry *entry) +{ + if (entry == NULL) + return; + + git__free(entry); +} + +git_tree_entry *git_tree_entry_dup(const git_tree_entry *entry) +{ + size_t total_size; + git_tree_entry *copy; + + assert(entry); + + total_size = sizeof(git_tree_entry) + entry->filename_len + 1; + + copy = git__malloc(total_size); + if (!copy) + return NULL; + + memcpy(copy, entry, total_size); + return copy; +} + void git_tree__free(git_tree *tree) { unsigned int i; for (i = 0; i < tree->entries.length; ++i) { - git_tree_entry *e; - e = git_vector_get(&tree->entries, i); - - git__free(e->filename); - git__free(e); + git_tree_entry *e = git_vector_get(&tree->entries, i); + git_tree_entry_free(e); } git_vector_free(&tree->entries); @@ -179,19 +219,21 @@ int git_tree_entry_to_object( return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY); } -const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) +static git_tree_entry *entry_fromname(git_tree *tree, const char *name, size_t name_len) { - int idx; - - assert(tree && filename); - - idx = tree_key_search(&tree->entries, filename); - if (idx == GIT_ENOTFOUND) + int idx = tree_key_search(&tree->entries, name, name_len); + if (idx < 0) return NULL; return git_vector_get(&tree->entries, idx); } +const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) +{ + assert(tree && filename); + return entry_fromname(tree, filename, strlen(filename)); +} + const git_tree_entry *git_tree_entry_byindex(git_tree *tree, unsigned int idx) { assert(tree); @@ -244,28 +286,28 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf while (buffer < buffer_end) { git_tree_entry *entry; - int tmp; - - entry = git__calloc(1, sizeof(git_tree_entry)); - GITERR_CHECK_ALLOC(entry); + int attr; - if (git_vector_insert(&tree->entries, entry) < 0) - return -1; - - if (git__strtol32(&tmp, buffer, &buffer, 8) < 0 || - !buffer || !valid_attributes(tmp)) + if (git__strtol32(&attr, buffer, &buffer, 8) < 0 || + !buffer || !valid_attributes(attr)) return tree_error("Failed to parse tree. Can't parse attributes"); - entry->attr = tmp; - if (*buffer++ != ' ') return tree_error("Failed to parse tree. Object is corrupted"); if (memchr(buffer, 0, buffer_end - buffer) == NULL) return tree_error("Failed to parse tree. Object is corrupted"); - entry->filename = git__strdup(buffer); - entry->filename_len = strlen(buffer); + /** Allocate the entry and store it in the entries vector */ + { + entry = alloc_entry(buffer); + GITERR_CHECK_ALLOC(entry); + + if (git_vector_insert(&tree->entries, entry) < 0) + return -1; + + entry->attr = attr; + } while (buffer < buffer_end && *buffer != 0) buffer++; @@ -303,16 +345,17 @@ static unsigned int find_next_dir(const char *dirname, git_index *index, unsigne return i; } -static int append_entry(git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes) +static int append_entry( + git_treebuilder *bld, + const char *filename, + const git_oid *id, + unsigned int attributes) { git_tree_entry *entry; - entry = git__calloc(1, sizeof(git_tree_entry)); + entry = alloc_entry(filename); GITERR_CHECK_ALLOC(entry); - entry->filename = git__strdup(filename); - entry->filename_len = strlen(entry->filename); - git_oid_cpy(&entry->oid, id); entry->attr = attributes; @@ -488,7 +531,12 @@ on_error: return -1; } -int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes) +int git_treebuilder_insert( + const git_tree_entry **entry_out, + git_treebuilder *bld, + const char *filename, + const git_oid *id, + unsigned int attributes) { git_tree_entry *entry; int pos; @@ -501,30 +549,28 @@ int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, con if (!valid_entry_name(filename)) return tree_error("Failed to insert entry. Invalid name for a tree entry"); - pos = tree_key_search(&bld->entries, filename); + pos = tree_key_search(&bld->entries, filename, strlen(filename)); if (pos >= 0) { entry = git_vector_get(&bld->entries, pos); if (entry->removed) entry->removed = 0; } else { - entry = git__calloc(1, sizeof(git_tree_entry)); + entry = alloc_entry(filename); GITERR_CHECK_ALLOC(entry); - - entry->filename = git__strdup(filename); - entry->filename_len = strlen(entry->filename); } git_oid_cpy(&entry->oid, id); entry->attr = attributes; - if (pos == GIT_ENOTFOUND) { + if (pos < 0) { if (git_vector_insert(&bld->entries, entry) < 0) return -1; } - if (entry_out != NULL) + if (entry_out != NULL) { *entry_out = entry; + } return 0; } @@ -536,7 +582,7 @@ static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filenam assert(bld && filename); - idx = tree_key_search(&bld->entries, filename); + idx = tree_key_search(&bld->entries, filename, strlen(filename)); if (idx < 0) return NULL; @@ -625,8 +671,7 @@ void git_treebuilder_clear(git_treebuilder *bld) for (i = 0; i < bld->entries.length; ++i) { git_tree_entry *e = bld->entries.contents[i]; - git__free(e->filename); - git__free(e); + git_tree_entry_free(e); } git_vector_clear(&bld->entries); @@ -642,85 +687,78 @@ void git_treebuilder_free(git_treebuilder *bld) git__free(bld); } -static int tree_frompath( - git_tree **parent_out, +static size_t subpath_len(const char *path) +{ + const char *slash_pos = strchr(path, '/'); + if (slash_pos == NULL) + return strlen(path); + + return slash_pos - path; +} + +int git_tree_entry_bypath( + git_tree_entry **entry_out, git_tree *root, - git_buf *treeentry_path, - size_t offset) + const char *path) { - char *slash_pos = NULL; - const git_tree_entry* entry; int error = 0; git_tree *subtree; + const git_tree_entry *entry; + size_t filename_len; - if (!*(treeentry_path->ptr + offset)) { - giterr_set(GITERR_INVALID, - "Invalid relative path to a tree entry '%s'.", treeentry_path->ptr); - return -1; - } - - slash_pos = (char *)strchr(treeentry_path->ptr + offset, '/'); + /* Find how long is the current path component (i.e. + * the filename between two slashes */ + filename_len = subpath_len(path); - if (slash_pos == NULL) - return git_tree_lookup( - parent_out, - root->object.repo, - git_object_id((const git_object *)root) - ); - - if (slash_pos == treeentry_path->ptr + offset) { - giterr_set(GITERR_INVALID, - "Invalid relative path to a tree entry '%s'.", treeentry_path->ptr); - return -1; + if (filename_len == 0) { + giterr_set(GITERR_TREE, "Invalid tree path given"); + return GIT_ENOTFOUND; } - *slash_pos = '\0'; - - entry = git_tree_entry_byname(root, treeentry_path->ptr + offset); - - if (slash_pos != NULL) - *slash_pos = '/'; + entry = entry_fromname(root, path, filename_len); if (entry == NULL) { giterr_set(GITERR_TREE, - "No tree entry can be found from " - "the given tree and relative path '%s'.", treeentry_path->ptr); + "The path '%s' does not exist in the given tree", path); return GIT_ENOTFOUND; } + switch (path[filename_len]) { + case '/': + /* If there are more components in the path... + * then this entry *must* be a tree */ + if (!git_tree_entry__is_tree(entry)) { + giterr_set(GITERR_TREE, + "The path '%s' does not exist in the given tree", path); + return GIT_ENOTFOUND; + } + + /* If there's only a slash left in the path, we + * return the current entry; otherwise, we keep + * walking down the path */ + if (path[filename_len + 1] != '\0') + break; + + case '\0': + /* If there are no more components in the path, return + * this entry */ + *entry_out = git_tree_entry_dup(entry); + return 0; + } if (git_tree_lookup(&subtree, root->object.repo, &entry->oid) < 0) - return error; + return -1; - error = tree_frompath( - parent_out, + error = git_tree_entry_bypath( + entry_out, subtree, - treeentry_path, - (slash_pos - treeentry_path->ptr) + 1 + path + filename_len + 1 ); git_tree_free(subtree); return error; } -int git_tree_get_subtree( - git_tree **subtree, - git_tree *root, - const char *subtree_path) -{ - int error; - git_buf buffer = GIT_BUF_INIT; - - assert(subtree && root && subtree_path); - - if ((error = git_buf_sets(&buffer, subtree_path)) == 0) - error = tree_frompath(subtree, root, &buffer, 0); - - git_buf_free(&buffer); - - return error; -} - static int tree_walk_post( git_tree *tree, git_treewalk_cb callback, diff --git a/src/tree.h b/src/tree.h index 498a90d66..c49309cbc 100644 --- a/src/tree.h +++ b/src/tree.h @@ -13,11 +13,11 @@ #include "vector.h" struct git_tree_entry { - unsigned int attr; - char *filename; + uint16_t removed; + uint16_t attr; git_oid oid; size_t filename_len; - int removed; + char filename[1]; }; struct git_tree { diff --git a/src/unix/map.c b/src/unix/map.c index 772f4e247..9dcae5845 100644 --- a/src/unix/map.c +++ b/src/unix/map.c @@ -33,6 +33,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs mflag = MAP_PRIVATE; out->data = mmap(NULL, len, mprot, mflag, fd, offset); + if (!out->data || out->data == MAP_FAILED) { giterr_set(GITERR_OS, "Failed to mmap. Could not write data"); return -1; @@ -47,6 +48,7 @@ int p_munmap(git_map *map) { assert(map != NULL); munmap(map->data, map->len); + return 0; } diff --git a/src/unix/posix.h b/src/unix/posix.h index 304dd1419..7a3a388ec 100644 --- a/src/unix/posix.h +++ b/src/unix/posix.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_posix__w32_h__ #define INCLUDE_posix__w32_h__ -#ifndef __sun +#if !defined(__sun) && !defined(__amigaos4__) # include <fnmatch.h> # define p_fnmatch(p, s, f) fnmatch(p, s, f) #else diff --git a/src/util.h b/src/util.h index eed2bc80c..a84dcab1e 100644 --- a/src/util.h +++ b/src/util.h @@ -204,16 +204,14 @@ GIT_INLINE(bool) git__isalpha(int c) return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); } -GIT_INLINE(bool) git__isspace(int c) +GIT_INLINE(bool) git__isdigit(int c) { - return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v'); + return (c >= '0' && c <= '9'); } -GIT_INLINE(int) git__time_cmp(const git_time *a, const git_time *b) +GIT_INLINE(bool) git__isspace(int c) { - /* Adjust for time zones. Times are in seconds, offsets are in minutes. */ - git_time_t adjusted_a = a->time + ((b->offset - a->offset) * 60); - return (int)(adjusted_a - b->time); + return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v' || c == 0x85 /* Unicode CR+LF */); } GIT_INLINE(bool) git__iswildcard(int c) diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 62fbd1143..c0d66c7ff 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -238,7 +238,7 @@ int p_open(const char *path, int flags, ...) va_list arg_list; va_start(arg_list, flags); - mode = va_arg(arg_list, mode_t); + mode = (mode_t)va_arg(arg_list, int); va_end(arg_list); } |