summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/amiga/map.c51
-rw-r--r--src/attr_file.c14
-rw-r--r--src/branch.c56
-rw-r--r--src/buffer.c36
-rw-r--r--src/buffer.h19
-rw-r--r--src/commit.c45
-rw-r--r--src/config.c16
-rw-r--r--src/config_file.c37
-rw-r--r--src/config_file.h17
-rw-r--r--src/diff_output.c2
-rw-r--r--src/fetch.c9
-rw-r--r--src/fileops.c2
-rw-r--r--src/index.c2
-rw-r--r--src/indexer.c52
-rw-r--r--src/khash.h10
-rw-r--r--src/map.h4
-rw-r--r--src/mwindow.c18
-rw-r--r--src/mwindow.h1
-rw-r--r--src/netops.c10
-rw-r--r--src/notes.c22
-rw-r--r--src/object.c4
-rw-r--r--src/odb.c12
-rw-r--r--src/odb_loose.c84
-rw-r--r--src/odb_pack.c20
-rw-r--r--src/oidmap.h2
-rw-r--r--src/pack.c46
-rw-r--r--src/pack.h4
-rw-r--r--src/path.c73
-rw-r--r--src/path.h23
-rw-r--r--src/pkt.c2
-rw-r--r--src/pool.c2
-rw-r--r--src/posix.c93
-rw-r--r--src/posix.h37
-rw-r--r--src/reflog.c48
-rw-r--r--src/refs.c142
-rw-r--r--src/repository.c139
-rw-r--r--src/revparse.c1328
-rw-r--r--src/revwalk.c42
-rw-r--r--src/signature.c24
-rw-r--r--src/strmap.h2
-rw-r--r--src/submodule.c6
-rw-r--r--src/transports/http.c7
-rw-r--r--src/tree.c238
-rw-r--r--src/tree.h6
-rw-r--r--src/unix/map.c2
-rw-r--r--src/unix/posix.h2
-rw-r--r--src/util.h10
-rw-r--r--src/win32/posix_w32.c2
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(&regex, regexp, REG_EXTENDED)) < 0) {
+ giterr_set_regex(&regex, result);
+ regfree(&regex);
+ 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(&regex, 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(&regex);
+
+ 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(&regex, regex_str, REG_EXTENDED);
if (result < 0) {
giterr_set_regex(&regex, result);
+ regfree(&regex);
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);
diff --git a/src/map.h b/src/map.h
index 96d879547..6ce6d3685 100644
--- a/src/map.h
+++ b/src/map.h
@@ -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);
diff --git a/src/odb.c b/src/odb.c
index e0c8fa262..493c8292a 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -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
diff --git a/src/pkt.c b/src/pkt.c
index 88510f4b1..e003b97e2 100644
--- a/src/pkt.c
+++ b/src/pkt.c
@@ -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(&regex, ".+-[0-9]+-g[0-9a-fA-F]+", REG_EXTENDED);
- if (regex_error != 0) {
- giterr_set_regex(&regex, regex_error);
- return 1; /* To be safe */
- }
- retcode = regexec(&regex, spec, 0, NULL, 0);
- regfree(&regex);
- return retcode == 0;
+ regex_t regex;
+ int regex_error, retcode;
+
+ regex_error = regcomp(&regex, ".+-[0-9]+-g[0-9a-fA-F]+", REG_EXTENDED);
+ if (regex_error != 0) {
+ giterr_set_regex(&regex, regex_error);
+ return regex_error;
+ }
+
+ retcode = regexec(&regex, spec, 0, NULL, 0);
+ regfree(&regex);
+ 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(&regex, "checkout: moving from (.*) to .*", REG_EXTENDED);
- if (regex_error != 0) {
- giterr_set_regex(&regex, 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(&regex, 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(&regex);
- }
- }
- 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(&timestamp, 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(&regex, "checkout: moving from (.*) to .*", REG_EXTENDED);
+ if (regex_error != 0) {
+ giterr_set_regex(&regex, 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(&regex, 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(&regex);
+ }
+ }
+ }
+ } 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(&timestamp, 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);
}