summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVicent Martí <tanoku@gmail.com>2012-05-02 15:59:02 -0700
committerVicent Martí <tanoku@gmail.com>2012-05-02 15:59:02 -0700
commit40879facad0337d954d4904e212af3b36cdb9465 (patch)
treeaea730551948c67bb1fb88098cf8a67d3ed3211d /src
parent2218fd57a50ceb851cb131939bf0747e072e40f6 (diff)
parent3fd99be98a91416dae77d65fe593965a0723fa8c (diff)
downloadlibgit2-40879facad0337d954d4904e212af3b36cdb9465.tar.gz
Merge branch 'new-error-handling' into development
Conflicts: .travis.yml include/git2/diff.h src/config_file.c src/diff.c src/diff_output.c src/mwindow.c src/path.c tests-clar/clar_helpers.c tests-clar/object/tree/frompath.c tests/t00-core.c tests/t03-objwrite.c tests/t08-tag.c tests/t10-refs.c tests/t12-repo.c tests/t18-status.c tests/test_helpers.c tests/test_main.c
Diffstat (limited to 'src')
-rw-r--r--src/attr.c277
-rw-r--r--src/attr.h18
-rw-r--r--src/attr_file.c228
-rw-r--r--src/attr_file.h29
-rw-r--r--src/blob.c116
-rw-r--r--src/branch.c208
-rw-r--r--src/branch.h17
-rw-r--r--src/buffer.c77
-rw-r--r--src/buffer.h38
-rw-r--r--src/cache.c16
-rw-r--r--src/cc-compat.h8
-rw-r--r--src/commit.c186
-rw-r--r--src/common.h12
-rw-r--r--src/config.c274
-rw-r--r--src/config.h5
-rw-r--r--src/config_cache.c1
-rw-r--r--src/config_file.c874
-rw-r--r--src/config_file.h31
-rw-r--r--src/crlf.c7
-rw-r--r--src/delta-apply.c15
-rw-r--r--src/diff.c518
-rw-r--r--src/diff.h15
-rw-r--r--src/diff_output.c242
-rw-r--r--src/errors.c110
-rw-r--r--src/fetch.c152
-rw-r--r--src/fetch.h7
-rw-r--r--src/filebuf.c241
-rw-r--r--src/filebuf.h25
-rw-r--r--src/fileops.c262
-rw-r--r--src/fileops.h41
-rw-r--r--src/filter.c16
-rw-r--r--src/global.c6
-rw-r--r--src/global.h3
-rw-r--r--src/hashtable.c258
-rw-r--r--src/hashtable.h95
-rw-r--r--src/ignore.c122
-rw-r--r--src/index.c326
-rw-r--r--src/index.h2
-rw-r--r--src/indexer.c653
-rw-r--r--src/iterator.c145
-rw-r--r--src/khash.h608
-rw-r--r--src/map.h5
-rw-r--r--src/mwindow.c54
-rw-r--r--src/mwindow.h6
-rw-r--r--src/netops.c106
-rw-r--r--src/netops.h10
-rw-r--r--src/notes.c179
-rw-r--r--src/object.c30
-rw-r--r--src/odb.c312
-rw-r--r--src/odb.h10
-rw-r--r--src/odb_loose.c352
-rw-r--r--src/odb_pack.c172
-rw-r--r--src/oid.c77
-rw-r--r--src/oidmap.h42
-rw-r--r--src/pack.c340
-rw-r--r--src/pack.h23
-rw-r--r--src/path.c267
-rw-r--r--src/path.h37
-rw-r--r--src/pkt.c217
-rw-r--r--src/pkt.h12
-rw-r--r--src/pool.c294
-rw-r--r--src/pool.h125
-rw-r--r--src/posix.c38
-rw-r--r--src/posix.h8
-rw-r--r--src/pqueue.c12
-rw-r--r--src/protocol.c30
-rw-r--r--src/reflog.c177
-rw-r--r--src/refs.c1191
-rw-r--r--src/refs.h27
-rw-r--r--src/refspec.c45
-rw-r--r--src/remote.c298
-rw-r--r--src/repository.c935
-rw-r--r--src/repository.h13
-rw-r--r--src/revwalk.c554
-rw-r--r--src/sha1.c4
-rw-r--r--src/sha1.h2
-rw-r--r--src/sha1_lookup.c3
-rw-r--r--src/signature.c125
-rw-r--r--src/status.c833
-rw-r--r--src/strmap.h54
-rw-r--r--src/submodule.c384
-rw-r--r--src/tag.c244
-rw-r--r--src/transport.c8
-rw-r--r--src/transport.h11
-rw-r--r--src/transports/git.c325
-rw-r--r--src/transports/http.c369
-rw-r--r--src/transports/local.c141
-rw-r--r--src/tree.c287
-rw-r--r--src/tree.h2
-rw-r--r--src/tsort.c28
-rw-r--r--src/unix/map.c30
-rw-r--r--src/util.c78
-rw-r--r--src/util.h36
-rw-r--r--src/vector.c65
-rw-r--r--src/vector.h2
-rw-r--r--src/win32/dir.c88
-rw-r--r--src/win32/map.c52
-rw-r--r--src/win32/posix.h2
-rw-r--r--src/win32/posix_w32.c232
-rw-r--r--src/win32/pthread.c13
-rw-r--r--src/win32/utf-conv.c41
-rw-r--r--src/xdiff/xemit.c2
-rw-r--r--src/xdiff/xmerge.c6
-rw-r--r--src/xdiff/xutils.c6
104 files changed, 8877 insertions, 6878 deletions
diff --git a/src/attr.c b/src/attr.c
index 603498df2..120d12737 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -3,6 +3,8 @@
#include "config.h"
#include <ctype.h>
+GIT__USE_STRMAP;
+
static int collect_attr_files(
git_repository *repo, const char *path, git_vector *files);
@@ -21,10 +23,11 @@ int git_attr_get(
*value = NULL;
- if ((error = git_attr_path__init(
- &path, pathname, git_repository_workdir(repo))) < GIT_SUCCESS ||
- (error = collect_attr_files(repo, pathname, &files)) < GIT_SUCCESS)
- return git__rethrow(error, "Could not get attribute for %s", pathname);
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
+ return -1;
+
+ if ((error = collect_attr_files(repo, pathname, &files)) < 0)
+ goto cleanup;
attr.name = name;
attr.name_hash = git_attr_file__name_hash(name);
@@ -33,18 +36,17 @@ int git_attr_get(
git_attr_file__foreach_matching_rule(file, &path, j, rule) {
int pos = git_vector_bsearch(&rule->assigns, &attr);
- git_clearerror(); /* okay if search failed */
-
if (pos >= 0) {
*value = ((git_attr_assignment *)git_vector_get(
&rule->assigns, pos))->value;
- goto found;
+ goto cleanup;
}
}
}
-found:
+cleanup:
git_vector_free(&files);
+ git_attr_path__free(&path);
return error;
}
@@ -70,15 +72,14 @@ int git_attr_get_many(
memset((void *)values, 0, sizeof(const char *) * num_attr);
- if ((error = git_attr_path__init(
- &path, pathname, git_repository_workdir(repo))) < GIT_SUCCESS ||
- (error = collect_attr_files(repo, pathname, &files)) < GIT_SUCCESS)
- return git__rethrow(error, "Could not get attributes for %s", pathname);
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
+ return -1;
- if ((info = git__calloc(num_attr, sizeof(attr_get_many_info))) == NULL) {
- git__rethrow(GIT_ENOMEM, "Could not get attributes for %s", pathname);
+ if ((error = collect_attr_files(repo, pathname, &files)) < 0)
goto cleanup;
- }
+
+ info = git__calloc(num_attr, sizeof(attr_get_many_info));
+ GITERR_CHECK_ALLOC(info);
git_vector_foreach(&files, i, file) {
@@ -96,8 +97,6 @@ int git_attr_get_many(
}
pos = git_vector_bsearch(&rule->assigns, &info[k].name);
- git_clearerror(); /* okay if search failed */
-
if (pos >= 0) {
info[k].found = (git_attr_assignment *)
git_vector_get(&rule->assigns, pos);
@@ -112,6 +111,7 @@ int git_attr_get_many(
cleanup:
git_vector_free(&files);
+ git_attr_path__free(&path);
git__free(info);
return error;
@@ -130,18 +130,16 @@ int git_attr_foreach(
git_attr_file *file;
git_attr_rule *rule;
git_attr_assignment *assign;
- git_hashtable *seen = NULL;
+ git_strmap *seen = NULL;
- if ((error = git_attr_path__init(
- &path, pathname, git_repository_workdir(repo))) < GIT_SUCCESS ||
- (error = collect_attr_files(repo, pathname, &files)) < GIT_SUCCESS)
- return git__rethrow(error, "Could not get attributes for %s", pathname);
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
+ return -1;
- seen = git_hashtable_alloc(8, git_hash__strhash_cb, git_hash__strcmp_cb);
- if (!seen) {
- error = GIT_ENOMEM;
+ if ((error = collect_attr_files(repo, pathname, &files)) < 0)
goto cleanup;
- }
+
+ seen = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(seen);
git_vector_foreach(&files, i, file) {
@@ -149,27 +147,23 @@ int git_attr_foreach(
git_vector_foreach(&rule->assigns, k, assign) {
/* skip if higher priority assignment was already seen */
- if (git_hashtable_lookup(seen, assign->name))
+ if (git_strmap_exists(seen, assign->name))
continue;
- error = git_hashtable_insert(seen, assign->name, assign);
- if (error != GIT_SUCCESS)
- goto cleanup;
+ git_strmap_insert(seen, assign->name, assign, error);
+ if (error >= 0)
+ error = callback(assign->name, assign->value, payload);
- error = callback(assign->name, assign->value, payload);
- if (error != GIT_SUCCESS)
+ if (error != 0)
goto cleanup;
}
}
}
cleanup:
- if (seen)
- git_hashtable_free(seen);
+ git_strmap_free(seen);
git_vector_free(&files);
-
- if (error != GIT_SUCCESS)
- (void)git__rethrow(error, "Could not get attributes for %s", pathname);
+ git_attr_path__free(&path);
return error;
}
@@ -182,40 +176,42 @@ int git_attr_add_macro(
{
int error;
git_attr_rule *macro = NULL;
+ git_pool *pool;
- if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS)
- return error;
+ if (git_attr_cache__init(repo) < 0)
+ return -1;
macro = git__calloc(1, sizeof(git_attr_rule));
- if (!macro)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(macro);
- macro->match.pattern = git__strdup(name);
- if (!macro->match.pattern) {
- git__free(macro);
- return GIT_ENOMEM;
- }
+ pool = &git_repository_attr_cache(repo)->pool;
+
+ macro->match.pattern = git_pool_strdup(pool, name);
+ GITERR_CHECK_ALLOC(macro->match.pattern);
macro->match.length = strlen(macro->match.pattern);
macro->match.flags = GIT_ATTR_FNMATCH_MACRO;
- error = git_attr_assignment__parse(repo, &macro->assigns, &values);
+ error = git_attr_assignment__parse(repo, pool, &macro->assigns, &values);
- if (error == GIT_SUCCESS)
+ if (!error)
error = git_attr_cache__insert_macro(repo, macro);
- if (error < GIT_SUCCESS)
+ if (error < 0)
git_attr_rule__free(macro);
return error;
}
-int git_attr_cache__is_cached(git_repository *repo, const char *path)
+bool git_attr_cache__is_cached(git_repository *repo, const char *path)
{
const char *cache_key = path;
+ git_strmap *files = git_repository_attr_cache(repo)->files;
+
if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0)
cache_key += strlen(git_repository_workdir(repo));
- return (git_hashtable_lookup(repo->attrcache.files, cache_key) == NULL);
+
+ return git_strmap_exists(files, cache_key);
}
int git_attr_cache__lookup_or_create_file(
@@ -226,32 +222,36 @@ int git_attr_cache__lookup_or_create_file(
git_attr_file **file_ptr)
{
int error;
- git_attr_cache *cache = &repo->attrcache;
+ git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file *file = NULL;
+ khiter_t pos;
- file = git_hashtable_lookup(cache->files, key);
- if (file) {
- *file_ptr = file;
- return GIT_SUCCESS;
+ pos = git_strmap_lookup_index(cache->files, key);
+ if (git_strmap_valid_index(cache->files, pos)) {
+ *file_ptr = git_strmap_value_at(cache->files, pos);
+ return 0;
}
- if (loader && git_path_exists(filename) != GIT_SUCCESS) {
+ if (loader && git_path_exists(filename) == false) {
*file_ptr = NULL;
- return GIT_SUCCESS;
+ return 0;
}
- if ((error = git_attr_file__new(&file)) < GIT_SUCCESS)
- return error;
+ if (git_attr_file__new(&file, &cache->pool) < 0)
+ return -1;
if (loader)
error = loader(repo, filename, file);
else
error = git_attr_file__set_path(repo, key, file);
- if (error == GIT_SUCCESS)
- error = git_hashtable_insert(cache->files, file->path, file);
+ if (!error) {
+ git_strmap_insert(cache->files, file->path, file, error);
+ if (error > 0)
+ error = 0;
+ }
- if (error < GIT_SUCCESS) {
+ if (error < 0) {
git_attr_file__free(file);
file = NULL;
}
@@ -274,8 +274,8 @@ int git_attr_cache__push_file(
const char *cache_key;
if (base != NULL) {
- if ((error = git_buf_joinpath(&path, base, filename)) < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&path, base, filename) < 0)
+ return -1;
filename = path.ptr;
}
@@ -287,7 +287,7 @@ int git_attr_cache__push_file(
error = git_attr_cache__lookup_or_create_file(
repo, cache_key, filename, loader, &file);
- if (error == GIT_SUCCESS && file != NULL)
+ if (!error && file != NULL)
error = git_vector_insert(stack, file);
git_buf_free(&path);
@@ -311,19 +311,17 @@ static int push_one_attr(void *ref, git_buf *path)
static int collect_attr_files(
git_repository *repo, const char *path, git_vector *files)
{
- int error = GIT_SUCCESS;
+ int error;
git_buf dir = GIT_BUF_INIT;
- git_config *cfg;
const char *workdir = git_repository_workdir(repo);
attr_walk_up_info info;
- if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS)
- goto cleanup;
-
- if ((error = git_vector_init(files, 4, NULL)) < GIT_SUCCESS)
- goto cleanup;
+ if (git_attr_cache__init(repo) < 0 ||
+ git_vector_init(files, 4, NULL) < 0)
+ return -1;
- if ((error = git_path_find_dir(&dir, path, workdir)) < GIT_SUCCESS)
+ error = git_path_find_dir(&dir, path, workdir);
+ if (error < 0)
goto cleanup;
/* in precendence order highest to lowest:
@@ -333,38 +331,33 @@ static int collect_attr_files(
* - $GIT_PREFIX/etc/gitattributes
*/
- error = push_attrs(repo, files, repo->path_repository, GIT_ATTR_FILE_INREPO);
- if (error < GIT_SUCCESS)
+ error = push_attrs(
+ repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO);
+ if (error < 0)
goto cleanup;
info.repo = repo;
info.files = files;
error = git_path_walk_up(&dir, workdir, push_one_attr, &info);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
- if ((error = git_repository_config(&cfg, repo)) == GIT_SUCCESS) {
- const char *core_attribs = NULL;
- git_config_get_string(cfg, GIT_ATTR_CONFIG, &core_attribs);
- git_clearerror(); /* don't care if attributesfile is not set */
- if (core_attribs)
- error = push_attrs(repo, files, NULL, core_attribs);
- git_config_free(cfg);
+ if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
+ error = push_attrs(
+ repo, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);
+ if (error < 0)
+ goto cleanup;
}
- if (error == GIT_SUCCESS) {
- error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
- if (error == GIT_SUCCESS)
- error = push_attrs(repo, files, NULL, dir.ptr);
- else if (error == GIT_ENOTFOUND)
- error = GIT_SUCCESS;
- }
+ error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
+ if (!error)
+ error = push_attrs(repo, files, NULL, dir.ptr);
+ else if (error == GIT_ENOTFOUND)
+ error = 0;
cleanup:
- if (error < GIT_SUCCESS) {
- git__rethrow(error, "Could not get attributes for '%s'", path);
+ if (error < 0)
git_vector_free(files);
- }
git_buf_free(&dir);
return error;
@@ -373,68 +366,108 @@ static int collect_attr_files(
int git_attr_cache__init(git_repository *repo)
{
- int error = GIT_SUCCESS;
- git_attr_cache *cache = &repo->attrcache;
+ int ret;
+ git_attr_cache *cache = git_repository_attr_cache(repo);
+ git_config *cfg;
if (cache->initialized)
- return GIT_SUCCESS;
+ return 0;
+
+ /* cache config settings for attributes and ignores */
+ if (git_repository_config__weakptr(&cfg, repo) < 0)
+ return -1;
+
+ ret = git_config_get_string(cfg, GIT_ATTR_CONFIG, &cache->cfg_attr_file);
+ if (ret < 0 && ret != GIT_ENOTFOUND)
+ return ret;
+
+ ret = git_config_get_string(cfg, GIT_IGNORE_CONFIG, &cache->cfg_excl_file);
+ if (ret < 0 && ret != GIT_ENOTFOUND)
+ return ret;
+ giterr_clear();
+
+ /* allocate hashtable for attribute and ignore file contents */
if (cache->files == NULL) {
- cache->files = git_hashtable_alloc(
- 8, git_hash__strhash_cb, git_hash__strcmp_cb);
- if (!cache->files)
- return git__throw(GIT_ENOMEM, "Could not initialize attribute cache");
+ cache->files = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(cache->files);
}
+ /* allocate hashtable for attribute macros */
if (cache->macros == NULL) {
- cache->macros = git_hashtable_alloc(
- 8, git_hash__strhash_cb, git_hash__strcmp_cb);
- if (!cache->macros)
- return git__throw(GIT_ENOMEM, "Could not initialize attribute cache");
+ cache->macros = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(cache->macros);
}
+ /* allocate string pool */
+ if (git_pool_init(&cache->pool, 1, 0) < 0)
+ return -1;
+
cache->initialized = 1;
/* insert default macros */
- error = git_attr_add_macro(repo, "binary", "-diff -crlf");
-
- return error;
+ return git_attr_add_macro(repo, "binary", "-diff -crlf -text");
}
void git_attr_cache_flush(
git_repository *repo)
{
+ git_attr_cache *cache;
+
if (!repo)
return;
- if (repo->attrcache.files) {
+ cache = git_repository_attr_cache(repo);
+
+ if (cache->files != NULL) {
git_attr_file *file;
- GIT_HASHTABLE_FOREACH_VALUE(repo->attrcache.files, file,
- git_attr_file__free(file));
+ git_strmap_foreach_value(cache->files, file, {
+ git_attr_file__free(file);
+ });
- git_hashtable_free(repo->attrcache.files);
- repo->attrcache.files = NULL;
+ git_strmap_free(cache->files);
}
- if (repo->attrcache.macros) {
+ if (cache->macros != NULL) {
git_attr_rule *rule;
- GIT_HASHTABLE_FOREACH_VALUE(repo->attrcache.macros, rule,
- git_attr_rule__free(rule));
+ git_strmap_foreach_value(cache->macros, rule, {
+ git_attr_rule__free(rule);
+ });
- git_hashtable_free(repo->attrcache.macros);
- repo->attrcache.macros = NULL;
+ git_strmap_free(cache->macros);
}
- repo->attrcache.initialized = 0;
+ git_pool_clear(&cache->pool);
+
+ cache->initialized = 0;
}
int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
{
+ git_strmap *macros = git_repository_attr_cache(repo)->macros;
+ int error;
+
+ /* TODO: generate warning log if (macro->assigns.length == 0) */
if (macro->assigns.length == 0)
- return git__throw(GIT_EMISSINGOBJDATA, "git attribute macro with no values");
+ return 0;
- return git_hashtable_insert(
- repo->attrcache.macros, macro->match.pattern, macro);
+ git_strmap_insert(macros, macro->match.pattern, macro, error);
+ return (error < 0) ? -1 : 0;
}
+
+git_attr_rule *git_attr_cache__lookup_macro(
+ git_repository *repo, const char *name)
+{
+ git_strmap *macros = git_repository_attr_cache(repo)->macros;
+ khiter_t pos;
+
+ pos = git_strmap_lookup_index(macros, name);
+
+ if (!git_strmap_valid_index(macros, pos))
+ return NULL;
+
+ return (git_attr_rule *)git_strmap_value_at(macros, pos);
+}
+
diff --git a/src/attr.h b/src/attr.h
index 5dbbb2366..43caf1b81 100644
--- a/src/attr.h
+++ b/src/attr.h
@@ -8,11 +8,18 @@
#define INCLUDE_attr_h__
#include "attr_file.h"
+#include "strmap.h"
+
+#define GIT_ATTR_CONFIG "core.attributesfile"
+#define GIT_IGNORE_CONFIG "core.excludesfile"
typedef struct {
int initialized;
- git_hashtable *files; /* hash path to git_attr_file of rules */
- git_hashtable *macros; /* hash name to vector<git_attr_assignment> */
+ git_pool pool;
+ git_strmap *files; /* hash path to git_attr_file of rules */
+ git_strmap *macros; /* hash name to vector<git_attr_assignment> */
+ const char *cfg_attr_file; /* cached value of core.attributesfile */
+ const char *cfg_excl_file; /* cached value of core.excludesfile */
} git_attr_cache;
extern int git_attr_cache__init(git_repository *repo);
@@ -20,6 +27,9 @@ extern int git_attr_cache__init(git_repository *repo);
extern int git_attr_cache__insert_macro(
git_repository *repo, git_attr_rule *macro);
+extern git_attr_rule *git_attr_cache__lookup_macro(
+ git_repository *repo, const char *name);
+
extern int git_attr_cache__lookup_or_create_file(
git_repository *repo,
const char *key,
@@ -34,7 +44,7 @@ extern int git_attr_cache__push_file(
const char *filename,
int (*loader)(git_repository *, const char *, git_attr_file *));
-/* returns GIT_SUCCESS if path is in cache */
-extern int git_attr_cache__is_cached(git_repository *repo, const char *path);
+/* returns true if path is in cache */
+extern bool git_attr_cache__is_cached(git_repository *repo, const char *path);
#endif
diff --git a/src/attr_file.c b/src/attr_file.c
index 3783b5ef3..25c21b1fd 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -9,26 +9,32 @@ const char *git_attr__false = "[internal]__FALSE__";
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);
-int git_attr_file__new(git_attr_file **attrs_ptr)
+int git_attr_file__new(git_attr_file **attrs_ptr, git_pool *pool)
{
- int error;
git_attr_file *attrs = NULL;
attrs = git__calloc(1, sizeof(git_attr_file));
- if (attrs == NULL)
- error = GIT_ENOMEM;
- else
- error = git_vector_init(&attrs->rules, 4, NULL);
+ GITERR_CHECK_ALLOC(attrs);
- if (error != GIT_SUCCESS) {
- git__rethrow(error, "Could not allocate attribute storage");
- git__free(attrs);
- attrs = NULL;
+ if (pool)
+ attrs->pool = pool;
+ else {
+ attrs->pool = git__calloc(1, sizeof(git_pool));
+ if (!attrs->pool || git_pool_init(attrs->pool, 1, 0) < 0)
+ goto fail;
+ attrs->pool_is_allocated = true;
}
+ if (git_vector_init(&attrs->rules, 4, NULL) < 0)
+ goto fail;
+
*attrs_ptr = attrs;
+ return 0;
- return error;
+fail:
+ git_attr_file__free(attrs);
+ attrs_ptr = NULL;
+ return -1;
}
int git_attr_file__set_path(
@@ -50,13 +56,15 @@ int git_attr_file__set_path(
file->path = git__strdup(path);
}
- return (file->path == NULL) ? GIT_ENOMEM : GIT_SUCCESS;
+ GITERR_CHECK_ALLOC(file->path);
+
+ return 0;
}
int git_attr_file__from_buffer(
git_repository *repo, const char *buffer, git_attr_file *attrs)
{
- int error = GIT_SUCCESS;
+ int error = 0;
const char *scan = NULL;
char *context = NULL;
git_attr_rule *rule = NULL;
@@ -68,19 +76,21 @@ int git_attr_file__from_buffer(
if (attrs->path && git__suffixcmp(attrs->path, GIT_ATTR_FILE) == 0) {
context = git__strndup(attrs->path,
strlen(attrs->path) - strlen(GIT_ATTR_FILE));
- if (!context) error = GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(context);
}
- while (error == GIT_SUCCESS && *scan) {
+ while (!error && *scan) {
/* allocate rule if needed */
if (!rule && !(rule = git__calloc(1, sizeof(git_attr_rule)))) {
- error = GIT_ENOMEM;
+ error = -1;
break;
}
/* parse the next "pattern attr attr attr" line */
- if (!(error = git_attr_fnmatch__parse(&rule->match, context, &scan)) &&
- !(error = git_attr_assignment__parse(repo, &rule->assigns, &scan)))
+ if (!(error = git_attr_fnmatch__parse(
+ &rule->match, attrs->pool, context, &scan)) &&
+ !(error = git_attr_assignment__parse(
+ repo, attrs->pool, &rule->assigns, &scan)))
{
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
/* should generate error/warning if this is coming from any
@@ -92,10 +102,10 @@ int git_attr_file__from_buffer(
}
/* if the rule wasn't a pattern, on to the next */
- if (error != GIT_SUCCESS) {
+ if (error < 0) {
git_attr_rule__clear(rule); /* reset rule contents */
if (error == GIT_ENOTFOUND)
- error = GIT_SUCCESS;
+ error = 0;
} else {
rule = NULL; /* vector now "owns" the rule */
}
@@ -110,21 +120,20 @@ int git_attr_file__from_buffer(
int git_attr_file__from_file(
git_repository *repo, const char *path, git_attr_file *file)
{
- int error = GIT_SUCCESS;
+ int error;
git_buf fbuf = GIT_BUF_INIT;
assert(path && file);
- if (file->path == NULL)
- error = git_attr_file__set_path(repo, path, file);
+ if (file->path == NULL && git_attr_file__set_path(repo, path, file) < 0)
+ return -1;
+
+ if (git_futils_readbuffer(&fbuf, path) < 0)
+ return -1;
- if (error == GIT_SUCCESS &&
- (error = git_futils_readbuffer(&fbuf, path)) == GIT_SUCCESS)
- error = git_attr_file__from_buffer(repo, fbuf.ptr, file);
+ error = git_attr_file__from_buffer(repo, fbuf.ptr, file);
git_buf_free(&fbuf);
- if (error != GIT_SUCCESS)
- git__rethrow(error, "Could not open attribute file '%s'", path);
return error;
}
@@ -145,12 +154,18 @@ void git_attr_file__free(git_attr_file *file)
git__free(file->path);
file->path = NULL;
+ if (file->pool_is_allocated) {
+ git_pool_clear(file->pool);
+ git__free(file->pool);
+ }
+ file->pool = NULL;
+
git__free(file);
}
-unsigned long git_attr_file__name_hash(const char *name)
+uint32_t git_attr_file__name_hash(const char *name)
{
- unsigned long h = 5381;
+ uint32_t h = 5381;
int c;
assert(name);
while ((c = (int)*name++) != 0)
@@ -176,7 +191,6 @@ int git_attr_file__lookup_one(
git_attr_file__foreach_matching_rule(file, path, i, rule) {
int pos = git_vector_bsearch(&rule->assigns, &name);
- git_clearerror(); /* okay if search failed */
if (pos >= 0) {
*value = ((git_attr_assignment *)
@@ -185,37 +199,37 @@ int git_attr_file__lookup_one(
}
}
- return GIT_SUCCESS;
+ return 0;
}
-int git_attr_fnmatch__match(
+bool git_attr_fnmatch__match(
git_attr_fnmatch *match,
const git_attr_path *path)
{
- int matched = FNM_NOMATCH;
+ int fnm;
if (match->flags & GIT_ATTR_FNMATCH_DIRECTORY && !path->is_dir)
- return matched;
+ return false;
if (match->flags & GIT_ATTR_FNMATCH_FULLPATH)
- matched = p_fnmatch(match->pattern, path->path, FNM_PATHNAME);
+ fnm = p_fnmatch(match->pattern, path->path, FNM_PATHNAME);
else if (path->is_dir)
- matched = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR);
+ fnm = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR);
else
- matched = p_fnmatch(match->pattern, path->basename, 0);
+ fnm = p_fnmatch(match->pattern, path->basename, 0);
- return matched;
+ return (fnm == FNM_NOMATCH) ? false : true;
}
-int git_attr_rule__match(
+bool git_attr_rule__match(
git_attr_rule *rule,
const git_attr_path *path)
{
- int matched = git_attr_fnmatch__match(&rule->match, path);
+ bool matched = git_attr_fnmatch__match(&rule->match, path);
if (rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE)
- matched = (matched == GIT_SUCCESS) ? FNM_NOMATCH : GIT_SUCCESS;
+ matched = !matched;
return matched;
}
@@ -230,7 +244,6 @@ git_attr_assignment *git_attr_rule__lookup_assignment(
key.name_hash = git_attr_file__name_hash(name);
pos = git_vector_bsearch(&rule->assigns, &key);
- git_clearerror(); /* okay if search failed */
return (pos >= 0) ? git_vector_get(&rule->assigns, pos) : NULL;
}
@@ -238,25 +251,48 @@ git_attr_assignment *git_attr_rule__lookup_assignment(
int git_attr_path__init(
git_attr_path *info, const char *path, const char *base)
{
- assert(info && path);
- info->path = path;
- info->basename = strrchr(path, '/');
+ /* build full path as best we can */
+ git_buf_init(&info->full, 0);
+
+ if (base != NULL && git_path_root(path) < 0) {
+ if (git_buf_joinpath(&info->full, base, path) < 0)
+ return -1;
+ info->path = info->full.ptr + strlen(base);
+ } else {
+ if (git_buf_sets(&info->full, path) < 0)
+ return -1;
+ info->path = info->full.ptr;
+ }
+
+ /* remove trailing slashes */
+ while (info->full.size > 0) {
+ if (info->full.ptr[info->full.size - 1] != '/')
+ break;
+ info->full.size--;
+ }
+ info->full.ptr[info->full.size] = '\0';
+
+ /* skip leading slashes in path */
+ while (*info->path == '/')
+ info->path++;
+
+ /* find trailing basename component */
+ info->basename = strrchr(info->path, '/');
if (info->basename)
info->basename++;
if (!info->basename || !*info->basename)
- info->basename = path;
+ info->basename = info->path;
- if (base != NULL && git_path_root(path) < 0) {
- git_buf full_path = GIT_BUF_INIT;
- int error = git_buf_joinpath(&full_path, base, path);
- if (error == GIT_SUCCESS)
- info->is_dir = (git_path_isdir(full_path.ptr) == GIT_SUCCESS);
- git_buf_free(&full_path);
- return error;
- }
- info->is_dir = (git_path_isdir(path) == GIT_SUCCESS);
+ info->is_dir = (int)git_path_isdir(info->full.ptr);
+
+ return 0;
+}
- return GIT_SUCCESS;
+void git_attr_path__free(git_attr_path *info)
+{
+ git_buf_free(&info->full);
+ info->path = NULL;
+ info->basename = NULL;
}
@@ -293,12 +329,13 @@ int git_attr_path__init(
*/
/*
- * This will return GIT_SUCCESS if the spec was filled out,
+ * This will return 0 if the spec was filled out,
* GIT_ENOTFOUND if the fnmatch does not require matching, or
* another error code there was an actual problem.
*/
int git_attr_fnmatch__parse(
git_attr_fnmatch *spec,
+ git_pool *pool,
const char *source,
const char **base)
{
@@ -339,7 +376,13 @@ int git_attr_fnmatch__parse(
if (*scan == '/') {
spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH;
slash_count++;
+ if (pattern == scan)
+ pattern++;
}
+ /* remember if we see an unescaped wildcard in pattern */
+ else if ((*scan == '*' || *scan == '.' || *scan == '[') &&
+ (scan == pattern || (*(scan - 1) != '\\')))
+ spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD;
}
*base = scan;
@@ -360,7 +403,8 @@ int git_attr_fnmatch__parse(
/* given an unrooted fullpath match from a file inside a repo,
* prefix the pattern with the relative directory of the source file
*/
- spec->pattern = git__malloc(sourcelen + spec->length + 1);
+ spec->pattern = git_pool_malloc(
+ pool, (uint32_t)(sourcelen + spec->length + 1));
if (spec->pattern) {
memcpy(spec->pattern, source, sourcelen);
memcpy(spec->pattern + sourcelen, pattern, spec->length);
@@ -368,12 +412,12 @@ int git_attr_fnmatch__parse(
spec->pattern[spec->length] = '\0';
}
} else {
- spec->pattern = git__strndup(pattern, spec->length);
+ spec->pattern = git_pool_strndup(pool, pattern, spec->length);
}
if (!spec->pattern) {
*base = git__next_line(pattern);
- return GIT_ENOMEM;
+ return -1;
} else {
/* strip '\' that might have be used for internal whitespace */
char *to = spec->pattern;
@@ -389,7 +433,7 @@ int git_attr_fnmatch__parse(
}
}
- return GIT_SUCCESS;
+ return 0;
}
static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)
@@ -407,14 +451,11 @@ static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)
static void git_attr_assignment__free(git_attr_assignment *assign)
{
- git__free(assign->name);
+ /* name and value are stored in a git_pool associated with the
+ * git_attr_file, so they do not need to be freed here
+ */
assign->name = NULL;
-
- if (assign->is_allocated) {
- git__free((void *)assign->value);
- assign->value = NULL;
- }
-
+ assign->value = NULL;
git__free(assign);
}
@@ -430,10 +471,11 @@ static int merge_assignments(void **old_raw, void *new_raw)
int git_attr_assignment__parse(
git_repository *repo,
+ git_pool *pool,
git_vector *assigns,
const char **base)
{
- int error = GIT_SUCCESS;
+ int error;
const char *scan = *base;
git_attr_assignment *assign = NULL;
@@ -441,7 +483,7 @@ int git_attr_assignment__parse(
assigns->_cmp = sort_by_hash_and_name;
- while (*scan && *scan != '\n' && error == GIT_SUCCESS) {
+ while (*scan && *scan != '\n') {
const char *name_start, *value_start;
/* skip leading blanks */
@@ -450,16 +492,12 @@ int git_attr_assignment__parse(
/* allocate assign if needed */
if (!assign) {
assign = git__calloc(1, sizeof(git_attr_assignment));
- if (!assign) {
- error = GIT_ENOMEM;
- break;
- }
+ GITERR_CHECK_ALLOC(assign);
GIT_REFCOUNT_INC(assign);
}
assign->name_hash = 5381;
assign->value = git_attr__true;
- assign->is_allocated = 0;
/* look for magic name prefixes */
if (*scan == '-') {
@@ -487,11 +525,8 @@ int git_attr_assignment__parse(
}
/* allocate permanent storage for name */
- assign->name = git__strndup(name_start, scan - name_start);
- if (!assign->name) {
- error = GIT_ENOMEM;
- break;
- }
+ assign->name = git_pool_strndup(pool, name_start, scan - name_start);
+ GITERR_CHECK_ALLOC(assign->name);
/* if there is an equals sign, find the value */
if (*scan == '=') {
@@ -499,20 +534,15 @@ int git_attr_assignment__parse(
/* if we found a value, allocate permanent storage for it */
if (scan > value_start) {
- assign->value = git__strndup(value_start, scan - value_start);
- if (!assign->value) {
- error = GIT_ENOMEM;
- break;
- } else {
- assign->is_allocated = 1;
- }
+ assign->value = git_pool_strndup(pool, value_start, scan - value_start);
+ GITERR_CHECK_ALLOC(assign->value);
}
}
/* expand macros (if given a repo with a macro cache) */
if (repo != NULL && assign->value == git_attr__true) {
git_attr_rule *macro =
- git_hashtable_lookup(repo->attrcache.macros, assign->name);
+ git_attr_cache__lookup_macro(repo, assign->name);
if (macro != NULL) {
unsigned int i;
@@ -523,35 +553,27 @@ int git_attr_assignment__parse(
error = git_vector_insert_sorted(
assigns, massign, &merge_assignments);
-
- if (error == GIT_EEXISTS)
- error = GIT_SUCCESS;
- else if (error != GIT_SUCCESS)
- break;
+ if (error < 0 && error != GIT_EEXISTS)
+ return error;
}
}
}
/* insert allocated assign into vector */
error = git_vector_insert_sorted(assigns, assign, &merge_assignments);
- if (error == GIT_EEXISTS)
- error = GIT_SUCCESS;
- else if (error < GIT_SUCCESS)
- break;
+ if (error < 0 && error != GIT_EEXISTS)
+ return error;
/* clear assign since it is now "owned" by the vector */
assign = NULL;
}
- if (!assigns->length)
- error = git__throw(GIT_ENOTFOUND, "No attribute assignments found for rule");
-
if (assign != NULL)
git_attr_assignment__free(assign);
*base = git__next_line(scan);
- return error;
+ return (assigns->length == 0) ? GIT_ENOTFOUND : 0;
}
static void git_attr_rule__clear(git_attr_rule *rule)
@@ -568,7 +590,7 @@ static void git_attr_rule__clear(git_attr_rule *rule)
git_vector_free(&rule->assigns);
}
- git__free(rule->match.pattern);
+ /* match.pattern is stored in a git_pool, so no need to free */
rule->match.pattern = NULL;
rule->match.length = 0;
}
diff --git a/src/attr_file.h b/src/attr_file.h
index 1ba18f9e4..10851bc49 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -9,18 +9,19 @@
#include "git2/attr.h"
#include "vector.h"
-#include "hashtable.h"
+#include "pool.h"
+#include "buffer.h"
#define GIT_ATTR_FILE ".gitattributes"
#define GIT_ATTR_FILE_INREPO "info/attributes"
#define GIT_ATTR_FILE_SYSTEM "gitattributes"
-#define GIT_ATTR_CONFIG "core.attributesfile"
#define GIT_ATTR_FNMATCH_NEGATIVE (1U << 0)
#define GIT_ATTR_FNMATCH_DIRECTORY (1U << 1)
#define GIT_ATTR_FNMATCH_FULLPATH (1U << 2)
#define GIT_ATTR_FNMATCH_MACRO (1U << 3)
#define GIT_ATTR_FNMATCH_IGNORE (1U << 4)
+#define GIT_ATTR_FNMATCH_HASWILD (1U << 5)
typedef struct {
char *pattern;
@@ -36,33 +37,35 @@ typedef struct {
typedef struct {
git_refcount unused;
const char *name;
- unsigned long name_hash;
+ uint32_t name_hash;
} git_attr_name;
typedef struct {
git_refcount rc; /* for macros */
char *name;
- unsigned long name_hash;
+ uint32_t name_hash;
const char *value;
- int is_allocated;
} git_attr_assignment;
typedef struct {
char *path; /* cache the path this was loaded from */
git_vector rules; /* vector of <rule*> or <fnmatch*> */
+ git_pool *pool;
+ bool pool_is_allocated;
} git_attr_file;
typedef struct {
+ git_buf full;
const char *path;
const char *basename;
- int is_dir;
+ int is_dir;
} git_attr_path;
/*
* git_attr_file API
*/
-extern int git_attr_file__new(git_attr_file **attrs_ptr);
+extern int git_attr_file__new(git_attr_file **attrs_ptr, git_pool *pool);
extern void git_attr_file__free(git_attr_file *file);
extern int git_attr_file__from_buffer(
@@ -82,9 +85,9 @@ extern int git_attr_file__lookup_one(
/* loop over rules in file from bottom to top */
#define git_attr_file__foreach_matching_rule(file, path, iter, rule) \
git_vector_rforeach(&(file)->rules, (iter), (rule)) \
- if (git_attr_rule__match((rule), (path)) == GIT_SUCCESS)
+ if (git_attr_rule__match((rule), (path)))
-extern unsigned long git_attr_file__name_hash(const char *name);
+extern uint32_t git_attr_file__name_hash(const char *name);
/*
@@ -93,16 +96,17 @@ extern unsigned long git_attr_file__name_hash(const char *name);
extern int git_attr_fnmatch__parse(
git_attr_fnmatch *spec,
+ git_pool *pool,
const char *source,
const char **base);
-extern int git_attr_fnmatch__match(
+extern bool git_attr_fnmatch__match(
git_attr_fnmatch *rule,
const git_attr_path *path);
extern void git_attr_rule__free(git_attr_rule *rule);
-extern int git_attr_rule__match(
+extern bool git_attr_rule__match(
git_attr_rule *rule,
const git_attr_path *path);
@@ -112,8 +116,11 @@ extern git_attr_assignment *git_attr_rule__lookup_assignment(
extern int git_attr_path__init(
git_attr_path *info, const char *path, const char *base);
+extern void git_attr_path__free(git_attr_path *info);
+
extern int git_attr_assignment__parse(
git_repository *repo, /* needed to expand macros */
+ git_pool *pool,
git_vector *assigns,
const char **scan);
diff --git a/src/blob.c b/src/blob.c
index b67f8afa5..36571c70a 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -42,7 +42,7 @@ int git_blob__parse(git_blob *blob, git_odb_object *odb_obj)
assert(blob);
git_cached_obj_incref((git_cached_obj *)odb_obj);
blob->odb_object = odb_obj;
- return GIT_SUCCESS;
+ return 0;
}
int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len)
@@ -51,58 +51,50 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
git_odb *odb;
git_odb_stream *stream;
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS)
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
+ (error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < 0)
return error;
- if ((error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create blob");
+ if ((error = stream->write(stream, buffer, len)) == 0)
+ error = stream->finalize_write(oid, stream);
- if ((error = stream->write(stream, buffer, len)) < GIT_SUCCESS) {
- stream->free(stream);
- return error;
- }
-
- error = stream->finalize_write(oid, stream);
stream->free(stream);
-
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create blob");
-
- return GIT_SUCCESS;
+ return error;
}
-static int write_file_stream(git_oid *oid, git_odb *odb, const char *path, git_off_t file_size)
+static int write_file_stream(
+ git_oid *oid, git_odb *odb, const char *path, git_off_t file_size)
{
int fd, error;
char buffer[4096];
git_odb_stream *stream = NULL;
- if ((error = git_odb_open_wstream(&stream, odb, file_size, GIT_OBJ_BLOB)) < GIT_SUCCESS)
+ if ((error = git_odb_open_wstream(
+ &stream, odb, (size_t)file_size, GIT_OBJ_BLOB)) < 0)
return error;
- if ((fd = p_open(path, O_RDONLY)) < 0) {
- error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", path);
- goto cleanup;
+ if ((fd = git_futils_open_ro(path)) < 0) {
+ stream->free(stream);
+ return -1;
}
- while (file_size > 0) {
+ while (!error && file_size > 0) {
ssize_t read_len = p_read(fd, buffer, sizeof(buffer));
if (read_len < 0) {
- error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
- p_close(fd);
- goto cleanup;
+ giterr_set(
+ GITERR_OS, "Failed to create blob. Can't read whole file");
+ error = -1;
}
-
- stream->write(stream, buffer, read_len);
- file_size -= read_len;
+ else if (!(error = stream->write(stream, buffer, read_len)))
+ file_size -= read_len;
}
p_close(fd);
- error = stream->finalize_write(oid, stream);
-cleanup:
+ if (!error)
+ error = stream->finalize_write(oid, stream);
+
stream->free(stream);
return error;
}
@@ -117,8 +109,7 @@ static int write_file_filtered(
git_buf source = GIT_BUF_INIT;
git_buf dest = GIT_BUF_INIT;
- error = git_futils_readbuffer(&source, full_path);
- if (error < GIT_SUCCESS)
+ if ((error = git_futils_readbuffer(&source, full_path)) < 0)
return error;
error = git_filters_apply(&dest, &source, filters);
@@ -127,40 +118,39 @@ static int write_file_filtered(
* and we don't want to ODB write to choke */
git_buf_free(&source);
- if (error == GIT_SUCCESS) {
- /* Write the file to disk if it was properly filtered */
+ /* Write the file to disk if it was properly filtered */
+ if (!error)
error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
- }
git_buf_free(&dest);
- return GIT_SUCCESS;
+ return error;
}
-static int write_symlink(git_oid *oid, git_odb *odb, const char *path, size_t link_size)
+static int write_symlink(
+ git_oid *oid, git_odb *odb, const char *path, size_t link_size)
{
char *link_data;
ssize_t read_len;
int error;
link_data = git__malloc(link_size);
- if (!link_data)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(link_data);
read_len = p_readlink(path, link_data, link_size);
-
if (read_len != (ssize_t)link_size) {
- free(link_data);
- return git__throw(GIT_EOSERR, "Failed to create blob. Can't read symlink");
+ giterr_set(GITERR_OS, "Failed to create blob. Can't read symlink '%s'", path);
+ git__free(link_data);
+ return -1;
}
error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
- free(link_data);
+ git__free(link_data);
return error;
}
int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
{
- int error = GIT_SUCCESS;
+ int error;
git_buf full_path = GIT_BUF_INIT;
git_off_t size;
struct stat st;
@@ -168,25 +158,18 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
git_odb *odb = NULL;
workdir = git_repository_workdir(repo);
- if (workdir == NULL)
- return git__throw(GIT_ENOTFOUND, "Failed to create blob. (No working directory found)");
+ assert(workdir); /* error to call this on bare repo */
- error = git_buf_joinpath(&full_path, workdir, path);
- if (error < GIT_SUCCESS)
+ if ((error = git_buf_joinpath(&full_path, workdir, path)) < 0 ||
+ (error = git_path_lstat(full_path.ptr, &st)) < 0 ||
+ (error = git_repository_odb__weakptr(&odb, repo)) < 0)
+ {
+ git_buf_free(&full_path);
return error;
-
- error = p_lstat(full_path.ptr, &st);
- if (error < 0) {
- error = git__throw(GIT_EOSERR, "Failed to stat blob. %s", strerror(errno));
- goto cleanup;
}
size = st.st_size;
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
if (S_ISLNK(st.st_mode)) {
error = write_symlink(oid, odb, full_path.ptr, (size_t)size);
} else {
@@ -194,12 +177,12 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
int filter_count;
/* Load the filters for writing this file to the ODB */
- filter_count = git_filters_load(&write_filters, repo, path, GIT_FILTER_TO_ODB);
+ filter_count = git_filters_load(
+ &write_filters, repo, path, GIT_FILTER_TO_ODB);
if (filter_count < 0) {
/* Negative value means there was a critical error */
error = filter_count;
- goto cleanup;
} else if (filter_count == 0) {
/* No filters need to be applied to the document: we can stream
* directly from disk */
@@ -212,19 +195,20 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
git_filters_free(&write_filters);
/*
- * TODO: eventually support streaming filtered files, for files which are bigger
- * than a given threshold. This is not a priority because applying a filter in
- * streaming mode changes the final size of the blob, and without knowing its
- * final size, the blob cannot be written in stream mode to the ODB.
+ * TODO: eventually support streaming filtered files, for files
+ * which are bigger than a given threshold. This is not a priority
+ * because applying a filter in streaming mode changes the final
+ * size of the blob, and without knowing its final size, the blob
+ * cannot be written in stream mode to the ODB.
*
- * The plan is to do streaming writes to a tempfile on disk and then opening
- * streaming that file to the ODB, using `write_file_stream`.
+ * The plan is to do streaming writes to a tempfile on disk and then
+ * opening streaming that file to the ODB, using
+ * `write_file_stream`.
*
* CAREFULLY DESIGNED APIS YO
*/
}
-cleanup:
git_buf_free(&full_path);
return error;
}
diff --git a/src/branch.c b/src/branch.c
new file mode 100644
index 000000000..c980cf08c
--- /dev/null
+++ b/src/branch.c
@@ -0,0 +1,208 @@
+/*
+ * 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 "common.h"
+#include "commit.h"
+#include "branch.h"
+#include "tag.h"
+
+static int retrieve_branch_reference(
+ git_reference **branch_reference_out,
+ git_repository *repo,
+ const char *branch_name,
+ int is_remote)
+{
+ git_reference *branch;
+ int error = -1;
+ char *prefix;
+ git_buf ref_name = GIT_BUF_INIT;
+
+ *branch_reference_out = NULL;
+
+ prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
+
+ if (git_buf_joinpath(&ref_name, prefix, branch_name) < 0)
+ goto cleanup;
+
+ if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) {
+ giterr_set(GITERR_REFERENCE,
+ "Cannot locate %s branch '%s'.", is_remote ? "remote-tracking" : "local", branch_name);
+ goto cleanup;
+ }
+
+ *branch_reference_out = branch;
+
+cleanup:
+ git_buf_free(&ref_name);
+ return error;
+}
+
+static int create_error_invalid(const char *msg)
+{
+ giterr_set(GITERR_INVALID, "Cannot create branch - %s", msg);
+ return -1;
+}
+
+int git_branch_create(
+ git_oid *oid_out,
+ git_repository *repo,
+ const char *branch_name,
+ const git_object *target,
+ int force)
+{
+ git_otype target_type = GIT_OBJ_BAD;
+ git_object *commit = NULL;
+ git_reference *branch = NULL;
+ git_buf canonical_branch_name = GIT_BUF_INIT;
+ int error = -1;
+
+ assert(repo && branch_name && target && oid_out);
+
+ if (git_object_owner(target) != repo)
+ return create_error_invalid("The given target does not belong to this repository");
+
+ target_type = git_object_type(target);
+
+ switch (target_type)
+ {
+ case GIT_OBJ_TAG:
+ if (git_tag_peel(&commit, (git_tag *)target) < 0)
+ goto cleanup;
+
+ if (git_object_type(commit) != GIT_OBJ_COMMIT) {
+ create_error_invalid("The given target does not resolve to a commit");
+ goto cleanup;
+ }
+ break;
+
+ case GIT_OBJ_COMMIT:
+ commit = (git_object *)target;
+ break;
+
+ default:
+ return create_error_invalid("Only git_tag and git_commit objects are valid targets.");
+ }
+
+ if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
+ goto cleanup;
+
+ if (git_reference_create_oid(&branch, repo, git_buf_cstr(&canonical_branch_name), git_object_id(commit), force) < 0)
+ goto cleanup;
+
+ git_oid_cpy(oid_out, git_reference_oid(branch));
+ error = 0;
+
+cleanup:
+ if (target_type == GIT_OBJ_TAG)
+ git_object_free(commit);
+
+ git_reference_free(branch);
+ git_buf_free(&canonical_branch_name);
+ return error;
+}
+
+int git_branch_delete(git_repository *repo, const char *branch_name, git_branch_type branch_type)
+{
+ git_reference *branch = NULL;
+ git_reference *head = NULL;
+ int error;
+
+ assert((branch_type == GIT_BRANCH_LOCAL) || (branch_type == GIT_BRANCH_REMOTE));
+
+ if ((error = retrieve_branch_reference(&branch, repo, branch_name, branch_type == GIT_BRANCH_REMOTE)) < 0)
+ goto on_error;
+
+ if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
+ giterr_set(GITERR_REFERENCE, "Cannot locate HEAD.");
+ goto on_error;
+ }
+
+ if ((git_reference_type(head) == GIT_REF_SYMBOLIC)
+ && (strcmp(git_reference_target(head), git_reference_name(branch)) == 0)) {
+ giterr_set(GITERR_REFERENCE,
+ "Cannot delete branch '%s' as it is the current HEAD of the repository.", branch_name);
+ goto on_error;
+ }
+
+ if (git_reference_delete(branch) < 0)
+ goto on_error;
+
+ git_reference_free(head);
+ return 0;
+
+on_error:
+ git_reference_free(head);
+ git_reference_free(branch);
+ return -1;
+}
+
+typedef struct {
+ git_vector *branchlist;
+ unsigned int branch_type;
+} branch_filter_data;
+
+static int branch_list_cb(const char *branch_name, void *payload)
+{
+ branch_filter_data *filter = (branch_filter_data *)payload;
+
+ if ((filter->branch_type & GIT_BRANCH_LOCAL && git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0)
+ || (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));
+
+ return 0;
+}
+
+int git_branch_list(git_strarray *branch_names, git_repository *repo, unsigned int list_flags)
+{
+ int error;
+ branch_filter_data filter;
+ git_vector branchlist;
+
+ assert(branch_names && repo);
+
+ if (git_vector_init(&branchlist, 8, NULL) < 0)
+ return -1;
+
+ filter.branchlist = &branchlist;
+ filter.branch_type = list_flags;
+
+ error = git_reference_foreach(repo, GIT_REF_OID|GIT_REF_PACKED, &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;
+}
+
+int git_branch_move(git_repository *repo, const char *old_branch_name, const char *new_branch_name, int force)
+{
+ git_reference *reference = NULL;
+ git_buf old_reference_name = GIT_BUF_INIT, new_reference_name = GIT_BUF_INIT;
+ int error = 0;
+
+ if ((error = git_buf_joinpath(&old_reference_name, GIT_REFS_HEADS_DIR, old_branch_name)) < 0)
+ goto cleanup;
+
+ /* We need to be able to return GIT_ENOTFOUND */
+ if ((error = git_reference_lookup(&reference, repo, git_buf_cstr(&old_reference_name))) < 0)
+ goto cleanup;
+
+ if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
+ goto cleanup;
+
+ error = git_reference_rename(reference, git_buf_cstr(&new_reference_name), force);
+
+cleanup:
+ git_reference_free(reference);
+ git_buf_free(&old_reference_name);
+ git_buf_free(&new_reference_name);
+
+ return error;
+}
diff --git a/src/branch.h b/src/branch.h
new file mode 100644
index 000000000..d0e5abc8b
--- /dev/null
+++ b/src/branch.h
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+#ifndef INCLUDE_branch_h__
+#define INCLUDE_branch_h__
+
+#include "git2/branch.h"
+
+struct git_branch {
+ char *remote; /* TODO: Make this a git_remote */
+ char *merge;
+};
+
+#endif
diff --git a/src/buffer.c b/src/buffer.c
index 3098f6d68..24a0abdbe 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -17,8 +17,8 @@ char git_buf_initbuf[1];
static char git_buf__oom;
#define ENSURE_SIZE(b, d) \
- if ((d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
- return GIT_ENOMEM;
+ if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\
+ return -1;
void git_buf_init(git_buf *buf, size_t initial_size)
@@ -34,10 +34,8 @@ void git_buf_init(git_buf *buf, size_t initial_size)
int git_buf_grow(git_buf *buf, size_t target_size)
{
int error = git_buf_try_grow(buf, target_size);
- if (error != GIT_SUCCESS) {
+ if (error != 0)
buf->ptr = &git_buf__oom;
- }
-
return error;
}
@@ -47,10 +45,10 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
size_t new_size;
if (buf->ptr == &git_buf__oom)
- return GIT_ENOMEM;
+ return -1;
if (target_size <= buf->asize)
- return GIT_SUCCESS;
+ return 0;
if (buf->asize == 0) {
new_size = target_size;
@@ -70,7 +68,7 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
new_ptr = git__realloc(new_ptr, new_size);
if (!new_ptr)
- return GIT_ENOMEM;
+ return -1;
buf->asize = new_size;
buf->ptr = new_ptr;
@@ -80,7 +78,7 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
buf->size = buf->asize - 1;
buf->ptr[buf->size] = '\0';
- return GIT_SUCCESS;
+ return 0;
}
void git_buf_free(git_buf *buf)
@@ -100,16 +98,11 @@ void git_buf_clear(git_buf *buf)
buf->ptr[0] = '\0';
}
-int git_buf_oom(const git_buf *buf)
+bool git_buf_oom(const git_buf *buf)
{
return (buf->ptr == &git_buf__oom);
}
-int git_buf_lasterror(const git_buf *buf)
-{
- return (buf->ptr == &git_buf__oom) ? GIT_ENOMEM : GIT_SUCCESS;
-}
-
int git_buf_set(git_buf *buf, const char *data, size_t len)
{
if (len == 0 || data == NULL) {
@@ -122,7 +115,7 @@ int git_buf_set(git_buf *buf, const char *data, size_t len)
buf->size = len;
buf->ptr[buf->size] = '\0';
}
- return GIT_SUCCESS;
+ return 0;
}
int git_buf_sets(git_buf *buf, const char *string)
@@ -135,7 +128,7 @@ int git_buf_putc(git_buf *buf, char c)
ENSURE_SIZE(buf, buf->size + 2);
buf->ptr[buf->size++] = c;
buf->ptr[buf->size] = '\0';
- return GIT_SUCCESS;
+ return 0;
}
int git_buf_put(git_buf *buf, const char *data, size_t len)
@@ -144,7 +137,7 @@ int git_buf_put(git_buf *buf, const char *data, size_t len)
memmove(buf->ptr + buf->size, data, len);
buf->size += len;
buf->ptr[buf->size] = '\0';
- return GIT_SUCCESS;
+ return 0;
}
int git_buf_puts(git_buf *buf, const char *string)
@@ -166,9 +159,9 @@ int git_buf_printf(git_buf *buf, const char *format, ...)
va_end(arglist);
if (len < 0) {
- free(buf->ptr);
+ git__free(buf->ptr);
buf->ptr = &git_buf__oom;
- return GIT_ENOMEM;
+ return -1;
}
if ((size_t)len + 1 <= buf->asize - buf->size) {
@@ -179,7 +172,7 @@ int git_buf_printf(git_buf *buf, const char *format, ...)
ENSURE_SIZE(buf, buf->size + len + 1);
}
- return GIT_SUCCESS;
+ return 0;
}
void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
@@ -220,8 +213,8 @@ void git_buf_truncate(git_buf *buf, size_t len)
void git_buf_rtruncate_at_char(git_buf *buf, char separator)
{
- int idx = git_buf_rfind_next(buf, separator);
- git_buf_truncate(buf, idx < 0 ? 0 : idx);
+ ssize_t idx = git_buf_rfind_next(buf, separator);
+ git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);
}
void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
@@ -262,9 +255,9 @@ void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
{
va_list ap;
- int i, error = GIT_SUCCESS;
- size_t total_size = 0;
- char *out;
+ int i;
+ size_t total_size = 0, original_size = buf->size;
+ char *out, *original = buf->ptr;
if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
++total_size; /* space for initial separator */
@@ -288,9 +281,10 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
va_end(ap);
/* expand buffer if needed */
- if (total_size > 0 &&
- (error = git_buf_grow(buf, buf->size + total_size + 1)) < GIT_SUCCESS)
- return error;
+ if (total_size == 0)
+ return 0;
+ if (git_buf_grow(buf, buf->size + total_size + 1) < 0)
+ return -1;
out = buf->ptr + buf->size;
@@ -307,12 +301,23 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
if (!segment)
continue;
+ /* deal with join that references buffer's original content */
+ if (segment >= original && segment < original + original_size) {
+ size_t offset = (segment - original);
+ segment = buf->ptr + offset;
+ segment_len = original_size - offset;
+ } else {
+ segment_len = strlen(segment);
+ }
+
/* skip leading separators */
if (out > buf->ptr && out[-1] == separator)
- while (*segment == separator) segment++;
+ while (segment_len > 0 && *segment == separator) {
+ segment++;
+ segment_len--;
+ }
/* copy over next buffer */
- segment_len = strlen(segment);
if (segment_len > 0) {
memmove(out, segment, segment_len);
out += segment_len;
@@ -328,7 +333,7 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
buf->size = out - buf->ptr;
buf->ptr[buf->size] = '\0';
- return error;
+ return 0;
}
int git_buf_join(
@@ -337,7 +342,6 @@ int git_buf_join(
const char *str_a,
const char *str_b)
{
- int error = GIT_SUCCESS;
size_t strlen_a = str_a ? strlen(str_a) : 0;
size_t strlen_b = strlen(str_b);
int need_sep = 0;
@@ -357,9 +361,8 @@ int git_buf_join(
if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
offset_a = str_a - buf->ptr;
- error = git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0)
+ return -1;
/* fix up internal pointers */
if (offset_a >= 0)
@@ -375,7 +378,7 @@ int git_buf_join(
buf->size = strlen_a + strlen_b + need_sep;
buf->ptr[buf->size] = '\0';
- return error;
+ return 0;
}
void git_buf_rtrim(git_buf *buf)
diff --git a/src/buffer.h b/src/buffer.h
index 3cdd794af..1cf588a62 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -32,7 +32,7 @@ void git_buf_init(git_buf *buf, size_t initial_size);
* If the allocation fails, this will return an error and the buffer
* will be marked as invalid for future operations. The existing
* contents of the buffer will be preserved however.
- * @return GIT_SUCCESS or GIT_ENOMEM on failure
+ * @return 0 on success or -1 on failure
*/
int git_buf_grow(git_buf *buf, size_t target_size);
@@ -57,23 +57,18 @@ void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
* further calls to modify the buffer will fail. Check git_buf_oom() at the
* end of your sequence and it will be true if you ran out of memory at any
* point with that buffer.
- * @return 0 if no error, 1 if allocation error.
- */
-int git_buf_oom(const git_buf *buf);
-
-/**
- * Just like git_buf_oom, except returns appropriate error code.
- * @return GIT_ENOMEM if allocation error, GIT_SUCCESS if not.
+ *
+ * @return false if no error, true if allocation error
*/
-int git_buf_lasterror(const git_buf *buf);
+bool git_buf_oom(const git_buf *buf);
/*
- * The functions below that return int values, will return GIT_ENOMEM
- * if they fail to expand the git_buf when they are called, otherwise
- * GIT_SUCCESS. Passing a git_buf that has failed an allocation will
- * automatically return GIT_ENOMEM for all further calls. As a result,
- * you can ignore the return code of these functions and call them in a
- * series then just call git_buf_lasterror at the end.
+ * Functions below that return int value error codes will return 0 on
+ * success or -1 on failure (which generally means an allocation failed).
+ * Using a git_buf where the allocation has failed with result in -1 from
+ * all further calls using that buffer. As a result, you can ignore the
+ * return code of these functions and call them in a series then just call
+ * git_buf_oom at the end.
*/
int git_buf_set(git_buf *buf, const char *data, size_t len);
int git_buf_sets(git_buf *buf, const char *string);
@@ -91,25 +86,30 @@ int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *st
/**
* Join two strings as paths, inserting a slash between as needed.
- * @return error code or GIT_SUCCESS
+ * @return 0 on success, -1 on failure
*/
GIT_INLINE(int) git_buf_joinpath(git_buf *buf, const char *a, const char *b)
{
return git_buf_join(buf, '/', a, b);
}
-GIT_INLINE(const char *) git_buf_cstr(git_buf *buf)
+GIT_INLINE(const char *) git_buf_cstr(const git_buf *buf)
{
return buf->ptr;
}
+GIT_INLINE(size_t) git_buf_len(const git_buf *buf)
+{
+ return buf->size;
+}
+
void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf);
#define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)
-GIT_INLINE(int) git_buf_rfind_next(git_buf *buf, char ch)
+GIT_INLINE(ssize_t) git_buf_rfind_next(git_buf *buf, char ch)
{
- int idx = buf->size - 1;
+ ssize_t idx = (ssize_t)buf->size - 1;
while (idx >= 0 && buf->ptr[idx] == ch) idx--;
while (idx >= 0 && buf->ptr[idx] != ch) idx--;
return idx;
diff --git a/src/cache.c b/src/cache.c
index 9e566792a..31da3c36e 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -9,21 +9,14 @@
#include "repository.h"
#include "commit.h"
#include "thread-utils.h"
+#include "util.h"
#include "cache.h"
int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
{
if (size < 8)
size = 8;
-
- /* round up size to closest power of 2 */
- size--;
- size |= size >> 1;
- size |= size >> 2;
- size |= size >> 4;
- size |= size >> 8;
- size |= size >> 16;
- size++;
+ size = git__size_t_powerof2(size);
cache->size_mask = size - 1;
cache->lru_count = 0;
@@ -32,11 +25,10 @@ int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_pt
git_mutex_init(&cache->lock);
cache->nodes = git__malloc(size * sizeof(git_cached_obj *));
- if (cache->nodes == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(cache->nodes);
memset(cache->nodes, 0x0, size * sizeof(git_cached_obj *));
- return GIT_SUCCESS;
+ return 0;
}
void git_cache_free(git_cache *cache)
diff --git a/src/cc-compat.h b/src/cc-compat.h
index 3df36b61f..507985daa 100644
--- a/src/cc-compat.h
+++ b/src/cc-compat.h
@@ -50,4 +50,12 @@
# pragma warning ( disable : 4127 )
#endif
+#if defined (_MSC_VER)
+ typedef unsigned char bool;
+# define true 1
+# define false 0
+#else
+# include <stdbool.h>
+#endif
+
#endif /* INCLUDE_compat_h__ */
diff --git a/src/commit.c b/src/commit.c
index 189d8fe9e..04f37fe16 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -69,24 +69,84 @@ int git_commit_create_v(
...)
{
va_list ap;
- int i, error;
+ int i, res;
const git_commit **parents;
parents = git__malloc(parent_count * sizeof(git_commit *));
+ GITERR_CHECK_ALLOC(parents);
va_start(ap, parent_count);
for (i = 0; i < parent_count; ++i)
parents[i] = va_arg(ap, const git_commit *);
va_end(ap);
- error = git_commit_create(
+ res = git_commit_create(
oid, repo, update_ref, author, committer,
message_encoding, message,
tree, parent_count, parents);
git__free((void *)parents);
+ return res;
+}
+
+/* Update the reference named `ref_name` so it points to `oid` */
+static int update_reference(git_repository *repo, git_oid *oid, const char *ref_name)
+{
+ git_reference *ref;
+ int res;
+
+ res = git_reference_lookup(&ref, repo, ref_name);
+
+ /* If we haven't found the reference at all, we assume we need to create
+ * a new reference and that's it */
+ if (res == GIT_ENOTFOUND) {
+ giterr_clear();
+ return git_reference_create_oid(NULL, repo, ref_name, oid, 1);
+ }
+
+ if (res < 0)
+ return -1;
+
+ /* If we have found a reference, but it's symbolic, we need to update
+ * the direct reference it points to */
+ if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
+ git_reference *aux;
+ const char *sym_target;
+
+ /* The target pointed at by this reference */
+ sym_target = git_reference_target(ref);
+
+ /* resolve the reference to the target it points to */
+ res = git_reference_resolve(&aux, ref);
- return error;
+ /*
+ * if the symbolic reference pointed to an inexisting ref,
+ * this is means we're creating a new branch, for example.
+ * We need to create a new direct reference with that name
+ */
+ if (res == GIT_ENOTFOUND) {
+ giterr_clear();
+ res = git_reference_create_oid(NULL, repo, sym_target, oid, 1);
+ git_reference_free(ref);
+ return res;
+ }
+
+ /* free the original symbolic reference now; not before because
+ * we're using the `sym_target` pointer */
+ git_reference_free(ref);
+
+ if (res < 0)
+ return -1;
+
+ /* store the newly found direct reference in its place */
+ ref = aux;
+ }
+
+ /* ref is made to point to `oid`: ref is either the original reference,
+ * or the target of the symbolic reference we've looked up */
+ res = git_reference_set_oid(ref, oid);
+ git_reference_free(ref);
+ return res;
}
int git_commit_create(
@@ -102,20 +162,15 @@ int git_commit_create(
const git_commit *parents[])
{
git_buf commit = GIT_BUF_INIT;
- int error, i;
+ int i;
git_odb *odb;
- if (git_object_owner((const git_object *)tree) != repo)
- return git__throw(GIT_EINVALIDARGS, "The given tree does not belong to this repository");
+ assert(git_object_owner((const git_object *)tree) == repo);
git_oid__writebuf(&commit, "tree ", git_object_id((const git_object *)tree));
for (i = 0; i < parent_count; ++i) {
- if (git_object_owner((const git_object *)parents[i]) != repo) {
- error = git__throw(GIT_EINVALIDARGS, "The given parent does not belong to this repository");
- goto cleanup;
- }
-
+ assert(git_object_owner((const git_object *)parents[i]) == repo);
git_oid__writebuf(&commit, "parent ", git_object_id((const git_object *)parents[i]));
}
@@ -128,67 +183,25 @@ int git_commit_create(
git_buf_putc(&commit, '\n');
git_buf_puts(&commit, message);
- if (git_buf_oom(&commit)) {
- error = git__throw(git_buf_lasterror(&commit),
- "Not enough memory to build the commit data");
- goto cleanup;
- }
-
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT);
- git_buf_free(&commit);
-
- if (error == GIT_SUCCESS && update_ref != NULL) {
- git_reference *head;
- git_reference *target;
-
- error = git_reference_lookup(&head, repo, update_ref);
- if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
- return git__rethrow(error, "Failed to create commit");
-
- if (error != GIT_ENOTFOUND) {
- update_ref = git_reference_target(head);
- error = git_reference_resolve(&target, head);
- }
-
- if (error < GIT_SUCCESS) {
- if (error != GIT_ENOTFOUND) {
- git_reference_free(head);
- return git__rethrow(error, "Failed to create commit");
- }
- /*
- * The target of the reference was not found. This can happen
- * just after a repository has been initialized (the master
- * branch doesn't exist yet, as it doesn't have anything to
- * point to) or after an orphan checkout, so if the target
- * branch doesn't exist yet, create it and return.
- */
- error = git_reference_create_oid(&target, repo, update_ref, oid, 1);
-
- git_reference_free(head);
- if (error == GIT_SUCCESS)
- git_reference_free(target);
+ if (git_buf_oom(&commit))
+ goto on_error;
- return error;
- }
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ goto on_error;
- error = git_reference_set_oid(target, oid);
+ if (git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0)
+ goto on_error;
- git_reference_free(head);
- git_reference_free(target);
- }
+ git_buf_free(&commit);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create commit");
+ if (update_ref != NULL)
+ return update_reference(repo, oid, update_ref);
- return GIT_SUCCESS;
+ return 0;
-cleanup:
+on_error:
git_buf_free(&commit);
- return error;
+ return -1;
}
int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
@@ -197,35 +210,40 @@ int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
const char *buffer_end = (const char *)data + len;
git_oid parent_oid;
- int error;
git_vector_init(&commit->parent_oids, 4, NULL);
- if ((error = git_oid__parse(&commit->tree_oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to parse buffer");
+ if (git_oid__parse(&commit->tree_oid, &buffer, buffer_end, "tree ") < 0)
+ goto bad_buffer;
/*
* TODO: commit grafts!
*/
- while (git_oid__parse(&parent_oid, &buffer, buffer_end, "parent ") == GIT_SUCCESS) {
+ while (git_oid__parse(&parent_oid, &buffer, buffer_end, "parent ") == 0) {
git_oid *new_oid;
new_oid = git__malloc(sizeof(git_oid));
+ GITERR_CHECK_ALLOC(new_oid);
+
git_oid_cpy(new_oid, &parent_oid);
- if (git_vector_insert(&commit->parent_oids, new_oid) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (git_vector_insert(&commit->parent_oids, new_oid) < 0)
+ return -1;
}
commit->author = git__malloc(sizeof(git_signature));
- if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n')) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to parse commit");
+ GITERR_CHECK_ALLOC(commit->author);
+
+ if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
+ return -1;
/* Always parse the committer; we need the commit time */
commit->committer = git__malloc(sizeof(git_signature));
- if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n')) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to parse commit");
+ GITERR_CHECK_ALLOC(commit->committer);
+
+ if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
+ return -1;
if (git__prefixcmp(buffer, "encoding ") == 0) {
const char *encoding_end;
@@ -236,8 +254,7 @@ int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
encoding_end++;
commit->message_encoding = git__strndup(buffer, encoding_end - buffer);
- if (!commit->message_encoding)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(commit->message_encoding);
buffer = encoding_end;
}
@@ -248,11 +265,14 @@ int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
if (buffer <= buffer_end) {
commit->message = git__strndup(buffer, buffer_end - buffer);
- if (!commit->message)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(commit->message);
}
- return GIT_SUCCESS;
+ return 0;
+
+bad_buffer:
+ giterr_set(GITERR_OBJECT, "Failed to parse bad commit object");
+ return -1;
}
int git_commit__parse(git_commit *commit, git_odb_object *obj)
@@ -290,8 +310,10 @@ int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
assert(commit);
parent_oid = git_vector_get(&commit->parent_oids, n);
- if (parent_oid == NULL)
- return git__throw(GIT_ENOTFOUND, "Parent %u does not exist", n);
+ if (parent_oid == NULL) {
+ giterr_set(GITERR_INVALID, "Parent %u does not exist", n);
+ return GIT_ENOTFOUND;
+ }
return git_commit_lookup(parent, commit->object.repo, parent_oid);
}
diff --git a/src/common.h b/src/common.h
index 0b4dc2e49..30757de70 100644
--- a/src/common.h
+++ b/src/common.h
@@ -46,6 +46,8 @@
#include "thread-utils.h"
#include "bswap.h"
+#include <regex.h>
+
extern void git___throw(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
#define git__throw(error, ...) \
(git___throw(__VA_ARGS__), error)
@@ -54,6 +56,16 @@ extern void git___rethrow(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
#define git__rethrow(error, ...) \
(git___rethrow(__VA_ARGS__), error)
+
+#define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; }
+
+void giterr_set_oom(void);
+void giterr_set(int error_class, const char *string, ...);
+void giterr_clear(void);
+void giterr_set_str(int error_class, const char *string);
+void giterr_set_regex(const regex_t *regex, int error_code);
+
+
#include "util.h"
diff --git a/src/config.c b/src/config.c
index 912224158..4c971924c 100644
--- a/src/config.c
+++ b/src/config.c
@@ -7,7 +7,6 @@
#include "common.h"
#include "fileops.h"
-#include "hashtable.h"
#include "config.h"
#include "git2/config.h"
#include "vector.h"
@@ -67,77 +66,71 @@ int git_config_new(git_config **out)
if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) {
git__free(cfg);
- return GIT_ENOMEM;
+ return -1;
}
*out = cfg;
GIT_REFCOUNT_INC(cfg);
- return GIT_SUCCESS;
+ return 0;
}
int git_config_add_file_ondisk(git_config *cfg, const char *path, int priority)
{
git_config_file *file = NULL;
- int error;
- error = git_config_file__ondisk(&file, path);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_config_file__ondisk(&file, path) < 0)
+ return -1;
- error = git_config_add_file(cfg, file, priority);
- if (error < GIT_SUCCESS) {
+ if (git_config_add_file(cfg, file, priority) < 0) {
/*
* free manually; the file is not owned by the config
* instance yet and will not be freed on cleanup
*/
file->free(file);
- return error;
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
int git_config_open_ondisk(git_config **cfg, const char *path)
{
- int error;
-
- error = git_config_new(cfg);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_config_new(cfg) < 0)
+ return -1;
- error = git_config_add_file_ondisk(*cfg, path, 1);
- if (error < GIT_SUCCESS)
+ if (git_config_add_file_ondisk(*cfg, path, 1) < 0) {
git_config_free(*cfg);
+ return -1;
+ }
- return error;
+ return 0;
}
int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
{
file_internal *internal;
- int error;
+ int result;
assert(cfg && file);
- if ((error = file->open(file)) < GIT_SUCCESS)
- return git__throw(error, "Failed to open config file");
+ if ((result = file->open(file)) < 0)
+ return result;
internal = git__malloc(sizeof(file_internal));
- if (internal == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(internal);
internal->file = file;
internal->priority = priority;
if (git_vector_insert(&cfg->files, internal) < 0) {
git__free(internal);
- return GIT_ENOMEM;
+ return -1;
}
git_vector_sort(&cfg->files);
internal->file->cfg = cfg;
- return GIT_SUCCESS;
+ return 0;
}
/*
@@ -146,7 +139,7 @@ int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data)
{
- int ret = GIT_SUCCESS;
+ int ret = 0;
unsigned int i;
file_internal *internal;
git_config_file *file;
@@ -165,8 +158,7 @@ int git_config_delete(git_config *cfg, const char *name)
file_internal *internal;
git_config_file *file;
- if (cfg->files.length == 0)
- return git__throw(GIT_EINVALIDARGS, "Cannot delete variable; no files open in the `git_config` instance");
+ assert(cfg->files.length);
internal = git_vector_get(&cfg->files, 0);
file = internal->file;
@@ -200,8 +192,7 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
file_internal *internal;
git_config_file *file;
- if (cfg->files.length == 0)
- return git__throw(GIT_EINVALIDARGS, "Cannot set variable value; no files open in the `git_config` instance");
+ assert(cfg->files.length);
internal = git_vector_get(&cfg->files, 0);
file = internal->file;
@@ -209,25 +200,25 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
return file->set(file, name, value);
}
-static int parse_bool(int *out, const char *value)
+int git_config_parse_bool(int *out, const char *value)
{
/* A missing value means true */
if (value == NULL) {
*out = 1;
- return GIT_SUCCESS;
+ return 0;
}
if (!strcasecmp(value, "true") ||
!strcasecmp(value, "yes") ||
!strcasecmp(value, "on")) {
*out = 1;
- return GIT_SUCCESS;
+ return 0;
}
if (!strcasecmp(value, "false") ||
!strcasecmp(value, "no") ||
!strcasecmp(value, "off")) {
*out = 0;
- return GIT_SUCCESS;
+ return 0;
}
return GIT_EINVALIDTYPE;
@@ -239,7 +230,7 @@ static int parse_int64(int64_t *out, const char *value)
int64_t num;
if (git__strtol64(&num, value, &num_end, 0) < 0)
- return GIT_EINVALIDTYPE;
+ return -1;
switch (*num_end) {
case 'g':
@@ -259,7 +250,7 @@ static int parse_int64(int64_t *out, const char *value)
/* check that that there are no more characters after the
* given modifier suffix */
if (num_end[1] != '\0')
- return GIT_EINVALIDTYPE;
+ return -1;
/* fallthrough */
@@ -268,7 +259,7 @@ static int parse_int64(int64_t *out, const char *value)
return 0;
default:
- return GIT_EINVALIDTYPE;
+ return -1;
}
}
@@ -278,11 +269,11 @@ static int parse_int32(int32_t *out, const char *value)
int32_t truncate;
if (parse_int64(&tmp, value) < 0)
- return GIT_EINVALIDTYPE;
+ return -1;
truncate = tmp & 0xFFFFFFFF;
if (truncate != tmp)
- return GIT_EOVERFLOW;
+ return -1;
*out = truncate;
return 0;
@@ -291,49 +282,62 @@ static int parse_int32(int32_t *out, const char *value)
/***********
* Getters
***********/
-int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out)
+int git_config_lookup_map_value(
+ git_cvar_map *maps, size_t map_n, const char *value, int *out)
{
size_t i;
- const char *value;
- int error;
- error = git_config_get_string(cfg, name, &value);
- if (error < GIT_SUCCESS)
- return error;
+ if (!value)
+ return GIT_ENOTFOUND;
for (i = 0; i < map_n; ++i) {
git_cvar_map *m = maps + i;
switch (m->cvar_type) {
- case GIT_CVAR_FALSE:
- case GIT_CVAR_TRUE: {
- int bool_val;
+ case GIT_CVAR_FALSE:
+ case GIT_CVAR_TRUE: {
+ int bool_val;
+
+ if (git_config_parse_bool(&bool_val, value) == 0 &&
+ bool_val == (int)m->cvar_type) {
+ *out = m->map_value;
+ return 0;
+ }
+ break;
+ }
- if (parse_bool(&bool_val, value) == 0 &&
- bool_val == (int)m->cvar_type) {
- *out = m->map_value;
- return 0;
- }
+ case GIT_CVAR_INT32:
+ if (parse_int32(out, value) == 0)
+ return 0;
+ break;
- break;
+ case GIT_CVAR_STRING:
+ if (strcasecmp(value, m->str_match) == 0) {
+ *out = m->map_value;
+ return 0;
}
+ break;
+ }
+ }
- case GIT_CVAR_INT32:
- if (parse_int32(out, value) == 0)
- return 0;
+ return GIT_ENOTFOUND;
+}
- break;
+int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out)
+{
+ const char *value;
+ int ret;
- case GIT_CVAR_STRING:
- if (strcasecmp(value, m->str_match) == 0) {
- *out = m->map_value;
- return 0;
- }
- }
- }
+ ret = git_config_get_string(cfg, name, &value);
+ if (ret < 0)
+ return ret;
+
+ if (!git_config_lookup_map_value(maps, map_n, value, out))
+ return 0;
- return git__throw(GIT_ENOTFOUND,
+ giterr_set(GITERR_CONFIG,
"Failed to map the '%s' config variable with a valid value", name);
+ return -1;
}
int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
@@ -342,69 +346,73 @@ int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
int ret;
ret = git_config_get_string(cfg, name, &value);
- if (ret < GIT_SUCCESS)
- return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
+ if (ret < 0)
+ return ret;
- if (parse_int64(out, value) < 0)
- return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as an integer", value);
+ if (parse_int64(out, value) < 0) {
+ giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value);
+ return -1;
+ }
- return GIT_SUCCESS;
+ return 0;
}
int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
{
const char *value;
- int error;
+ int ret;
- error = git_config_get_string(cfg, name, &value);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to get value for %s", name);
+ ret = git_config_get_string(cfg, name, &value);
+ if (ret < 0)
+ return ret;
+
+ if (parse_int32(out, value) < 0) {
+ giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value);
+ return -1;
+ }
- error = parse_int32(out, value);
- if (error < GIT_SUCCESS)
- return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as a 32-bit integer", value);
-
- return GIT_SUCCESS;
+ return 0;
}
int git_config_get_bool(git_config *cfg, const char *name, int *out)
{
const char *value;
- int error = GIT_SUCCESS;
+ int ret;
- error = git_config_get_string(cfg, name, &value);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to get value for %s", name);
+ ret = git_config_get_string(cfg, name, &value);
+ if (ret < 0)
+ return ret;
- if (parse_bool(out, value) == 0)
- return GIT_SUCCESS;
+ if (git_config_parse_bool(out, value) == 0)
+ return 0;
if (parse_int32(out, value) == 0) {
*out = !!(*out);
- return GIT_SUCCESS;
+ return 0;
}
- return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as a boolean value", value);
+ giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value);
+ return -1;
}
int git_config_get_string(git_config *cfg, const char *name, const char **out)
{
file_internal *internal;
- git_config_file *file;
- int error = GIT_ENOTFOUND;
unsigned int i;
- if (cfg->files.length == 0)
- return git__throw(GIT_EINVALIDARGS, "Cannot get variable value; no files open in the `git_config` instance");
+ assert(cfg->files.length);
- for (i = 0; i < cfg->files.length; ++i) {
- internal = git_vector_get(&cfg->files, i);
- file = internal->file;
- if ((error = file->get(file, name, out)) == GIT_SUCCESS)
- return GIT_SUCCESS;
+ *out = NULL;
+
+ git_vector_foreach(&cfg->files, i, internal) {
+ git_config_file *file = internal->file;
+ int ret = file->get(file, name, out);
+ if (ret != GIT_ENOTFOUND)
+ return ret;
}
- return git__throw(error, "Config value '%s' not found", name);
+ giterr_set(GITERR_CONFIG, "Config variable '%s' not found", name);
+ return GIT_ENOTFOUND;
}
int git_config_get_multivar(git_config *cfg, const char *name, const char *regexp,
@@ -412,12 +420,10 @@ int git_config_get_multivar(git_config *cfg, const char *name, const char *regex
{
file_internal *internal;
git_config_file *file;
- int error = GIT_ENOTFOUND;
+ int ret = GIT_ENOTFOUND;
unsigned int i;
-
- if (cfg->files.length == 0)
- return git__throw(GIT_EINVALIDARGS, "Cannot get variable value; no files open in the `git_config` instance");
+ assert(cfg->files.length);
/*
* This loop runs the "wrong" way 'round because we need to
@@ -426,30 +432,30 @@ int git_config_get_multivar(git_config *cfg, const char *name, const char *regex
for (i = cfg->files.length; i > 0; --i) {
internal = git_vector_get(&cfg->files, i - 1);
file = internal->file;
- error = file->get_multivar(file, name, regexp, fn, data);
- if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
- git__rethrow(error, "Failed to get multivar");
+ ret = file->get_multivar(file, name, regexp, fn, data);
+ if (ret < 0 && ret != GIT_ENOTFOUND)
+ return ret;
}
- return GIT_SUCCESS;
+ return 0;
}
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
{
file_internal *internal;
git_config_file *file;
- int error = GIT_ENOTFOUND;
+ int ret = GIT_ENOTFOUND;
unsigned int i;
for (i = cfg->files.length; i > 0; --i) {
internal = git_vector_get(&cfg->files, i - 1);
file = internal->file;
- error = file->set_multivar(file, name, regexp, value);
- if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
- git__rethrow(error, "Failed to replace multivar");
+ ret = file->set_multivar(file, name, regexp, value);
+ if (ret < 0 && ret != GIT_ENOTFOUND)
+ return ret;
}
- return GIT_SUCCESS;
+ return 0;
}
int git_config_find_global_r(git_buf *path)
@@ -460,18 +466,23 @@ int git_config_find_global_r(git_buf *path)
int git_config_find_global(char *global_config_path)
{
git_buf path = GIT_BUF_INIT;
- int error = git_config_find_global_r(&path);
+ int ret = git_config_find_global_r(&path);
- if (error == GIT_SUCCESS) {
- if (path.size > GIT_PATH_MAX)
- error = git__throw(GIT_ESHORTBUFFER, "Path is too long");
- else
- git_buf_copy_cstr(global_config_path, GIT_PATH_MAX, &path);
+ if (ret < 0) {
+ git_buf_free(&path);
+ return ret;
}
- git_buf_free(&path);
+ if (path.size > GIT_PATH_MAX) {
+ git_buf_free(&path);
+ giterr_set(GITERR_NOMEMORY,
+ "Path is to long to fit on the given buffer");
+ return -1;
+ }
- return error;
+ git_buf_copy_cstr(global_config_path, GIT_PATH_MAX, &path);
+ git_buf_free(&path);
+ return 0;
}
int git_config_find_system_r(git_buf *path)
@@ -482,18 +493,23 @@ int git_config_find_system_r(git_buf *path)
int git_config_find_system(char *system_config_path)
{
git_buf path = GIT_BUF_INIT;
- int error = git_config_find_system_r(&path);
+ int ret = git_config_find_system_r(&path);
- if (error == GIT_SUCCESS) {
- if (path.size > GIT_PATH_MAX)
- error = git__throw(GIT_ESHORTBUFFER, "Path is too long");
- else
- git_buf_copy_cstr(system_config_path, GIT_PATH_MAX, &path);
+ if (ret < 0) {
+ git_buf_free(&path);
+ return ret;
}
- git_buf_free(&path);
+ if (path.size > GIT_PATH_MAX) {
+ git_buf_free(&path);
+ giterr_set(GITERR_NOMEMORY,
+ "Path is to long to fit on the given buffer");
+ return -1;
+ }
- return error;
+ git_buf_copy_cstr(system_config_path, GIT_PATH_MAX, &path);
+ git_buf_free(&path);
+ return 0;
}
int git_config_open_global(git_config **out)
@@ -501,7 +517,7 @@ int git_config_open_global(git_config **out)
int error;
char global_path[GIT_PATH_MAX];
- if ((error = git_config_find_global(global_path)) < GIT_SUCCESS)
+ if ((error = git_config_find_global(global_path)) < 0)
return error;
return git_config_open_ondisk(out, global_path);
diff --git a/src/config.h b/src/config.h
index 59d1d9a26..82e98ce51 100644
--- a/src/config.h
+++ b/src/config.h
@@ -25,4 +25,9 @@ struct git_config {
extern int git_config_find_global_r(git_buf *global_config_path);
extern int git_config_find_system_r(git_buf *system_config_path);
+extern int git_config_parse_bool(int *out, const char *bool_string);
+
+extern int git_config_lookup_map_value(
+ git_cvar_map *maps, size_t map_n, const char *value, int *out);
+
#endif
diff --git a/src/config_cache.c b/src/config_cache.c
index 5e20847f5..3679a9646 100644
--- a/src/config_cache.c
+++ b/src/config_cache.c
@@ -7,7 +7,6 @@
#include "common.h"
#include "fileops.h"
-#include "hashtable.h"
#include "config.h"
#include "git2/config.h"
#include "vector.h"
diff --git a/src/config_file.c b/src/config_file.c
index e1f4ef932..ed5caf980 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -12,12 +12,14 @@
#include "buffer.h"
#include "git2/config.h"
#include "git2/types.h"
-
+#include "strmap.h"
#include <ctype.h>
#include <sys/types.h>
#include <regex.h>
+GIT__USE_STRMAP;
+
typedef struct cvar_t {
struct cvar_t *next;
char *key; /* TODO: we might be able to get rid of this */
@@ -70,7 +72,7 @@ typedef struct {
typedef struct {
git_config_file parent;
- git_hashtable *values;
+ git_strmap *values;
struct {
git_buf buffer;
@@ -86,6 +88,12 @@ static int config_parse(diskfile_backend *cfg_file);
static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
+static void set_parse_error(diskfile_backend *backend, int col, const char *error_str)
+{
+ giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
+ error_str, backend->file_path, backend->reader.line_number, col);
+}
+
static void cvar_free(cvar_t *var)
{
if (var == NULL)
@@ -104,15 +112,16 @@ static int normalize_name(const char *in, char **out)
assert(in && out);
name = git__strdup(in);
- if (name == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(name);
fdot = strchr(name, '.');
ldot = strrchr(name, '.');
if (fdot == NULL || ldot == NULL) {
git__free(name);
- return git__throw(GIT_EINVALIDARGS, "Bad format. No dot in '%s'", in);
+ giterr_set(GITERR_CONFIG,
+ "Invalid variable name: '%s'", in);
+ return -1;
}
/* Downcase up to the first dot and after the last one */
@@ -120,60 +129,50 @@ static int normalize_name(const char *in, char **out)
git__strtolower(ldot);
*out = name;
- return GIT_SUCCESS;
+ return 0;
}
-static void free_vars(git_hashtable *values)
+static void free_vars(git_strmap *values)
{
cvar_t *var = NULL;
if (values == NULL)
return;
- GIT_HASHTABLE_FOREACH_VALUE(values, var,
- do {
- cvar_t *next = CVAR_LIST_NEXT(var);
- cvar_free(var);
- var = next;
- } while (var != NULL);
- )
+ git_strmap_foreach_value(values, var,
+ while (var != NULL) {
+ cvar_t *next = CVAR_LIST_NEXT(var);
+ cvar_free(var);
+ var = next;
+ });
- git_hashtable_free(values);
+ git_strmap_free(values);
}
static int config_open(git_config_file *cfg)
{
- int error;
+ int res;
diskfile_backend *b = (diskfile_backend *)cfg;
- b->values = git_hashtable_alloc (20, git_hash__strhash_cb, git_hash__strcmp_cb);
- if (b->values == NULL)
- return GIT_ENOMEM;
+ b->values = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(b->values);
git_buf_init(&b->reader.buffer, 0);
- error = git_futils_readbuffer(&b->reader.buffer, b->file_path);
+ res = git_futils_readbuffer(&b->reader.buffer, b->file_path);
/* It's fine if the file doesn't exist */
- if (error == GIT_ENOTFOUND)
- return GIT_SUCCESS;
-
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = config_parse(b);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- git_buf_free(&b->reader.buffer);
-
- return GIT_SUCCESS;
+ if (res == GIT_ENOTFOUND)
+ return 0;
+
+ if (res < 0 || config_parse(b) < 0) {
+ free_vars(b->values);
+ b->values = NULL;
+ git_buf_free(&b->reader.buffer);
+ return -1;
+ }
- cleanup:
- free_vars(b->values);
- b->values = NULL;
git_buf_free(&b->reader.buffer);
-
- return git__rethrow(error, "Failed to open config");
+ return 0;
}
static void backend_free(git_config_file *_backend)
@@ -184,58 +183,61 @@ static void backend_free(git_config_file *_backend)
return;
git__free(backend->file_path);
-
free_vars(backend->values);
-
git__free(backend);
}
static int file_foreach(git_config_file *backend, int (*fn)(const char *, const char *, void *), void *data)
{
- int ret = GIT_SUCCESS;
- cvar_t *var;
diskfile_backend *b = (diskfile_backend *)backend;
+ cvar_t *var;
const char *key;
- GIT_HASHTABLE_FOREACH(b->values, key, var,
- do {
- ret = fn(key, var->value, data);
- if (ret)
- break;
- var = CVAR_LIST_NEXT(var);
- } while (var != NULL);
- )
+ if (!b->values)
+ return 0;
+ git_strmap_foreach(b->values, key, var,
+ do {
+ if (fn(key, var->value, data) < 0)
+ break;
- return ret;
+ var = CVAR_LIST_NEXT(var);
+ } while (var != NULL);
+ );
+
+ return 0;
}
static int config_set(git_config_file *cfg, const char *name, const char *value)
{
- cvar_t *var = NULL;
- cvar_t *existing = NULL, *old_value = NULL;
- int error = GIT_SUCCESS;
+ cvar_t *var = NULL, *old_var;
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
+ khiter_t pos;
+ int rval;
- if ((error = normalize_name(name, &key)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to normalize variable name '%s'", name);
+ if (normalize_name(name, &key) < 0)
+ return -1;
/*
* Try to find it in the existing values and update it if it
* only has one value.
*/
- existing = git_hashtable_lookup(b->values, key);
- if (existing != NULL) {
- char *tmp;
+ pos = git_strmap_lookup_index(b->values, key);
+ if (git_strmap_valid_index(b->values, pos)) {
+ cvar_t *existing = git_strmap_value_at(b->values, pos);
+ char *tmp = NULL;
git__free(key);
- if (existing->next != NULL)
- return git__throw(GIT_EINVALIDARGS, "Multivar incompatible with simple set");
+ if (existing->next != NULL) {
+ giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
+ return -1;
+ }
- tmp = value ? git__strdup(value) : NULL;
- if (tmp == NULL && value != NULL)
- return GIT_ENOMEM;
+ if (value) {
+ tmp = git__strdup(value);
+ GITERR_CHECK_ALLOC(tmp);
+ }
git__free(existing->value);
existing->value = tmp;
@@ -244,32 +246,30 @@ static int config_set(git_config_file *cfg, const char *name, const char *value)
}
var = git__malloc(sizeof(cvar_t));
- if (var == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(var);
memset(var, 0x0, sizeof(cvar_t));
var->key = key;
+ var->value = NULL;
- var->value = value ? git__strdup(value) : NULL;
- if (var->value == NULL && value != NULL) {
- error = GIT_ENOMEM;
- goto out;
+ if (value) {
+ var->value = git__strdup(value);
+ GITERR_CHECK_ALLOC(var->value);
}
- error = git_hashtable_insert2(b->values, key, var, (void **)&old_value);
- if (error < GIT_SUCCESS)
- goto out;
-
- cvar_free(old_value);
-
- error = config_write(b, key, NULL, value);
+ git_strmap_insert2(b->values, key, var, old_var, rval);
+ if (rval < 0)
+ return -1;
+ if (old_var != NULL)
+ cvar_free(old_var);
- out:
- if (error < GIT_SUCCESS)
+ if (config_write(b, key, NULL, value) < 0) {
cvar_free(var);
+ return -1;
+ }
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to set config value");
+ return 0;
}
/*
@@ -277,172 +277,188 @@ static int config_set(git_config_file *cfg, const char *name, const char *value)
*/
static int config_get(git_config_file *cfg, const char *name, const char **out)
{
- cvar_t *var;
- int error = GIT_SUCCESS;
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
+ khiter_t pos;
- if ((error = normalize_name(name, &key)) < GIT_SUCCESS)
- return error;
+ if (normalize_name(name, &key) < 0)
+ return -1;
- var = git_hashtable_lookup(b->values, key);
+ pos = git_strmap_lookup_index(b->values, key);
git__free(key);
- if (var == NULL)
- return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
+ /* no error message; the config system will write one */
+ if (!git_strmap_valid_index(b->values, pos))
+ return GIT_ENOTFOUND;
- *out = var->value;
+ *out = ((cvar_t *)git_strmap_value_at(b->values, pos))->value;
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to get config value for %s", name);
+ return 0;
}
-static int config_get_multivar(git_config_file *cfg, const char *name, const char *regexp, int (*fn)(const char *, void *), void *data)
+static int config_get_multivar(
+ git_config_file *cfg,
+ const char *name,
+ const char *regex_str,
+ int (*fn)(const char *, void *),
+ void *data)
{
cvar_t *var;
- int error = GIT_SUCCESS;
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
- regex_t preg;
+ khiter_t pos;
- if ((error = normalize_name(name, &key)) < GIT_SUCCESS)
- return error;
+ if (normalize_name(name, &key) < 0)
+ return -1;
- var = git_hashtable_lookup(b->values, key);
+ pos = git_strmap_lookup_index(b->values, key);
git__free(key);
- if (var == NULL)
- return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
+ if (!git_strmap_valid_index(b->values, pos))
+ return GIT_ENOTFOUND;
- if (regexp != NULL) {
- error = regcomp(&preg, regexp, REG_EXTENDED);
- if (error < 0)
- return git__throw(GIT_EINVALIDARGS, "Failed to compile regex");
- }
+ var = git_strmap_value_at(b->values, pos);
- do {
- if (regexp == NULL || !regexec(&preg, var->value, 0, NULL, 0)) {
- error = fn(var->value, data);
- if (error < GIT_SUCCESS)
- goto exit;
+ if (regex_str != NULL) {
+ regex_t regex;
+ int result;
+
+ /* regex matching; build the regex */
+ result = regcomp(&regex, regex_str, REG_EXTENDED);
+ if (result < 0) {
+ giterr_set_regex(&regex, result);
+ return -1;
}
- var = var->next;
- } while (var != NULL);
+ /* and throw the callback only on the variables that
+ * match the regex */
+ do {
+ if (regexec(&regex, var->value, 0, NULL, 0) == 0) {
+ /* early termination by the user is not an error;
+ * just break and return successfully */
+ if (fn(var->value, data) < 0)
+ break;
+ }
+
+ var = var->next;
+ } while (var != NULL);
+ regfree(&regex);
+ } else {
+ /* no regex; go through all the variables */
+ do {
+ /* early termination by the user is not an error;
+ * just break and return successfully */
+ if (fn(var->value, data) < 0)
+ break;
+
+ var = var->next;
+ } while (var != NULL);
+ }
- exit:
- if (regexp != NULL)
- regfree(&preg);
- return error;
+ return 0;
}
-static int config_set_multivar(git_config_file *cfg, const char *name, const char *regexp, const char *value)
+static int config_set_multivar(
+ git_config_file *cfg, const char *name, const char *regexp, const char *value)
{
- int error, replaced = 0;
+ int replaced = 0;
cvar_t *var, *newvar;
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
regex_t preg;
+ int result;
+ khiter_t pos;
- if (regexp == NULL)
- return git__throw(GIT_EINVALIDARGS, "No regex supplied");
+ assert(regexp);
- if ((error = normalize_name(name, &key)) < GIT_SUCCESS)
- return error;
+ if (normalize_name(name, &key) < 0)
+ return -1;
- var = git_hashtable_lookup(b->values, key);
- if (var == NULL) {
- free(key);
- return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
+ pos = git_strmap_lookup_index(b->values, key);
+ if (!git_strmap_valid_index(b->values, pos)) {
+ git__free(key);
+ return GIT_ENOTFOUND;
}
- error = regcomp(&preg, regexp, REG_EXTENDED);
- if (error < 0) {
- free(key);
- return git__throw(GIT_EINVALIDARGS, "Failed to compile regex");
+ var = git_strmap_value_at(b->values, pos);
+
+ result = regcomp(&preg, regexp, REG_EXTENDED);
+ if (result < 0) {
+ git__free(key);
+ giterr_set_regex(&preg, result);
+ return -1;
}
- do {
- if (!regexec(&preg, var->value, 0, NULL, 0)) {
+ for (;;) {
+ if (regexec(&preg, var->value, 0, NULL, 0) == 0) {
char *tmp = git__strdup(value);
- if (tmp == NULL) {
- error = GIT_ENOMEM;
- goto exit;
- }
+ GITERR_CHECK_ALLOC(tmp);
- free(var->value);
+ git__free(var->value);
var->value = tmp;
replaced = 1;
}
- if (var->next != NULL)
- var = var->next;
- else
+ if (var->next == NULL)
break;
- } while (var != NULL);
+
+ var = var->next;
+ }
/* If we've reached the end of the variables and we haven't found it yet, we need to append it */
if (!replaced) {
newvar = git__malloc(sizeof(cvar_t));
- if (newvar == NULL) {
- error = GIT_ENOMEM;
- goto exit;
- }
+ GITERR_CHECK_ALLOC(newvar);
memset(newvar, 0x0, sizeof(cvar_t));
+
newvar->key = git__strdup(var->key);
- if (newvar->key == NULL) {
- error = GIT_ENOMEM;
- goto exit;
- }
+ GITERR_CHECK_ALLOC(newvar->key);
+
newvar->value = git__strdup(value);
- if (newvar->value == NULL) {
- error = GIT_ENOMEM;
- goto exit;
- }
+ GITERR_CHECK_ALLOC(newvar->value);
var->next = newvar;
}
- error = config_write(b, key, &preg, value);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to update value in file");
- goto exit;
- }
+ result = config_write(b, key, &preg, value);
- exit:
- free(key);
+ git__free(key);
regfree(&preg);
- return error;
+
+ return result;
}
static int config_delete(git_config_file *cfg, const char *name)
{
- int error;
- const cvar_t *var;
- cvar_t *old_value;
+ cvar_t *var;
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
+ int result;
+ khiter_t pos;
- if ((error = normalize_name(name, &key)) < GIT_SUCCESS)
- return error;
+ if (normalize_name(name, &key) < 0)
+ return -1;
- var = git_hashtable_lookup(b->values, key);
- free(key);
+ pos = git_strmap_lookup_index(b->values, key);
+ git__free(key);
- if (var == NULL)
- return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
+ if (!git_strmap_valid_index(b->values, pos))
+ return GIT_ENOTFOUND;
- if (var->next != NULL)
- return git__throw(GIT_EINVALIDARGS, "Multivar incompatible with simple delete");
+ var = git_strmap_value_at(b->values, pos);
+ if (var->next != NULL) {
+ giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
+ return -1;
+ }
- if ((error = git_hashtable_remove2(b->values, var->key, (void **)&old_value)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to remove %s from hashtable", key);
+ git_strmap_delete_at(b->values, pos);
- error = config_write(b, var->key, NULL, NULL);
- cvar_free(old_value);
+ result = config_write(b, var->key, NULL, NULL);
- return error;
+ cvar_free(var);
+ return result;
}
int git_config_file__ondisk(git_config_file **out, const char *path)
@@ -450,16 +466,12 @@ int git_config_file__ondisk(git_config_file **out, const char *path)
diskfile_backend *backend;
backend = git__malloc(sizeof(diskfile_backend));
- if (backend == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(backend);
memset(backend, 0x0, sizeof(diskfile_backend));
backend->file_path = git__strdup(path);
- if (backend->file_path == NULL) {
- git__free(backend);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(backend->file_path);
backend->parent.open = config_open;
backend->parent.get = config_get;
@@ -472,7 +484,7 @@ int git_config_file__ondisk(git_config_file **out, const char *path)
*out = (git_config_file *)backend;
- return GIT_SUCCESS;
+ return 0;
}
static int cfg_getchar_raw(diskfile_backend *cfg)
@@ -551,7 +563,7 @@ static int cfg_peek(diskfile_backend *cfg, int flags)
/*
* Read and consume a line, returning it in newly-allocated memory.
*/
-static char *cfg_readline(diskfile_backend *cfg)
+static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)
{
char *line = NULL;
char *line_src, *line_end;
@@ -559,9 +571,11 @@ static char *cfg_readline(diskfile_backend *cfg)
line_src = cfg->reader.read_ptr;
- /* Skip empty empty lines */
- while (isspace(*line_src))
- ++line_src;
+ if (skip_whitespace) {
+ /* Skip empty empty lines */
+ while (isspace(*line_src))
+ ++line_src;
+ }
line_end = strchr(line_src, '\n');
@@ -621,12 +635,11 @@ GIT_INLINE(int) config_keychar(int c)
return isalnum(c) || c == '-';
}
-static int parse_section_header_ext(const char *line, const char *base_name, char **section_name)
+static int parse_section_header_ext(diskfile_backend *cfg, const char *line, const char *base_name, char **section_name)
{
int c, rpos;
char *first_quote, *last_quote;
git_buf buf = GIT_BUF_INIT;
- int error = GIT_SUCCESS;
int quote_marks;
/*
* base_name is what came before the space. We should be at the
@@ -637,8 +650,10 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
first_quote = strchr(line, '"');
last_quote = strrchr(line, '"');
- if (last_quote - first_quote == 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. There is no final quotation mark");
+ if (last_quote - first_quote == 0) {
+ set_parse_error(cfg, 0, "Missing closing quotation mark in section header");
+ return -1;
+ }
git_buf_grow(&buf, strlen(base_name) + last_quote - first_quote + 2);
git_buf_printf(&buf, "%s.", base_name);
@@ -655,27 +670,30 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
*/
do {
if (quote_marks == 2) {
- puts("too many marks");
- error = git__throw(GIT_EOBJCORRUPTED, "Falied to parse ext header. Text after closing quote");
- goto out;
-
+ set_parse_error(cfg, rpos, "Unexpected text after closing quotes");
+ git_buf_free(&buf);
+ return -1;
}
switch (c) {
case '"':
++quote_marks;
continue;
+
case '\\':
c = line[rpos++];
+
switch (c) {
case '"':
case '\\':
break;
+
default:
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. Unsupported escape char \\%c", c);
- goto out;
+ set_parse_error(cfg, rpos, "Unsupported escape sequence");
+ git_buf_free(&buf);
+ return -1;
}
- break;
+
default:
break;
}
@@ -683,61 +701,53 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
git_buf_putc(&buf, c);
} while ((c = line[rpos++]) != ']');
- *section_name = git__strdup(git_buf_cstr(&buf));
- out:
- git_buf_free(&buf);
-
- return error;
+ *section_name = git_buf_detach(&buf);
+ return 0;
}
static int parse_section_header(diskfile_backend *cfg, char **section_out)
{
char *name, *name_end;
int name_length, c, pos;
- int error = GIT_SUCCESS;
+ int result;
char *line;
- line = cfg_readline(cfg);
+ line = cfg_readline(cfg, true);
if (line == NULL)
- return GIT_ENOMEM;
+ return -1;
/* find the end of the variable's name */
name_end = strchr(line, ']');
if (name_end == NULL) {
git__free(line);
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Can't find header name end");
+ set_parse_error(cfg, 0, "Missing ']' in section header");
+ return -1;
}
name = (char *)git__malloc((size_t)(name_end - line) + 1);
- if (name == NULL) {
- git__free(line);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(name);
name_length = 0;
pos = 0;
/* Make sure we were given a section header */
c = line[pos++];
- if (c != '[') {
- error = git__throw(GIT_ERROR, "Failed to parse header. Didn't get section header. This is a bug");
- goto error;
- }
+ assert(c == '[');
c = line[pos++];
do {
if (isspace(c)){
name[name_length] = '\0';
- error = parse_section_header_ext(line, name, section_out);
+ result = parse_section_header_ext(cfg, line, name, section_out);
git__free(line);
git__free(name);
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse header");
+ return result;
}
if (!config_keychar(c) && c != '.') {
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Wrong format on header");
- goto error;
+ set_parse_error(cfg, pos, "Unexpected character in header");
+ goto fail_parse;
}
name[name_length++] = (char) tolower(c);
@@ -745,20 +755,21 @@ static int parse_section_header(diskfile_backend *cfg, char **section_out)
} while ((c = line[pos++]) != ']');
if (line[pos - 1] != ']') {
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Config file ended unexpectedly");
- goto error;
+ set_parse_error(cfg, pos, "Unexpected end of file");
+ goto fail_parse;
}
- name[name_length] = 0;
git__free(line);
- git__strtolower(name);
+
+ name[name_length] = 0;
*section_out = name;
- return GIT_SUCCESS;
-error:
+ return 0;
+
+fail_parse:
git__free(line);
git__free(name);
- return error;
+ return -1;
}
static int skip_bom(diskfile_backend *cfg)
@@ -766,7 +777,7 @@ static int skip_bom(diskfile_backend *cfg)
static const char utf8_bom[] = "\xef\xbb\xbf";
if (cfg->reader.buffer.size < sizeof(utf8_bom))
- return GIT_SUCCESS;
+ return 0;
if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
cfg->reader.read_ptr += sizeof(utf8_bom);
@@ -775,7 +786,7 @@ static int skip_bom(diskfile_backend *cfg)
shit with the BoM
*/
- return GIT_SUCCESS;
+ return 0;
}
/*
@@ -817,9 +828,9 @@ static int skip_bom(diskfile_backend *cfg)
boolean_false = "no" | "0" | "false" | "off"
*/
-static void strip_comments(char *line)
+static int strip_comments(char *line, int in_quotes)
{
- int quote_count = 0;
+ int quote_count = in_quotes;
char *ptr;
for (ptr = line; *ptr; ++ptr) {
@@ -832,19 +843,25 @@ static void strip_comments(char *line)
}
}
+ /* skip any space at the end */
if (isspace(ptr[-1])) {
- /* TODO skip whitespace */
+ ptr--;
}
+ ptr[0] = '\0';
+
+ return quote_count;
}
static int config_parse(diskfile_backend *cfg_file)
{
- int error = GIT_SUCCESS, c;
+ int c;
char *current_section = NULL;
char *var_name;
char *var_value;
cvar_t *var, *existing;
git_buf buf = GIT_BUF_INIT;
+ int result = 0;
+ khiter_t pos;
/* Initialize the reading position */
cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;
@@ -852,11 +869,11 @@ static int config_parse(diskfile_backend *cfg_file)
/* If the file is empty, there's nothing for us to do */
if (*cfg_file->reader.read_ptr == '\0')
- return GIT_SUCCESS;
+ return 0;
skip_bom(cfg_file);
- while (error == GIT_SUCCESS && !cfg_file->reader.eof) {
+ while (result == 0 && !cfg_file->reader.eof) {
c = cfg_peek(cfg_file, SKIP_WHITESPACE);
@@ -868,7 +885,7 @@ static int config_parse(diskfile_backend *cfg_file)
case '[': /* section header, new section begins */
git__free(current_section);
current_section = NULL;
- error = parse_section_header(cfg_file, &current_section);
+ result = parse_section_header(cfg_file, &current_section);
break;
case ';':
@@ -877,16 +894,12 @@ static int config_parse(diskfile_backend *cfg_file)
break;
default: /* assume variable declaration */
- error = parse_variable(cfg_file, &var_name, &var_value);
-
- if (error < GIT_SUCCESS)
+ result = parse_variable(cfg_file, &var_name, &var_value);
+ if (result < 0)
break;
var = git__malloc(sizeof(cvar_t));
- if (var == NULL) {
- error = GIT_ENOMEM;
- break;
- }
+ GITERR_CHECK_ALLOC(var);
memset(var, 0x0, sizeof(cvar_t));
@@ -894,19 +907,21 @@ static int config_parse(diskfile_backend *cfg_file)
git_buf_printf(&buf, "%s.%s", current_section, var_name);
git__free(var_name);
- if (git_buf_oom(&buf)) {
- error = GIT_ENOMEM;
- break;
- }
+ if (git_buf_oom(&buf))
+ return -1;
var->key = git_buf_detach(&buf);
var->value = var_value;
/* Add or append the new config option */
- existing = git_hashtable_lookup(cfg_file->values, var->key);
- if (existing == NULL) {
- error = git_hashtable_insert(cfg_file->values, var->key, var);
+ pos = git_strmap_lookup_index(cfg_file->values, var->key);
+ if (!git_strmap_valid_index(cfg_file->values, pos)) {
+ git_strmap_insert(cfg_file->values, var->key, var, result);
+ if (result < 0)
+ break;
+ result = 0;
} else {
+ existing = git_strmap_value_at(cfg_file->values, pos);
while (existing->next != NULL) {
existing = existing->next;
}
@@ -918,13 +933,12 @@ static int config_parse(diskfile_backend *cfg_file)
}
git__free(current_section);
-
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse config");
+ return result;
}
static int write_section(git_filebuf *file, const char *key)
{
- int error;
+ int result;
const char *dot;
git_buf buf = GIT_BUF_INIT;
@@ -939,13 +953,14 @@ static int write_section(git_filebuf *file, const char *key)
git_buf_printf(&buf, " \"%s\"", dot + 1);
}
git_buf_puts(&buf, "]\n");
+
if (git_buf_oom(&buf))
- return GIT_ENOMEM;
+ return -1;
- error = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
+ result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
git_buf_free(&buf);
- return error;
+ return result;
}
/*
@@ -953,50 +968,45 @@ static int write_section(git_filebuf *file, const char *key)
*/
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
{
- int error = GIT_SUCCESS, c;
- int section_matches = 0, last_section_matched = 0, preg_replaced = 0;
+ int result, c;
+ int section_matches = 0, last_section_matched = 0, preg_replaced = 0, write_trailer = 0;
+ const char *pre_end = NULL, *post_start = NULL, *data_start;
char *current_section = NULL, *section, *name, *ldot;
- char *var_name, *var_value;
git_filebuf file = GIT_FILEBUF_INIT;
- const char *pre_end = NULL, *post_start = NULL, *data_start;
/* We need to read in our own config file */
- error = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path);
- if (error < GIT_SUCCESS && error != GIT_ENOTFOUND) {
- return git__rethrow(error, "Failed to read existing config file %s", cfg->file_path);
- }
+ result = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path);
/* Initialise the reading position */
- if (error == GIT_ENOTFOUND) {
- error = GIT_SUCCESS;
+ if (result == GIT_ENOTFOUND) {
cfg->reader.read_ptr = NULL;
cfg->reader.eof = 1;
data_start = NULL;
git_buf_clear(&cfg->reader.buffer);
- } else {
+ } else if (result == 0) {
cfg->reader.read_ptr = cfg->reader.buffer.ptr;
cfg->reader.eof = 0;
data_start = cfg->reader.read_ptr;
+ } else {
+ return -1; /* OS error when reading the file */
}
/* Lock the file */
- error = git_filebuf_open(&file, cfg->file_path, 0);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lock config file");
+ if (git_filebuf_open(&file, cfg->file_path, 0) < 0)
+ return -1;
skip_bom(cfg);
ldot = strrchr(key, '.');
name = ldot + 1;
section = git__strndup(key, ldot - key);
- while (error == GIT_SUCCESS && !cfg->reader.eof) {
+ while (!cfg->reader.eof) {
c = cfg_peek(cfg, SKIP_WHITESPACE);
- switch (c) {
- case '\0': /* We've arrived at the end of the file */
+ if (c == '\0') { /* We've arrived at the end of the file */
break;
- case '[': /* section header, new section begins */
+ } else if (c == '[') { /* section header, new section begins */
/*
* We set both positions to the current one in case we
* need to add a variable to the end of a section. In that
@@ -1005,23 +1015,21 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
* default case will take care of updating them.
*/
pre_end = post_start = cfg->reader.read_ptr;
- if (current_section)
- git__free(current_section);
- error = parse_section_header(cfg, &current_section);
- if (error < GIT_SUCCESS)
- break;
+
+ git__free(current_section);
+ if (parse_section_header(cfg, &current_section) < 0)
+ goto rewrite_fail;
/* Keep track of when it stops matching */
last_section_matched = section_matches;
section_matches = !strcmp(current_section, section);
- break;
+ }
- case ';':
- case '#':
+ else if (c == ';' || c == '#') {
cfg_consume_line(cfg);
- break;
+ }
- default:
+ else {
/*
* If the section doesn't match, but the last section did,
* it means we need to add a variable (so skip the line
@@ -1035,67 +1043,54 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
if (!section_matches) {
if (!last_section_matched) {
cfg_consume_line(cfg);
- break;
+ continue;
}
} else {
- int cmp = -1;
+ int has_matched = 0;
+ char *var_name, *var_value;
pre_end = cfg->reader.read_ptr;
- if ((error = parse_variable(cfg, &var_name, &var_value)) == GIT_SUCCESS)
- cmp = strcasecmp(name, var_name);
+ if (parse_variable(cfg, &var_name, &var_value) < 0)
+ goto rewrite_fail;
+
+ /* First try to match the name of the variable */
+ if (strcasecmp(name, var_name) == 0)
+ has_matched = 1;
- if (cmp == 0 && preg != NULL)
- cmp = regexec(preg, var_value, 0, NULL, 0);
+ /* If the name matches, and we have a regex to match the
+ * value, try to match it */
+ if (has_matched && preg != NULL)
+ has_matched = (regexec(preg, var_value, 0, NULL, 0) == 0);
git__free(var_name);
git__free(var_value);
- if (cmp != 0)
- break;
+ /* if there is no match, keep going */
+ if (!has_matched)
+ continue;
post_start = cfg->reader.read_ptr;
}
- /*
- * We've found the variable we wanted to change, so
- * write anything up to it
- */
+ /* We've found the variable we wanted to change, so
+ * write anything up to it */
+ git_filebuf_write(&file, data_start, pre_end - data_start);
preg_replaced = 1;
- error = git_filebuf_write(&file, data_start, pre_end - data_start);
- if (error < GIT_SUCCESS) {
- git__rethrow(error, "Failed to write the first part of the file");
- break;
- }
- /*
- * Then replace the variable. If the value is NULL, it
- * means we want to delete it, so pretend everything went
- * fine
- */
- if (value == NULL)
- error = GIT_SUCCESS;
- else
- error = git_filebuf_printf(&file, "\t%s = %s\n", name, value);
- if (error < GIT_SUCCESS) {
- git__rethrow(error, "Failed to overwrite the variable");
- break;
+ /* Then replace the variable. If the value is NULL, it
+ * means we want to delete it, so don't write anything. */
+ if (value != NULL) {
+ git_filebuf_printf(&file, "\t%s = %s\n", name, value);
}
+ /* multiline variable? we need to keep reading lines to match */
if (preg != NULL) {
data_start = post_start;
continue;
}
- /* And then the write out rest of the file */
- error = git_filebuf_write(&file, post_start,
- cfg->reader.buffer.size - (post_start - data_start));
-
- if (error < GIT_SUCCESS) {
- git__rethrow(error, "Failed to write the rest of the file");
- break;
- }
-
- goto cleanup;
+ write_trailer = 1;
+ break; /* break from the loop */
}
}
@@ -1115,134 +1110,159 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
* want to write the rest of the file. Otherwise we need to write
* out the whole file and then the new variable.
*/
- if (preg_replaced) {
- error = git_filebuf_printf(&file, "\n%s", data_start);
- if (error < GIT_SUCCESS)
- error = git__rethrow(error, "Failed to write the rest of the file");
-
- goto cleanup;
- }
+ if (write_trailer) {
+ /* Write out rest of the file */
+ git_filebuf_write(&file, post_start, cfg->reader.buffer.size - (post_start - data_start));
+ } else {
+ if (preg_replaced) {
+ git_filebuf_printf(&file, "\n%s", data_start);
+ } else {
+ git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);
+
+ /* And now if we just need to add a variable */
+ if (!section_matches && write_section(&file, section) < 0)
+ goto rewrite_fail;
+
+ /* Sanity check: if we are here, and value is NULL, that means that somebody
+ * touched the config file after our intial read. We should probably assert()
+ * this, but instead we'll handle it gracefully with an error. */
+ if (value == NULL) {
+ giterr_set(GITERR_CONFIG,
+ "Race condition when writing a config file (a cvar has been removed)");
+ goto rewrite_fail;
+ }
- error = git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);
- if (error < GIT_SUCCESS) {
- git__rethrow(error, "Failed to write original config content");
- goto cleanup;
+ git_filebuf_printf(&file, "\t%s = %s\n", name, value);
+ }
}
- /* And now if we just need to add a variable */
- if (section_matches) {
- error = git_filebuf_printf(&file, "\t%s = %s\n", name, value);
- goto cleanup;
- }
+ git__free(section);
+ git__free(current_section);
- /* Or maybe we need to write out a whole section */
- error = write_section(&file, section);
- if (error < GIT_SUCCESS)
- git__rethrow(error, "Failed to write new section");
+ result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
+ git_buf_free(&cfg->reader.buffer);
+ return result;
- error = git_filebuf_printf(&file, "\t%s = %s\n", name, value);
- cleanup:
+rewrite_fail:
git__free(section);
git__free(current_section);
- if (error < GIT_SUCCESS)
- git_filebuf_cleanup(&file);
- else
- error = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
-
+ git_filebuf_cleanup(&file);
git_buf_free(&cfg->reader.buffer);
- return error;
+ return -1;
}
-static int is_multiline_var(const char *str)
+/* '\"' -> '"' etc */
+static char *fixup_line(const char *ptr, int quote_count)
{
- char *end = strrchr(str, '\0') - 1;
+ char *str = git__malloc(strlen(ptr) + 1);
+ char *out = str, *esc;
+ const char *escapes = "ntb\"\\";
+ const char *escaped = "\n\t\b\"\\";
+
+ if (str == NULL)
+ return NULL;
- while (isspace(*end))
- --end;
+ while (*ptr != '\0') {
+ if (*ptr == '"') {
+ quote_count++;
+ } else if (*ptr != '\\') {
+ *out++ = *ptr;
+ } else {
+ /* backslash, check the next char */
+ ptr++;
+ /* if we're at the end, it's a multiline, so keep the backslash */
+ if (*ptr == '\0') {
+ *out++ = '\\';
+ goto out;
+ }
+ if ((esc = strchr(escapes, *ptr)) != NULL) {
+ *out++ = escaped[esc - escapes];
+ } else {
+ git__free(str);
+ giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);
+ return NULL;
+ }
+ }
+ ptr++;
+ }
+
+out:
+ *out = '\0';
+
+ return str;
+}
- return *end == '\\';
+static int is_multiline_var(const char *str)
+{
+ const char *end = str + strlen(str);
+ return (end > str) && (end[-1] == '\\');
}
-static int parse_multiline_variable(diskfile_backend *cfg, const char *first, char **out)
+static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int in_quotes)
{
- char *line = NULL, *end;
- int error = GIT_SUCCESS, ret;
- size_t len;
- char *buf;
+ char *line = NULL, *proc_line = NULL;
+ int quote_count;
/* Check that the next line exists */
- line = cfg_readline(cfg);
+ line = cfg_readline(cfg, false);
if (line == NULL)
- return GIT_ENOMEM;
+ return -1;
/* We've reached the end of the file, there is input missing */
if (line[0] == '\0') {
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse multiline var. File ended unexpectedly");
- goto out;
+ set_parse_error(cfg, 0, "Unexpected end of file while parsing multine var");
+ git__free(line);
+ return -1;
}
- strip_comments(line);
+ quote_count = strip_comments(line, !!in_quotes);
/* If it was just a comment, pretend it didn't exist */
if (line[0] == '\0') {
- error = parse_multiline_variable(cfg, first, out);
- goto out;
+ git__free(line);
+ return parse_multiline_variable(cfg, value, quote_count);
+ /* TODO: unbounded recursion. This **could** be exploitable */
}
- /* Find the continuation character '\' and strip the whitespace */
- end = strrchr(first, '\\');
- while (isspace(end[-1]))
- --end;
-
- *end = '\0'; /* Terminate the string here */
+ /* Drop the continuation character '\': to closely follow the UNIX
+ * standard, this character **has** to be last one in the buf, with
+ * no whitespace after it */
+ assert(is_multiline_var(value->ptr));
+ git_buf_truncate(value, git_buf_len(value) - 1);
- len = strlen(first) + strlen(line) + 2;
- buf = git__malloc(len);
- if (buf == NULL) {
- error = GIT_ENOMEM;
- goto out;
- }
-
- ret = p_snprintf(buf, len, "%s %s", first, line);
- if (ret < 0) {
- error = git__throw(GIT_EOSERR, "Failed to parse multiline var. Failed to put together two lines. OS err: %s", strerror(errno));
- git__free(buf);
- goto out;
+ proc_line = fixup_line(line, in_quotes);
+ if (proc_line == NULL) {
+ git__free(line);
+ return -1;
}
+ /* add this line to the multiline var */
+ git_buf_puts(value, proc_line);
+ git__free(line);
+ git__free(proc_line);
/*
- * If we need to continue reading the next line, pretend
- * everything we've read up to now was in one line and call
- * ourselves.
+ * If we need to continue reading the next line, let's just
+ * keep putting stuff in the buffer
*/
- if (is_multiline_var(buf)) {
- char *final_val;
- error = parse_multiline_variable(cfg, buf, &final_val);
- git__free(buf);
- buf = final_val;
- }
+ if (is_multiline_var(value->ptr))
+ return parse_multiline_variable(cfg, value, quote_count);
- *out = buf;
-
- out:
- git__free(line);
- return error;
+ return 0;
}
static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value)
{
- char *tmp;
- int error = GIT_SUCCESS;
const char *var_end = NULL;
const char *value_start = NULL;
char *line;
+ int quote_count;
- line = cfg_readline(cfg);
+ line = cfg_readline(cfg, true);
if (line == NULL)
- return GIT_ENOMEM;
+ return -1;
- strip_comments(line);
+ quote_count = strip_comments(line, 0);
var_end = strchr(line, '=');
@@ -1256,52 +1276,42 @@ static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_val
while (isspace(var_end[0]));
}
- tmp = git__strndup(line, var_end - line + 1);
- if (tmp == NULL) {
- error = GIT_ENOMEM;
- goto out;
- }
+ *var_name = git__strndup(line, var_end - line + 1);
+ GITERR_CHECK_ALLOC(*var_name);
- *var_name = tmp;
+ /* If there is no value, boolean true is assumed */
+ *var_value = NULL;
/*
* Now, let's try to parse the value
*/
if (value_start != NULL) {
-
while (isspace(value_start[0]))
value_start++;
- if (value_start[0] == '\0') {
- *var_value = NULL;
- goto out;
- }
-
if (is_multiline_var(value_start)) {
- error = parse_multiline_variable(cfg, value_start, var_value);
- if (error != GIT_SUCCESS)
- {
- *var_value = NULL;
+ git_buf multi_value = GIT_BUF_INIT;
+ char *proc_line = fixup_line(value_start, 0);
+ GITERR_CHECK_ALLOC(proc_line);
+ git_buf_puts(&multi_value, proc_line);
+ git__free(proc_line);
+ if (parse_multiline_variable(cfg, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
git__free(*var_name);
+ git__free(line);
+ git_buf_free(&multi_value);
+ return -1;
}
- goto out;
- }
- tmp = git__strdup(value_start);
- if (tmp == NULL) {
- git__free(*var_name);
- *var_value = NULL;
- error = GIT_ENOMEM;
- goto out;
+ *var_value = git_buf_detach(&multi_value);
+
+ }
+ else if (value_start[0] != '\0') {
+ *var_value = fixup_line(value_start, 0);
+ GITERR_CHECK_ALLOC(*var_value);
}
- *var_value = tmp;
- } else {
- /* If there is no value, boolean true is assumed */
- *var_value = NULL;
}
- out:
git__free(line);
- return error;
+ return 0;
}
diff --git a/src/config_file.h b/src/config_file.h
new file mode 100644
index 000000000..0080b5713
--- /dev/null
+++ b/src/config_file.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 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.
+ */
+#ifndef INCLUDE_config_file_h__
+#define INCLUDE_config_file_h__
+
+#include "git2/config.h"
+
+GIT_INLINE(int) git_config_file_open(git_config_file *cfg)
+{
+ return cfg->open(cfg);
+}
+
+GIT_INLINE(void) git_config_file_free(git_config_file *cfg)
+{
+ cfg->free(cfg);
+}
+
+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);
+}
+
+#endif
+
diff --git a/src/crlf.c b/src/crlf.c
index f0ec7b736..8fe588a35 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -105,7 +105,7 @@ static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, con
static int drop_crlf(git_buf *dest, const git_buf *source)
{
const char *scan = source->ptr, *next;
- const char *scan_end = source->ptr + source->size;
+ const char *scan_end = git_buf_cstr(source) + git_buf_len(source);
/* Main scan loop. Find the next carriage return and copy the
* whole chunk up to that point to the destination buffer.
@@ -128,8 +128,7 @@ static int drop_crlf(git_buf *dest, const git_buf *source)
/* Copy remaining input into dest */
git_buf_put(dest, scan, scan_end - scan);
-
- return git_buf_lasterror(dest);
+ return 0;
}
static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *source)
@@ -139,7 +138,7 @@ static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *sou
assert(self && dest && source);
/* Empty file? Nothing to do */
- if (source->size == 0)
+ if (git_buf_len(source) == 0)
return 0;
/* Heuristics to see if we can skip the conversion.
diff --git a/src/delta-apply.c b/src/delta-apply.c
index 24eba2bda..c8c662fa8 100644
--- a/src/delta-apply.c
+++ b/src/delta-apply.c
@@ -51,11 +51,15 @@ int git__delta_apply(
* if not we would underflow while accessing data from the
* base object, resulting in data corruption or segfault.
*/
- if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len))
- return git__throw(GIT_ERROR, "Failed to apply delta. Base size does not match given data");
+ if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) {
+ giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
+ return -1;
+ }
- if (hdr_sz(&res_sz, &delta, delta_end) < 0)
- return git__throw(GIT_ERROR, "Failed to apply delta. Base size does not match given data");
+ if (hdr_sz(&res_sz, &delta, delta_end) < 0) {
+ giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
+ return -1;
+ }
if ((res_dp = git__malloc(res_sz + 1)) == NULL)
return GIT_ENOMEM;
@@ -111,5 +115,6 @@ int git__delta_apply(
fail:
git__free(out->data);
out->data = NULL;
- return git__throw(GIT_ERROR, "Failed to apply delta");
+ giterr_set(GITERR_INVALID, "Failed to apply delta");
+ return -1;
}
diff --git a/src/diff.c b/src/diff.c
index db339d74e..b21dfaf90 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -8,23 +8,50 @@
#include "git2/diff.h"
#include "diff.h"
#include "fileops.h"
+#include "config.h"
+#include "attr_file.h"
-static void diff_delta__free(git_diff_delta *delta)
+static bool diff_pathspec_is_interesting(const git_strarray *pathspec)
{
- if (!delta)
- return;
+ const char *str;
- if (delta->new_file.flags & GIT_DIFF_FILE_FREE_PATH) {
- git__free((char *)delta->new_file.path);
- delta->new_file.path = NULL;
- }
+ if (pathspec == NULL || pathspec->count == 0)
+ return false;
+ if (pathspec->count > 1)
+ return true;
+
+ str = pathspec->strings[0];
+ if (!str || !str[0] || (!str[1] && (str[0] == '*' || str[0] == '.')))
+ return false;
+ return true;
+}
+
+static bool diff_path_matches_pathspec(git_diff_list *diff, const char *path)
+{
+ unsigned int i;
+ git_attr_fnmatch *match;
+
+ if (!diff->pathspec.length)
+ return true;
+
+ git_vector_foreach(&diff->pathspec, i, match) {
+ int result = git__fnmatch(match->pattern, path, 0);
+
+ /* if we didn't match, look for exact dirname prefix match */
+ if (result == GIT_ENOMATCH &&
+ (match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
+ strncmp(path, match->pattern, match->length) == 0 &&
+ path[match->length] == '/')
+ result = 0;
- if (delta->old_file.flags & GIT_DIFF_FILE_FREE_PATH) {
- git__free((char *)delta->old_file.path);
- delta->old_file.path = NULL;
+ if (result == 0)
+ return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;
+
+ if (result != GIT_ENOMATCH)
+ giterr_clear();
}
- git__free(delta);
+ return false;
}
static git_diff_delta *diff_delta__alloc(
@@ -36,12 +63,12 @@ static git_diff_delta *diff_delta__alloc(
if (!delta)
return NULL;
- delta->old_file.path = git__strdup(path);
+ delta->old_file.path = git_pool_strdup(&diff->pool, path);
if (delta->old_file.path == NULL) {
git__free(delta);
return NULL;
}
- delta->old_file.flags |= GIT_DIFF_FILE_FREE_PATH;
+
delta->new_file.path = delta->old_file.path;
if (diff->opts.flags & GIT_DIFF_REVERSE) {
@@ -56,7 +83,8 @@ static git_diff_delta *diff_delta__alloc(
return delta;
}
-static git_diff_delta *diff_delta__dup(const git_diff_delta *d)
+static git_diff_delta *diff_delta__dup(
+ const git_diff_delta *d, git_pool *pool)
{
git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
if (!delta)
@@ -64,33 +92,29 @@ static git_diff_delta *diff_delta__dup(const git_diff_delta *d)
memcpy(delta, d, sizeof(git_diff_delta));
- delta->old_file.path = git__strdup(d->old_file.path);
- if (delta->old_file.path == NULL) {
- git__free(delta);
- return NULL;
- }
- delta->old_file.flags |= GIT_DIFF_FILE_FREE_PATH;
+ delta->old_file.path = git_pool_strdup(pool, d->old_file.path);
+ if (delta->old_file.path == NULL)
+ goto fail;
if (d->new_file.path != d->old_file.path) {
- delta->new_file.path = git__strdup(d->new_file.path);
- if (delta->new_file.path == NULL) {
- git__free(delta->old_file.path);
- git__free(delta);
- return NULL;
- }
- delta->new_file.flags |= GIT_DIFF_FILE_FREE_PATH;
+ delta->new_file.path = git_pool_strdup(pool, d->new_file.path);
+ if (delta->new_file.path == NULL)
+ goto fail;
} else {
delta->new_file.path = delta->old_file.path;
- delta->new_file.flags &= ~GIT_DIFF_FILE_FREE_PATH;
}
return delta;
+
+fail:
+ git__free(delta);
+ return NULL;
}
static git_diff_delta *diff_delta__merge_like_cgit(
- const git_diff_delta *a, const git_diff_delta *b)
+ const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
{
- git_diff_delta *dup = diff_delta__dup(a);
+ git_diff_delta *dup = diff_delta__dup(a, pool);
if (!dup)
return NULL;
@@ -101,9 +125,7 @@ static git_diff_delta *diff_delta__merge_like_cgit(
dup->new_file.mode = b->new_file.mode;
dup->new_file.size = b->new_file.size;
- dup->new_file.flags =
- (dup->new_file.flags & GIT_DIFF_FILE_FREE_PATH) |
- (b->new_file.flags & ~GIT_DIFF_FILE_FREE_PATH);
+ dup->new_file.flags = b->new_file.flags;
/* Emulate C git for merging two diffs (a la 'git diff <sha>').
*
@@ -132,10 +154,21 @@ static int diff_delta__from_one(
git_delta_t status,
const git_index_entry *entry)
{
- int error;
- git_diff_delta *delta = diff_delta__alloc(diff, status, entry->path);
- if (!delta)
- return git__rethrow(GIT_ENOMEM, "Could not allocate diff record");
+ git_diff_delta *delta;
+
+ if (status == GIT_DELTA_IGNORED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
+ return 0;
+
+ if (status == GIT_DELTA_UNTRACKED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
+ return 0;
+
+ if (!diff_path_matches_pathspec(diff, entry->path))
+ return 0;
+
+ delta = diff_delta__alloc(diff, status, entry->path);
+ GITERR_CHECK_ALLOC(delta);
/* This fn is just for single-sided diffs */
assert(status != GIT_DELTA_MODIFIED);
@@ -153,10 +186,12 @@ static int diff_delta__from_one(
delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
- if ((error = git_vector_insert(&diff->deltas, delta)) < GIT_SUCCESS)
- diff_delta__free(delta);
+ if (git_vector_insert(&diff->deltas, delta) < 0) {
+ git__free(delta);
+ return -1;
+ }
- return error;
+ return 0;
}
static int diff_delta__from_two(
@@ -166,9 +201,12 @@ static int diff_delta__from_two(
const git_index_entry *new_entry,
git_oid *new_oid)
{
- int error;
git_diff_delta *delta;
+ if (status == GIT_DELTA_UNMODIFIED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+ return 0;
+
if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {
const git_index_entry *temp = old_entry;
old_entry = new_entry;
@@ -176,8 +214,7 @@ static int diff_delta__from_two(
}
delta = diff_delta__alloc(diff, status, old_entry->path);
- if (!delta)
- return git__rethrow(GIT_ENOMEM, "Could not allocate diff record");
+ GITERR_CHECK_ALLOC(delta);
delta->old_file.mode = old_entry->mode;
git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
@@ -188,28 +225,23 @@ static int diff_delta__from_two(
if (new_oid || !git_oid_iszero(&new_entry->oid))
delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
- if ((error = git_vector_insert(&diff->deltas, delta)) < GIT_SUCCESS)
- diff_delta__free(delta);
+ if (git_vector_insert(&diff->deltas, delta) < 0) {
+ git__free(delta);
+ return -1;
+ }
- return error;
+ return 0;
}
-#define DIFF_OLD_PREFIX_DEFAULT "a/"
-#define DIFF_NEW_PREFIX_DEFAULT "b/"
-
-static char *diff_strdup_prefix(const char *prefix)
+static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
{
size_t len = strlen(prefix);
- char *str = git__malloc(len + 2);
- if (str != NULL) {
- memcpy(str, prefix, len + 1);
- /* append '/' at end if needed */
- if (len > 0 && str[len - 1] != '/') {
- str[len] = '/';
- str[len + 1] = '\0';
- }
- }
- return str;
+
+ /* append '/' at end if needed */
+ if (len > 0 && prefix[len - 1] != '/')
+ return git_pool_strcat(pool, prefix, "/");
+ else
+ return git_pool_strndup(pool, prefix, len + 1);
}
static int diff_delta__cmp(const void *a, const void *b)
@@ -219,29 +251,55 @@ static int diff_delta__cmp(const void *a, const void *b)
return val ? val : ((int)da->status - (int)db->status);
}
+static int config_bool(git_config *cfg, const char *name, int defvalue)
+{
+ int val = defvalue;
+ if (git_config_get_bool(cfg, name, &val) < 0)
+ giterr_clear();
+ return val;
+}
+
static git_diff_list *git_diff_list_alloc(
git_repository *repo, const git_diff_options *opts)
{
+ git_config *cfg;
+ size_t i;
git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
if (diff == NULL)
return NULL;
diff->repo = repo;
+ if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 ||
+ git_pool_init(&diff->pool, 1, 0) < 0)
+ goto fail;
+
+ /* load config values that affect diff behavior */
+ if (git_repository_config__weakptr(&cfg, repo) < 0)
+ goto fail;
+ if (config_bool(cfg, "core.symlinks", 1))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
+ if (config_bool(cfg, "core.ignorestat", 0))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;
+ if (config_bool(cfg, "core.filemode", 1))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_EXEC_BIT;
+ if (config_bool(cfg, "core.trustctime", 1))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
+ /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
+
if (opts == NULL)
return diff;
memcpy(&diff->opts, opts, sizeof(git_diff_options));
+ memset(&diff->opts.pathspec, 0, sizeof(diff->opts.pathspec));
- diff->opts.old_prefix = diff_strdup_prefix(
+ diff->opts.old_prefix = diff_strdup_prefix(&diff->pool,
opts->old_prefix ? opts->old_prefix : DIFF_OLD_PREFIX_DEFAULT);
- diff->opts.new_prefix = diff_strdup_prefix(
+ diff->opts.new_prefix = diff_strdup_prefix(&diff->pool,
opts->new_prefix ? opts->new_prefix : DIFF_NEW_PREFIX_DEFAULT);
- if (!diff->opts.old_prefix || !diff->opts.new_prefix) {
- git__free(diff);
- return NULL;
- }
+ if (!diff->opts.old_prefix || !diff->opts.new_prefix)
+ goto fail;
if (diff->opts.flags & GIT_DIFF_REVERSE) {
char *swap = diff->opts.old_prefix;
@@ -249,33 +307,63 @@ static git_diff_list *git_diff_list_alloc(
diff->opts.new_prefix = swap;
}
- if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < GIT_SUCCESS) {
- git__free(diff->opts.old_prefix);
- git__free(diff->opts.new_prefix);
- git__free(diff);
- return NULL;
- }
+ /* only copy pathspec if it is "interesting" so we can test
+ * diff->pathspec.length > 0 to know if it is worth calling
+ * fnmatch as we iterate.
+ */
+ if (!diff_pathspec_is_interesting(&opts->pathspec))
+ return diff;
- /* do something safe with the pathspec strarray */
+ if (git_vector_init(
+ &diff->pathspec, (unsigned int)opts->pathspec.count, NULL) < 0)
+ goto fail;
+
+ for (i = 0; i < opts->pathspec.count; ++i) {
+ int ret;
+ const char *pattern = opts->pathspec.strings[i];
+ git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
+ if (!match)
+ goto fail;
+ ret = git_attr_fnmatch__parse(match, &diff->pool, NULL, &pattern);
+ if (ret == GIT_ENOTFOUND) {
+ git__free(match);
+ continue;
+ } else if (ret < 0)
+ goto fail;
+
+ if (git_vector_insert(&diff->pathspec, match) < 0)
+ goto fail;
+ }
return diff;
+
+fail:
+ git_diff_list_free(diff);
+ return NULL;
}
void git_diff_list_free(git_diff_list *diff)
{
git_diff_delta *delta;
+ git_attr_fnmatch *match;
unsigned int i;
if (!diff)
return;
git_vector_foreach(&diff->deltas, i, delta) {
- diff_delta__free(delta);
+ git__free(delta);
diff->deltas.contents[i] = NULL;
}
git_vector_free(&diff->deltas);
- git__free(diff->opts.old_prefix);
- git__free(diff->opts.new_prefix);
+
+ git_vector_foreach(&diff->pathspec, i, match) {
+ git__free(match);
+ diff->pathspec.contents[i] = NULL;
+ }
+ git_vector_free(&diff->pathspec);
+
+ git_pool_clear(&diff->pool);
git__free(diff);
}
@@ -284,27 +372,24 @@ static int oid_for_workdir_item(
const git_index_entry *item,
git_oid *oid)
{
- int error = GIT_SUCCESS;
+ int result;
git_buf full_path = GIT_BUF_INIT;
- error = git_buf_joinpath(
- &full_path, git_repository_workdir(repo), item->path);
- if (error != GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&full_path, git_repository_workdir(repo), item->path) < 0)
+ return -1;
- /* otherwise calculate OID for file */
+ /* calculate OID for file if possible*/
if (S_ISLNK(item->mode))
- error = git_odb__hashlink(oid, full_path.ptr);
- else if (!git__is_sizet(item->file_size))
- error = git__throw(GIT_ERROR, "File size overflow for 32-bit systems");
- else {
- int fd;
-
- if ((fd = p_open(full_path.ptr, O_RDONLY)) < 0)
- error = git__throw(
- GIT_EOSERR, "Could not open '%s'", item->path);
+ result = git_odb__hashlink(oid, full_path.ptr);
+ else if (!git__is_sizet(item->file_size)) {
+ giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
+ result = -1;
+ } else {
+ int fd = git_futils_open_ro(full_path.ptr);
+ if (fd < 0)
+ result = fd;
else {
- error = git_odb__hashfd(
+ result = git_odb__hashfd(
oid, fd, (size_t)item->file_size, GIT_OBJ_BLOB);
p_close(fd);
}
@@ -312,9 +397,11 @@ static int oid_for_workdir_item(
git_buf_free(&full_path);
- return error;
+ return result;
}
+#define EXEC_BIT_MASK 0000111
+
static int maybe_modified(
git_iterator *old_iter,
const git_index_entry *oitem,
@@ -322,55 +409,96 @@ static int maybe_modified(
const git_index_entry *nitem,
git_diff_list *diff)
{
- int error = GIT_SUCCESS;
git_oid noid, *use_noid = NULL;
+ git_delta_t status = GIT_DELTA_MODIFIED;
+ unsigned int omode = oitem->mode;
+ unsigned int nmode = nitem->mode;
GIT_UNUSED(old_iter);
- /* support "assume unchanged" & "skip worktree" bits */
- if ((oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) != 0 ||
- (oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
- return GIT_SUCCESS;
+ if (!diff_path_matches_pathspec(diff, oitem->path))
+ return 0;
+
+ /* on platforms with no symlinks, promote plain files to symlinks */
+ if (S_ISLNK(omode) && S_ISREG(nmode) &&
+ !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
+ nmode = GIT_MODE_TYPE(omode) | (nmode & GIT_MODE_PERMS_MASK);
- if (GIT_MODE_TYPE(oitem->mode) != GIT_MODE_TYPE(nitem->mode)) {
- error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem);
- if (error == GIT_SUCCESS)
- error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem);
- return error;
+ /* on platforms with no execmode, clear exec bit from comparisons */
+ if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_EXEC_BIT)) {
+ omode = omode & ~EXEC_BIT_MASK;
+ nmode = nmode & ~EXEC_BIT_MASK;
}
- if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
- oitem->mode == nitem->mode)
- return GIT_SUCCESS;
+ /* support "assume unchanged" (badly, b/c we still stat everything) */
+ if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0)
+ status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ?
+ GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED;
+
+ /* support "skip worktree" index bit */
+ else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* if basic type of file changed, then split into delete and add */
+ else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
+ if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
+ diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0)
+ return -1;
+ return 0;
+ }
+
+ /* if oids and modes match, then file is unmodified */
+ else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
+ omode == nmode)
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* if we have a workdir item with an unknown oid, check deeper */
+ else if (git_oid_iszero(&nitem->oid) && new_iter->type == GIT_ITERATOR_WORKDIR) {
+ /* TODO: add check against index file st_mtime to avoid racy-git */
- if (git_oid_iszero(&nitem->oid) && new_iter->type == GIT_ITERATOR_WORKDIR) {
/* if they files look exactly alike, then we'll assume the same */
if (oitem->file_size == nitem->file_size &&
- oitem->ctime.seconds == nitem->ctime.seconds &&
+ (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) ||
+ (oitem->ctime.seconds == nitem->ctime.seconds)) &&
oitem->mtime.seconds == nitem->mtime.seconds &&
- oitem->dev == nitem->dev &&
+ (!(diff->diffcaps & GIT_DIFFCAPS_USE_DEV) ||
+ (oitem->dev == nitem->dev)) &&
oitem->ino == nitem->ino &&
oitem->uid == nitem->uid &&
oitem->gid == nitem->gid)
- return GIT_SUCCESS;
+ status = GIT_DELTA_UNMODIFIED;
+
+ else if (S_ISGITLINK(nmode)) {
+ git_submodule *sub;
+
+ if ((diff->opts.flags & GIT_DIFF_IGNORE_SUBMODULES) != 0)
+ status = GIT_DELTA_UNMODIFIED;
+ else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0)
+ return -1;
+ else if (sub->ignore == GIT_SUBMODULE_IGNORE_ALL)
+ status = GIT_DELTA_UNMODIFIED;
+ else {
+ /* TODO: support other GIT_SUBMODULE_IGNORE values */
+ status = GIT_DELTA_UNMODIFIED;
+ }
+ }
/* TODO: check git attributes so we will not have to read the file
* in if it is marked binary.
*/
- error = oid_for_workdir_item(diff->repo, nitem, &noid);
- if (error != GIT_SUCCESS)
- return error;
- if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
- oitem->mode == nitem->mode)
- return GIT_SUCCESS;
+ else if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
+ return -1;
+
+ else if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
+ omode == nmode)
+ status = GIT_DELTA_UNMODIFIED;
/* store calculated oid so we don't have to recalc later */
use_noid = &noid;
}
- return diff_delta__from_two(
- diff, GIT_DELTA_MODIFIED, oitem, nitem, use_noid);
+ return diff_delta__from_two(diff, status, oitem, nitem, use_noid);
}
static int diff_from_iterators(
@@ -380,37 +508,33 @@ static int diff_from_iterators(
git_iterator *new_iter,
git_diff_list **diff_ptr)
{
- int error;
const git_index_entry *oitem, *nitem;
char *ignore_prefix = NULL;
git_diff_list *diff = git_diff_list_alloc(repo, opts);
- if (!diff) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ if (!diff)
+ goto fail;
diff->old_src = old_iter->type;
diff->new_src = new_iter->type;
- if ((error = git_iterator_current(old_iter, &oitem)) < GIT_SUCCESS ||
- (error = git_iterator_current(new_iter, &nitem)) < GIT_SUCCESS)
- goto cleanup;
+ if (git_iterator_current(old_iter, &oitem) < 0 ||
+ git_iterator_current(new_iter, &nitem) < 0)
+ goto fail;
/* run iterators building diffs */
- while (!error && (oitem || nitem)) {
+ while (oitem || nitem) {
/* create DELETED records for old items not matched in new */
if (oitem && (!nitem || strcmp(oitem->path, nitem->path) < 0)) {
- error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem);
- if (error == GIT_SUCCESS)
- error = git_iterator_advance(old_iter, &oitem);
- continue;
+ if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
+ git_iterator_advance(old_iter, &oitem) < 0)
+ goto fail;
}
/* create ADDED, TRACKED, or IGNORED records for new items not
* matched in old (and/or descend into directories as needed)
*/
- if (nitem && (!oitem || strcmp(oitem->path, nitem->path) > 0)) {
+ else if (nitem && (!oitem || strcmp(oitem->path, nitem->path) > 0)) {
int is_ignored;
git_delta_t delta_type = GIT_DELTA_ADDED;
@@ -418,17 +542,27 @@ static int diff_from_iterators(
if (ignore_prefix != NULL &&
git__prefixcmp(nitem->path, ignore_prefix) == 0)
{
- error = git_iterator_advance(new_iter, &nitem);
+ if (git_iterator_advance(new_iter, &nitem) < 0)
+ goto fail;
+
continue;
}
is_ignored = git_iterator_current_is_ignored(new_iter);
if (S_ISDIR(nitem->mode)) {
- if (git__prefixcmp(oitem->path, nitem->path) == 0) {
+ /* recurse into directory if explicitly requested or
+ * if there are tracked items inside the directory
+ */
+ if ((diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) ||
+ (oitem && git__prefixcmp(oitem->path, nitem->path) == 0))
+ {
if (is_ignored)
ignore_prefix = nitem->path;
- error = git_iterator_advance_into_directory(new_iter, &nitem);
+
+ if (git_iterator_advance_into_directory(new_iter, &nitem) < 0)
+ goto fail;
+
continue;
}
delta_type = GIT_DELTA_UNTRACKED;
@@ -438,36 +572,35 @@ static int diff_from_iterators(
else if (new_iter->type == GIT_ITERATOR_WORKDIR)
delta_type = GIT_DELTA_UNTRACKED;
- error = diff_delta__from_one(diff, delta_type, nitem);
- if (error == GIT_SUCCESS)
- error = git_iterator_advance(new_iter, &nitem);
- continue;
+ if (diff_delta__from_one(diff, delta_type, nitem) < 0 ||
+ git_iterator_advance(new_iter, &nitem) < 0)
+ goto fail;
}
/* otherwise item paths match, so create MODIFIED record
* (or ADDED and DELETED pair if type changed)
*/
- assert(oitem && nitem && strcmp(oitem->path, nitem->path) == 0);
+ else {
+ assert(oitem && nitem && strcmp(oitem->path, nitem->path) == 0);
- error = maybe_modified(old_iter, oitem, new_iter, nitem, diff);
- if (error == GIT_SUCCESS)
- error = git_iterator_advance(old_iter, &oitem);
- if (error == GIT_SUCCESS)
- error = git_iterator_advance(new_iter, &nitem);
+ if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
+ git_iterator_advance(old_iter, &oitem) < 0 ||
+ git_iterator_advance(new_iter, &nitem) < 0)
+ goto fail;
+ }
}
-cleanup:
git_iterator_free(old_iter);
git_iterator_free(new_iter);
-
- if (error != GIT_SUCCESS) {
- git_diff_list_free(diff);
- diff = NULL;
- }
-
*diff_ptr = diff;
+ return 0;
- return error;
+fail:
+ git_iterator_free(old_iter);
+ git_iterator_free(new_iter);
+ git_diff_list_free(diff);
+ *diff_ptr = NULL;
+ return -1;
}
@@ -478,14 +611,13 @@ int git_diff_tree_to_tree(
git_tree *new_tree,
git_diff_list **diff)
{
- int error;
git_iterator *a = NULL, *b = NULL;
assert(repo && old_tree && new_tree && diff);
- if ((error = git_iterator_for_tree(repo, old_tree, &a)) < GIT_SUCCESS ||
- (error = git_iterator_for_tree(repo, new_tree, &b)) < GIT_SUCCESS)
- return error;
+ if (git_iterator_for_tree(repo, old_tree, &a) < 0 ||
+ git_iterator_for_tree(repo, new_tree, &b) < 0)
+ return -1;
return diff_from_iterators(repo, opts, a, b, diff);
}
@@ -496,14 +628,13 @@ int git_diff_index_to_tree(
git_tree *old_tree,
git_diff_list **diff)
{
- int error;
git_iterator *a = NULL, *b = NULL;
assert(repo && old_tree && diff);
- if ((error = git_iterator_for_tree(repo, old_tree, &a)) < GIT_SUCCESS ||
- (error = git_iterator_for_index(repo, &b)) < GIT_SUCCESS)
- return error;
+ if (git_iterator_for_tree(repo, old_tree, &a) < 0 ||
+ git_iterator_for_index(repo, &b) < 0)
+ return -1;
return diff_from_iterators(repo, opts, a, b, diff);
}
@@ -513,14 +644,13 @@ int git_diff_workdir_to_index(
const git_diff_options *opts,
git_diff_list **diff)
{
- int error;
git_iterator *a = NULL, *b = NULL;
assert(repo && diff);
- if ((error = git_iterator_for_index(repo, &a)) < GIT_SUCCESS ||
- (error = git_iterator_for_workdir(repo, &b)) < GIT_SUCCESS)
- return error;
+ if (git_iterator_for_index(repo, &a) < 0 ||
+ git_iterator_for_workdir(repo, &b) < 0)
+ return -1;
return diff_from_iterators(repo, opts, a, b, diff);
}
@@ -532,14 +662,13 @@ int git_diff_workdir_to_tree(
git_tree *old_tree,
git_diff_list **diff)
{
- int error;
git_iterator *a = NULL, *b = NULL;
assert(repo && old_tree && diff);
- if ((error = git_iterator_for_tree(repo, old_tree, &a)) < GIT_SUCCESS ||
- (error = git_iterator_for_workdir(repo, &b)) < GIT_SUCCESS)
- return error;
+ if (git_iterator_for_tree(repo, old_tree, &a) < 0 ||
+ git_iterator_for_workdir(repo, &b) < 0)
+ return -1;
return diff_from_iterators(repo, opts, a, b, diff);
}
@@ -548,52 +677,53 @@ int git_diff_merge(
git_diff_list *onto,
const git_diff_list *from)
{
- int error;
- unsigned int i = 0, j = 0;
+ int error = 0;
+ git_pool onto_pool;
git_vector onto_new;
git_diff_delta *delta;
+ unsigned int i, j;
+
+ assert(onto && from);
- error = git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp);
- if (error < GIT_SUCCESS)
- return error;
+ if (!from->deltas.length)
+ return 0;
- while (i < onto->deltas.length || j < from->deltas.length) {
- git_diff_delta *o = git_vector_get(&onto->deltas, i);
- const git_diff_delta *f = git_vector_get_const(&from->deltas, j);
- const char *opath =
- !o ? NULL : o->old_file.path ? o->old_file.path : o->new_file.path;
- const char *fpath =
- !f ? NULL : f->old_file.path ? f->old_file.path : f->new_file.path;
+ if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0 ||
+ git_pool_init(&onto_pool, 1, 0) < 0)
+ return -1;
- if (opath && (!fpath || strcmp(opath, fpath) < 0)) {
- delta = diff_delta__dup(o);
+ for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
+ git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
+ const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
+ int cmp = !f ? -1 : !o ? 1 : strcmp(o->old_file.path, f->old_file.path);
+
+ if (cmp < 0) {
+ delta = diff_delta__dup(o, &onto_pool);
i++;
- } else if (fpath && (!opath || strcmp(opath, fpath) > 0)) {
- delta = diff_delta__dup(f);
+ } else if (cmp > 0) {
+ delta = diff_delta__dup(f, &onto_pool);
j++;
} else {
- delta = diff_delta__merge_like_cgit(o, f);
+ delta = diff_delta__merge_like_cgit(o, f, &onto_pool);
i++;
j++;
}
- if (!delta)
- error = GIT_ENOMEM;
- else
- error = git_vector_insert(&onto_new, delta);
-
- if (error != GIT_SUCCESS)
+ if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
break;
}
- if (error == GIT_SUCCESS) {
+ if (!error) {
git_vector_swap(&onto->deltas, &onto_new);
+ git_pool_swap(&onto->pool, &onto_pool);
onto->new_src = from->new_src;
}
git_vector_foreach(&onto_new, i, delta)
- diff_delta__free(delta);
+ git__free(delta);
git_vector_free(&onto_new);
+ git_pool_clear(&onto_pool);
return error;
}
+
diff --git a/src/diff.h b/src/diff.h
index 7d69199ea..ac2457956 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -12,13 +12,28 @@
#include "buffer.h"
#include "iterator.h"
#include "repository.h"
+#include "pool.h"
+
+#define DIFF_OLD_PREFIX_DEFAULT "a/"
+#define DIFF_NEW_PREFIX_DEFAULT "b/"
+
+enum {
+ GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
+ GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */
+ GIT_DIFFCAPS_TRUST_EXEC_BIT = (1 << 2), /* use st_mode exec bit? */
+ GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
+ GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
+};
struct git_diff_list {
git_repository *repo;
git_diff_options opts;
+ git_vector pathspec;
git_vector deltas; /* vector of git_diff_file_delta */
+ git_pool pool;
git_iterator_type_t old_src;
git_iterator_type_t new_src;
+ uint32_t diffcaps;
};
#endif
diff --git a/src/diff_output.c b/src/diff_output.c
index 23f74d216..ca28fd01e 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -19,9 +19,10 @@ typedef struct {
git_diff_list *diff;
void *cb_data;
git_diff_hunk_fn hunk_cb;
- git_diff_line_fn line_cb;
+ git_diff_data_fn line_cb;
unsigned int index;
git_diff_delta *delta;
+ git_diff_range range;
} diff_output_info;
static int read_next_int(const char **str, int *value)
@@ -35,31 +36,41 @@ static int read_next_int(const char **str, int *value)
v = (v * 10) + (*scan - '0');
*str = scan;
*value = v;
- return (digits > 0) ? GIT_SUCCESS : GIT_ENOTFOUND;
+ return (digits > 0) ? 0 : -1;
}
static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
{
- int err = GIT_SUCCESS;
diff_output_info *info = priv;
if (len == 1 && info->hunk_cb) {
git_diff_range range = { -1, 0, -1, 0 };
+ const char *scan = bufs[0].ptr;
/* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
- if (bufs[0].ptr[0] == '@') {
- const char *scan = bufs[0].ptr;
- if (!(err = read_next_int(&scan, &range.old_start)) && *scan == ',')
- err = read_next_int(&scan, &range.old_lines);
- if (!err &&
- !(err = read_next_int(&scan, &range.new_start)) && *scan == ',')
- err = read_next_int(&scan, &range.new_lines);
- if (!err && range.old_start >= 0 && range.new_start >= 0)
- err = info->hunk_cb(
- info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size);
- }
+ if (*scan != '@')
+ return -1;
+
+ if (read_next_int(&scan, &range.old_start) < 0)
+ return -1;
+ if (*scan == ',' && read_next_int(&scan, &range.old_lines) < 0)
+ return -1;
+
+ if (read_next_int(&scan, &range.new_start) < 0)
+ return -1;
+ if (*scan == ',' && read_next_int(&scan, &range.new_lines) < 0)
+ return -1;
+
+ if (range.old_start < 0 || range.new_start < 0)
+ return -1;
+
+ memcpy(&info->range, &range, sizeof(git_diff_range));
+
+ return info->hunk_cb(
+ info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size);
}
- else if ((len == 2 || len == 3) && info->line_cb) {
+
+ if ((len == 2 || len == 3) && info->line_cb) {
int origin;
/* expect " "/"-"/"+", then data, then maybe newline */
@@ -68,41 +79,43 @@ static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
GIT_DIFF_LINE_CONTEXT;
- err = info->line_cb(
- info->cb_data, info->delta, origin, bufs[1].ptr, bufs[1].size);
+ if (info->line_cb(
+ info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size) < 0)
+ return -1;
/* deal with adding and removing newline at EOF */
- if (err == GIT_SUCCESS && len == 3) {
+ if (len == 3) {
if (origin == GIT_DIFF_LINE_ADDITION)
origin = GIT_DIFF_LINE_ADD_EOFNL;
else
origin = GIT_DIFF_LINE_DEL_EOFNL;
- err = info->line_cb(
- info->cb_data, info->delta, origin, bufs[2].ptr, bufs[2].size);
+ return info->line_cb(
+ info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size);
}
}
- return err;
+ return 0;
}
#define BINARY_DIFF_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY)
-static int set_file_is_binary_by_attr(git_repository *repo, git_diff_file *file)
+static int update_file_is_binary_by_attr(git_repository *repo, git_diff_file *file)
{
const char *value;
- int error = git_attr_get(repo, file->path, "diff", &value);
- if (error != GIT_SUCCESS)
- return error;
+ if (git_attr_get(repo, file->path, "diff", &value) < 0)
+ return -1;
+
if (GIT_ATTR_FALSE(value))
file->flags |= GIT_DIFF_FILE_BINARY;
else if (GIT_ATTR_TRUE(value))
file->flags |= GIT_DIFF_FILE_NOT_BINARY;
/* otherwise leave file->flags alone */
- return error;
+
+ return 0;
}
-static void set_delta_is_binary(git_diff_delta *delta)
+static void update_delta_is_binary(git_diff_delta *delta)
{
if ((delta->old_file.flags & GIT_DIFF_FILE_BINARY) != 0 ||
(delta->new_file.flags & GIT_DIFF_FILE_BINARY) != 0)
@@ -117,7 +130,7 @@ static int file_is_binary_by_attr(
git_diff_list *diff,
git_diff_delta *delta)
{
- int error, mirror_new;
+ int error = 0, mirror_new;
delta->binary = -1;
@@ -128,7 +141,7 @@ static int file_is_binary_by_attr(
delta->old_file.flags |= GIT_DIFF_FILE_BINARY;
delta->new_file.flags |= GIT_DIFF_FILE_BINARY;
delta->binary = 1;
- return GIT_SUCCESS;
+ return 0;
}
/* check if user is forcing us to text diff these files */
@@ -136,22 +149,21 @@ static int file_is_binary_by_attr(
delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
delta->binary = 0;
- return GIT_SUCCESS;
+ return 0;
}
/* check diff attribute +, -, or 0 */
- error = set_file_is_binary_by_attr(diff->repo, &delta->old_file);
- if (error != GIT_SUCCESS)
- return error;
+ if (update_file_is_binary_by_attr(diff->repo, &delta->old_file) < 0)
+ return -1;
mirror_new = (delta->new_file.path == delta->old_file.path ||
strcmp(delta->new_file.path, delta->old_file.path) == 0);
if (mirror_new)
delta->new_file.flags &= (delta->old_file.flags & BINARY_DIFF_FLAGS);
else
- error = set_file_is_binary_by_attr(diff->repo, &delta->new_file);
+ error = update_file_is_binary_by_attr(diff->repo, &delta->new_file);
- set_delta_is_binary(delta);
+ update_delta_is_binary(delta);
return error;
}
@@ -191,11 +203,11 @@ static int file_is_binary_by_content(
delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
}
- set_delta_is_binary(delta);
+ update_delta_is_binary(delta);
/* TODO: if value != NULL, implement diff drivers */
- return GIT_SUCCESS;
+ return 0;
}
static void setup_xdiff_options(
@@ -226,17 +238,15 @@ static int get_blob_content(
git_map *map,
git_blob **blob)
{
- int error;
-
if (git_oid_iszero(oid))
- return GIT_SUCCESS;
+ return 0;
- if ((error = git_blob_lookup(blob, repo, oid)) == GIT_SUCCESS) {
- map->data = (void *)git_blob_rawcontent(*blob);
- map->len = git_blob_rawsize(*blob);
- }
+ if (git_blob_lookup(blob, repo, oid) < 0)
+ return -1;
- return error;
+ map->data = (void *)git_blob_rawcontent(*blob);
+ map->len = git_blob_rawsize(*blob);
+ return 0;
}
static int get_workdir_content(
@@ -244,35 +254,33 @@ static int get_workdir_content(
git_diff_file *file,
git_map *map)
{
- git_buf full_path = GIT_BUF_INIT;
- int error = git_buf_joinpath(
- &full_path, git_repository_workdir(repo), file->path);
- if (error != GIT_SUCCESS)
- return error;
+ int error = 0;
+ git_buf path = GIT_BUF_INIT;
+
+ if (git_buf_joinpath(&path, git_repository_workdir(repo), file->path) < 0)
+ return -1;
if (S_ISLNK(file->mode)) {
+ ssize_t read_len;
+
file->flags |= GIT_DIFF_FILE_FREE_DATA;
file->flags |= GIT_DIFF_FILE_BINARY;
map->data = git__malloc((size_t)file->size + 1);
- if (map->data == NULL)
- error = GIT_ENOMEM;
- else {
- ssize_t read_len =
- p_readlink(full_path.ptr, map->data, (size_t)file->size + 1);
- if (read_len != (ssize_t)file->size)
- error = git__throw(
- GIT_EOSERR, "Failed to read symlink %s", file->path);
- else
- map->len = read_len;
-
- }
+ GITERR_CHECK_ALLOC(map->data);
+
+ read_len = p_readlink(path.ptr, map->data, (size_t)file->size + 1);
+ if (read_len != (ssize_t)file->size) {
+ giterr_set(GITERR_OS, "Failed to read symlink '%s'", file->path);
+ error = -1;
+ } else
+ map->len = read_len;
}
else {
- error = git_futils_mmap_ro_file(map, full_path.ptr);
+ error = git_futils_mmap_ro_file(map, path.ptr);
file->flags |= GIT_DIFF_FILE_UNMAP_DATA;
}
- git_buf_free(&full_path);
+ git_buf_free(&path);
return error;
}
@@ -298,9 +306,9 @@ int git_diff_foreach(
void *data,
git_diff_file_fn file_cb,
git_diff_hunk_fn hunk_cb,
- git_diff_line_fn line_cb)
+ git_diff_data_fn line_cb)
{
- int error = GIT_SUCCESS;
+ int error = 0;
diff_output_info info;
git_diff_delta *delta;
xpparam_t xdiff_params;
@@ -322,7 +330,8 @@ int git_diff_foreach(
git_map old_data, new_data;
mmfile_t old_xdiff_data, new_xdiff_data;
- if (delta->status == GIT_DELTA_UNMODIFIED)
+ if (delta->status == GIT_DELTA_UNMODIFIED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
continue;
if (delta->status == GIT_DELTA_IGNORED &&
@@ -333,8 +342,7 @@ int git_diff_foreach(
(diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
continue;
- error = file_is_binary_by_attr(diff, delta);
- if (error < GIT_SUCCESS)
+ if ((error = file_is_binary_by_attr(diff, delta)) < 0)
goto cleanup;
old_data.data = "";
@@ -358,7 +366,8 @@ int git_diff_foreach(
else
error = get_blob_content(
diff->repo, &delta->old_file.oid, &old_data, &old_blob);
- if (error != GIT_SUCCESS)
+
+ if (error < 0)
goto cleanup;
}
@@ -372,13 +381,15 @@ int git_diff_foreach(
else
error = get_blob_content(
diff->repo, &delta->new_file.oid, &new_data, &new_blob);
- if (error != GIT_SUCCESS)
+
+ if (error < 0)
goto cleanup;
if ((delta->new_file.flags | GIT_DIFF_FILE_VALID_OID) == 0) {
error = git_odb_hash(
&delta->new_file.oid, new_data.data, new_data.len, GIT_OBJ_BLOB);
- if (error != GIT_SUCCESS)
+
+ if (error < 0)
goto cleanup;
/* since we did not have the definitive oid, we may have
@@ -386,7 +397,8 @@ int git_diff_foreach(
*/
if (git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid) == 0) {
delta->status = GIT_DELTA_UNMODIFIED;
- goto cleanup;
+ if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+ goto cleanup;
}
}
}
@@ -397,7 +409,7 @@ int git_diff_foreach(
if (delta->binary == -1) {
error = file_is_binary_by_content(
diff, delta, &old_data, &new_data);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
}
@@ -407,7 +419,7 @@ int git_diff_foreach(
if (file_cb != NULL) {
error = file_cb(data, delta, (float)info.index / diff->deltas.length);
- if (error != GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
}
@@ -434,7 +446,7 @@ cleanup:
release_content(&delta->old_file, &old_data, old_blob);
release_content(&delta->new_file, &new_data, new_blob);
- if (error != GIT_SUCCESS)
+ if (error < 0)
break;
}
@@ -444,7 +456,7 @@ cleanup:
typedef struct {
git_diff_list *diff;
- git_diff_output_fn print_cb;
+ git_diff_data_fn print_cb;
void *cb_data;
git_buf *buf;
} diff_print_info;
@@ -479,7 +491,7 @@ static int print_compact(void *data, git_diff_delta *delta, float progress)
}
if (!code)
- return GIT_SUCCESS;
+ return 0;
old_suffix = pick_suffix(delta->old_file.mode);
new_suffix = pick_suffix(delta->new_file.mode);
@@ -499,16 +511,16 @@ static int print_compact(void *data, git_diff_delta *delta, float progress)
else
git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path);
- if (git_buf_lasterror(pi->buf) != GIT_SUCCESS)
- return git_buf_lasterror(pi->buf);
+ if (git_buf_oom(pi->buf))
+ return -1;
- return pi->print_cb(pi->cb_data, GIT_DIFF_LINE_FILE_HDR, pi->buf->ptr);
+ return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
}
int git_diff_print_compact(
git_diff_list *diff,
void *cb_data,
- git_diff_output_fn print_cb)
+ git_diff_data_fn print_cb)
{
int error;
git_buf buf = GIT_BUF_INIT;
@@ -551,32 +563,42 @@ static int print_oid_range(diff_print_info *pi, git_diff_delta *delta)
git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid);
}
- return git_buf_lasterror(pi->buf);
+ if (git_buf_oom(pi->buf))
+ return -1;
+
+ return 0;
}
static int print_patch_file(void *data, git_diff_delta *delta, float progress)
{
- int error;
diff_print_info *pi = data;
const char *oldpfx = pi->diff->opts.old_prefix;
const char *oldpath = delta->old_file.path;
const char *newpfx = pi->diff->opts.new_prefix;
const char *newpath = delta->new_file.path;
+ int result;
GIT_UNUSED(progress);
+ if (!oldpfx)
+ oldpfx = DIFF_OLD_PREFIX_DEFAULT;
+
+ if (!newpfx)
+ newpfx = DIFF_NEW_PREFIX_DEFAULT;
+
git_buf_clear(pi->buf);
git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
- if ((error = print_oid_range(pi, delta)) < GIT_SUCCESS)
- return error;
+
+ if (print_oid_range(pi, delta) < 0)
+ return -1;
if (git_oid_iszero(&delta->old_file.oid)) {
oldpfx = "";
oldpath = "/dev/null";
}
if (git_oid_iszero(&delta->new_file.oid)) {
- oldpfx = "";
- oldpath = "/dev/null";
+ newpfx = "";
+ newpath = "/dev/null";
}
if (delta->binary != 1) {
@@ -584,21 +606,24 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress)
git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
}
- if (git_buf_lasterror(pi->buf) != GIT_SUCCESS)
- return git_buf_lasterror(pi->buf);
+ if (git_buf_oom(pi->buf))
+ return -1;
- error = pi->print_cb(pi->cb_data, GIT_DIFF_LINE_FILE_HDR, pi->buf->ptr);
- if (error != GIT_SUCCESS || delta->binary != 1)
- return error;
+ result = pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
+ if (result < 0)
+ return result;
+
+ if (delta->binary != 1)
+ return 0;
git_buf_clear(pi->buf);
git_buf_printf(
pi->buf, "Binary files %s%s and %s%s differ\n",
oldpfx, oldpath, newpfx, newpath);
- if (git_buf_lasterror(pi->buf) != GIT_SUCCESS)
- return git_buf_lasterror(pi->buf);
+ if (git_buf_oom(pi->buf))
+ return -1;
- return pi->print_cb(pi->cb_data, GIT_DIFF_LINE_BINARY, pi->buf->ptr);
+ return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_BINARY, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
}
static int print_patch_hunk(
@@ -610,28 +635,23 @@ static int print_patch_hunk(
{
diff_print_info *pi = data;
- GIT_UNUSED(d);
- GIT_UNUSED(r);
-
git_buf_clear(pi->buf);
+ if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
+ return -1;
- if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) == GIT_SUCCESS)
- return pi->print_cb(pi->cb_data, GIT_DIFF_LINE_HUNK_HDR, pi->buf->ptr);
- else
- return git_buf_lasterror(pi->buf);
+ return pi->print_cb(pi->cb_data, d, r, GIT_DIFF_LINE_HUNK_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
}
static int print_patch_line(
void *data,
git_diff_delta *delta,
+ git_diff_range *range,
char line_origin, /* GIT_DIFF_LINE value from above */
const char *content,
size_t content_len)
{
diff_print_info *pi = data;
- GIT_UNUSED(delta);
-
git_buf_clear(pi->buf);
if (line_origin == GIT_DIFF_LINE_ADDITION ||
@@ -641,16 +661,16 @@ static int print_patch_line(
else if (content_len > 0)
git_buf_printf(pi->buf, "%.*s", (int)content_len, content);
- if (git_buf_lasterror(pi->buf) != GIT_SUCCESS)
- return git_buf_lasterror(pi->buf);
+ if (git_buf_oom(pi->buf))
+ return -1;
- return pi->print_cb(pi->cb_data, line_origin, pi->buf->ptr);
+ return pi->print_cb(pi->cb_data, delta, range, line_origin, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
}
int git_diff_print_patch(
git_diff_list *diff,
void *cb_data,
- git_diff_output_fn print_cb)
+ git_diff_data_fn print_cb)
{
int error;
git_buf buf = GIT_BUF_INIT;
@@ -676,7 +696,7 @@ int git_diff_blobs(
git_diff_options *options,
void *cb_data,
git_diff_hunk_fn hunk_cb,
- git_diff_line_fn line_cb)
+ git_diff_data_fn line_cb)
{
diff_output_info info;
git_diff_delta delta;
@@ -732,5 +752,5 @@ int git_diff_blobs(
xdl_diff(&old_data, &new_data, &xdiff_params, &xdiff_config, &xdiff_callback);
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/errors.c b/src/errors.c
index 58e0976f2..7a6bbd654 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -6,6 +6,7 @@
*/
#include "common.h"
#include "global.h"
+#include "posix.h"
#include <stdarg.h>
static struct {
@@ -39,7 +40,7 @@ static struct {
{GIT_EEXISTS, "A reference with this name already exists"},
{GIT_EOVERFLOW, "The given integer literal is too large to be parsed"},
{GIT_ENOTNUM, "The given literal is not a valid number"},
- {GIT_EAMBIGUOUSOIDPREFIX, "The given oid prefix is ambiguous"},
+ {GIT_EAMBIGUOUS, "The given oid prefix is ambiguous"},
};
const char *git_strerror(int num)
@@ -91,8 +92,12 @@ const char *git_lasterror(void)
{
char *last_error = GIT_GLOBAL->error.last;
- if (!last_error[0])
+ if (!last_error[0]) {
+ const git_error *err = git_error_last();
+ if (err != NULL)
+ return err->message;
return NULL;
+ }
return last_error;
}
@@ -102,3 +107,104 @@ void git_clearerror(void)
char *last_error = GIT_GLOBAL->error.last;
last_error[0] = '\0';
}
+
+/********************************************
+ * New error handling
+ ********************************************/
+
+static git_error g_git_oom_error = {
+ "Out of memory",
+ GITERR_NOMEMORY
+};
+
+void giterr_set_oom(void)
+{
+ GIT_GLOBAL->last_error = &g_git_oom_error;
+}
+
+void giterr_set(int error_class, const char *string, ...)
+{
+ char error_str[1024];
+ va_list arglist;
+
+ /* Grab errno before calling vsnprintf() so it won't be overwritten */
+ const char *os_error_msg =
+ (error_class == GITERR_OS && errno != 0) ? strerror(errno) : NULL;
+#ifdef GIT_WIN32
+ DWORD dwLastError = GetLastError();
+#endif
+
+ va_start(arglist, string);
+ p_vsnprintf(error_str, sizeof(error_str), string, arglist);
+ va_end(arglist);
+
+ /* automatically suffix strerror(errno) for GITERR_OS errors */
+ if (error_class == GITERR_OS) {
+ if (os_error_msg != NULL) {
+ strncat(error_str, ": ", sizeof(error_str));
+ strncat(error_str, os_error_msg, sizeof(error_str));
+ errno = 0; /* reset so same error won't be reported twice */
+ }
+#ifdef GIT_WIN32
+ else if (dwLastError != 0) {
+ LPVOID lpMsgBuf = NULL;
+
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, dwLastError, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
+
+ if (lpMsgBuf) {
+ strncat(error_str, ": ", sizeof(error_str));
+ strncat(error_str, (const char *)lpMsgBuf, sizeof(error_str));
+ LocalFree(lpMsgBuf);
+ }
+
+ SetLastError(0);
+ }
+#endif
+ }
+
+ giterr_set_str(error_class, error_str);
+}
+
+void giterr_set_str(int error_class, const char *string)
+{
+ git_error *error = &GIT_GLOBAL->error_t;
+
+ git__free(error->message);
+
+ error->message = git__strdup(string);
+ error->klass = error_class;
+
+ if (error->message == NULL) {
+ giterr_set_oom();
+ return;
+ }
+
+ GIT_GLOBAL->last_error = error;
+}
+
+void giterr_set_regex(const regex_t *regex, int error_code)
+{
+ char error_buf[1024];
+ regerror(error_code, regex, error_buf, sizeof(error_buf));
+ giterr_set_str(GITERR_REGEX, error_buf);
+}
+
+void giterr_clear(void)
+{
+ GIT_GLOBAL->last_error = NULL;
+}
+
+const git_error *git_error_last(void)
+{
+ return GIT_GLOBAL->last_error;
+}
+
+void git_error_clear(void)
+{
+ giterr_clear();
+}
+
diff --git a/src/fetch.c b/src/fetch.c
index 23208f17e..6fe1b5676 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -9,6 +9,7 @@
#include "git2/oid.h"
#include "git2/refs.h"
#include "git2/revwalk.h"
+#include "git2/indexer.h"
#include "common.h"
#include "transport.h"
@@ -28,19 +29,21 @@ struct filter_payload {
static int filter_ref__cb(git_remote_head *head, void *payload)
{
struct filter_payload *p = payload;
- int error;
+ int ret;
if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) {
p->found_head = 1;
} else {
/* If it doesn't match the refpec, we don't want it */
- error = git_refspec_src_match(p->spec, head->name);
+ ret = git_refspec_src_match(p->spec, head->name);
- if (error == GIT_ENOMATCH)
- return GIT_SUCCESS;
+ if (ret == GIT_ENOMATCH)
+ return 0;
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Error matching remote ref name");
+ if (ret < GIT_SUCCESS) {
+ giterr_set(GITERR_NET, "Error matching remote ref name");
+ return -1;
+ }
}
/* If we have the object, mark it so we don't ask for it */
@@ -54,7 +57,6 @@ static int filter_ref__cb(git_remote_head *head, void *payload)
static int filter_wants(git_remote *remote)
{
- int error;
struct filter_payload p;
git_vector_clear(&remote->refs);
@@ -69,9 +71,8 @@ static int filter_wants(git_remote *remote)
p.found_head = 0;
p.remote = remote;
- error = git_repository_odb__weakptr(&p.odb, remote->repo);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_repository_odb__weakptr(&p.odb, remote->repo) < 0)
+ return -1;
return remote->transport->ls(remote->transport, &filter_ref__cb, &p);
}
@@ -83,19 +84,16 @@ static int filter_wants(git_remote *remote)
*/
int git_fetch_negotiate(git_remote *remote)
{
- int error;
git_transport *t = remote->transport;
- error = filter_wants(remote);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to filter the reference list for wants");
+ if (filter_wants(remote) < 0) {
+ giterr_set(GITERR_NET, "Failed to filter the reference list for wants");
+ return -1;
+ }
/* Don't try to negotiate when we don't want anything */
- if (remote->refs.length == 0)
- return GIT_SUCCESS;
-
- if (!remote->need_pack)
- return GIT_SUCCESS;
+ if (remote->refs.length == 0 || !remote->need_pack)
+ return 0;
/*
* Now we have everything set up so we can start tell the server
@@ -104,75 +102,103 @@ int git_fetch_negotiate(git_remote *remote)
return t->negotiate_fetch(t, remote->repo, &remote->refs);
}
-int git_fetch_download_pack(char **out, git_remote *remote)
+int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
{
- if(!remote->need_pack) {
- *out = NULL;
- return GIT_SUCCESS;
- }
+ if(!remote->need_pack)
+ return 0;
- return remote->transport->download_pack(out, remote->transport, remote->repo);
+ return remote->transport->download_pack(remote->transport, remote->repo, bytes, stats);
}
/* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
int git_fetch__download_pack(
- char **out,
const char *buffered,
size_t buffered_size,
GIT_SOCKET fd,
- git_repository *repo)
+ git_repository *repo,
+ git_off_t *bytes,
+ git_indexer_stats *stats)
{
- git_filebuf file = GIT_FILEBUF_INIT;
- int error;
+ int recvd;
char buff[1024];
- git_buf path = GIT_BUF_INIT;
- static const char suff[] = "/objects/pack/pack-received";
gitno_buffer buf;
+ git_indexer_stream *idx;
gitno_buffer_setup(&buf, buff, sizeof(buff), fd);
if (memcmp(buffered, "PACK", strlen("PACK"))) {
- return git__throw(GIT_ERROR, "The pack doesn't start with the signature");
+ giterr_set(GITERR_NET, "The pack doesn't start with the signature");
+ return -1;
}
- error = git_buf_joinpath(&path, repo->path_repository, suff);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
+ return -1;
- error = git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ memset(stats, 0, sizeof(git_indexer_stats));
+ if (git_indexer_stream_add(idx, buffered, buffered_size, stats) < 0)
+ goto on_error;
- /* Part of the packfile has been received, don't loose it */
- error = git_filebuf_write(&file, buffered, buffered_size);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ *bytes = buffered_size;
- while (1) {
- error = git_filebuf_write(&file, buf.data, buf.offset);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ do {
+ if (git_indexer_stream_add(idx, buf.data, buf.offset, stats) < 0)
+ goto on_error;
gitno_consume_n(&buf, buf.offset);
- error = gitno_recv(&buf);
- if (error < GIT_SUCCESS)
- goto cleanup;
- if (error == 0) /* Orderly shutdown */
- break;
- }
+ if ((recvd = gitno_recv(&buf)) < 0)
+ goto on_error;
+
+ *bytes += recvd;
+ } while(recvd > 0);
+
+ if (git_indexer_stream_finalize(idx, stats))
+ goto on_error;
+
+ git_indexer_stream_free(idx);
+ return 0;
+
+on_error:
+ git_indexer_stream_free(idx);
+ return -1;
+}
+
+int git_fetch_setup_walk(git_revwalk **out, git_repository *repo)
+{
+ git_revwalk *walk;
+ git_strarray refs;
+ unsigned int i;
+ git_reference *ref;
+
+ if (git_reference_listall(&refs, repo, GIT_REF_LISTALL) < 0)
+ return -1;
+
+ if (git_revwalk_new(&walk, repo) < 0)
+ return -1;
+
+ git_revwalk_sorting(walk, GIT_SORT_TIME);
+
+ for (i = 0; i < refs.count; ++i) {
+ /* No tags */
+ if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
+ continue;
+
+ if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0)
+ goto on_error;
+
+ if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
+ continue;
+ if (git_revwalk_push(walk, git_reference_oid(ref)) < 0)
+ goto on_error;
- *out = git__strdup(file.path_lock);
- if (*out == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
+ git_reference_free(ref);
}
- /* A bit dodgy, but we need to keep the pack at the temporary path */
- error = git_filebuf_commit_at(&file, file.path_lock, GIT_PACK_FILE_MODE);
-cleanup:
- if (error < GIT_SUCCESS)
- git_filebuf_cleanup(&file);
- git_buf_free(&path);
+ git_strarray_free(&refs);
+ *out = walk;
+ return 0;
- return error;
+on_error:
+ git_reference_free(ref);
+ git_strarray_free(&refs);
+ return -1;
}
diff --git a/src/fetch.h b/src/fetch.h
index c1ab84034..b3192a563 100644
--- a/src/fetch.h
+++ b/src/fetch.h
@@ -10,9 +10,10 @@
#include "netops.h"
int git_fetch_negotiate(git_remote *remote);
-int git_fetch_download_pack(char **out, git_remote *remote);
+int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
-int git_fetch__download_pack(char **out, const char *buffered, size_t buffered_size,
- GIT_SOCKET fd, git_repository *repo);
+int git_fetch__download_pack(const char *buffered, size_t buffered_size, GIT_SOCKET fd,
+ git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
+int git_fetch_setup_walk(git_revwalk **out, git_repository *repo);
#endif
diff --git a/src/filebuf.c b/src/filebuf.c
index 01df8e2d0..6538aea66 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -14,13 +14,46 @@
static const size_t WRITE_BUFFER_SIZE = (4096 * 2);
+enum buferr_t {
+ BUFERR_OK = 0,
+ BUFERR_WRITE,
+ BUFERR_ZLIB,
+ BUFERR_MEM
+};
+
+#define ENSURE_BUF_OK(buf) if ((buf)->last_error != BUFERR_OK) { return -1; }
+
+static int verify_last_error(git_filebuf *file)
+{
+ switch (file->last_error) {
+ case BUFERR_WRITE:
+ giterr_set(GITERR_OS, "Failed to write out file");
+ return -1;
+
+ case BUFERR_MEM:
+ giterr_set_oom();
+ return -1;
+
+ case BUFERR_ZLIB:
+ giterr_set(GITERR_ZLIB,
+ "Buffer error when writing out ZLib data");
+ return -1;
+
+ default:
+ return 0;
+ }
+}
+
static int lock_file(git_filebuf *file, int flags)
{
- if (git_path_exists(file->path_lock) == 0) {
+ if (git_path_exists(file->path_lock) == true) {
if (flags & GIT_FILEBUF_FORCE)
p_unlink(file->path_lock);
- else
- return git__throw(GIT_EOSERR, "Failed to lock file");
+ else {
+ giterr_set(GITERR_OS,
+ "Failed to lock file '%s' for writing", file->path_lock);
+ return -1;
+ }
}
/* create path to the file buffer is required */
@@ -32,16 +65,22 @@ static int lock_file(git_filebuf *file, int flags)
}
if (file->fd < 0)
- return git__throw(GIT_EOSERR, "Failed to create lock");
+ return -1;
+
+ file->fd_is_open = true;
- if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == 0) {
+ if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == true) {
git_file source;
char buffer[2048];
size_t read_bytes;
source = p_open(file->path_original, O_RDONLY);
- if (source < 0)
- return git__throw(GIT_EOSERR, "Failed to lock file. Could not open %s", file->path_original);
+ if (source < 0) {
+ giterr_set(GITERR_OS,
+ "Failed to open file '%s' for reading",
+ file->path_original);
+ return -1;
+ }
while ((read_bytes = p_read(source, buffer, 2048)) > 0) {
p_write(file->fd, buffer, read_bytes);
@@ -52,15 +91,15 @@ static int lock_file(git_filebuf *file, int flags)
p_close(source);
}
- return GIT_SUCCESS;
+ return 0;
}
void git_filebuf_cleanup(git_filebuf *file)
{
- if (file->fd >= 0)
+ if (file->fd_is_open && file->fd >= 0)
p_close(file->fd);
- if (file->fd >= 0 && file->path_lock && git_path_exists(file->path_lock) == GIT_SUCCESS)
+ if (file->fd_is_open && file->path_lock && git_path_exists(file->path_lock))
p_unlink(file->path_lock);
if (file->digest)
@@ -93,20 +132,21 @@ GIT_INLINE(int) flush_buffer(git_filebuf *file)
static int write_normal(git_filebuf *file, void *source, size_t len)
{
- int result = 0;
-
if (len > 0) {
- result = p_write(file->fd, (void *)source, len);
+ if (p_write(file->fd, (void *)source, len) < 0) {
+ file->last_error = BUFERR_WRITE;
+ return -1;
+ }
+
if (file->digest)
git_hash_update(file->digest, source, len);
}
- return result;
+ return 0;
}
static int write_deflate(git_filebuf *file, void *source, size_t len)
{
- int result = Z_OK;
z_stream *zs = &file->zs;
if (len > 0 || file->flush_mode == Z_FINISH) {
@@ -119,14 +159,17 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
zs->next_out = file->z_buf;
zs->avail_out = (uInt)file->buf_size;
- result = deflate(zs, file->flush_mode);
- if (result == Z_STREAM_ERROR)
- return git__throw(GIT_ERROR, "Failed to deflate input");
+ if (deflate(zs, file->flush_mode) == Z_STREAM_ERROR) {
+ file->last_error = BUFERR_ZLIB;
+ return -1;
+ }
have = file->buf_size - (size_t)zs->avail_out;
- if (p_write(file->fd, file->z_buf, have) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to write to file");
+ if (p_write(file->fd, file->z_buf, have) < 0) {
+ file->last_error = BUFERR_WRITE;
+ return -1;
+ }
} while (zs->avail_out == 0);
@@ -136,38 +179,39 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
git_hash_update(file->digest, source, len);
}
- return GIT_SUCCESS;
+ return 0;
}
int git_filebuf_open(git_filebuf *file, const char *path, int flags)
{
- int error, compression;
+ int compression;
size_t path_len;
- assert(file && path);
-
- if (file->buffer)
- return git__throw(GIT_EINVALIDARGS, "Tried to reopen an open filebuf");
+ /* opening an already open buffer is a programming error;
+ * assert that this never happens instead of returning
+ * an error code */
+ assert(file && path && file->buffer == NULL);
memset(file, 0x0, sizeof(git_filebuf));
+ if (flags & GIT_FILEBUF_DO_NOT_BUFFER)
+ file->do_not_buffer = true;
+
file->buf_size = WRITE_BUFFER_SIZE;
file->buf_pos = 0;
file->fd = -1;
+ file->last_error = BUFERR_OK;
/* Allocate the main cache buffer */
- file->buffer = git__malloc(file->buf_size);
- if (file->buffer == NULL){
- error = GIT_ENOMEM;
- goto cleanup;
+ if (!file->do_not_buffer) {
+ file->buffer = git__malloc(file->buf_size);
+ GITERR_CHECK_ALLOC(file->buffer);
}
/* If we are hashing on-write, allocate a new hash context */
if (flags & GIT_FILEBUF_HASH_CONTENTS) {
- if ((file->digest = git_hash_new_ctx()) == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ file->digest = git_hash_new_ctx();
+ GITERR_CHECK_ALLOC(file->digest);
}
compression = flags >> GIT_FILEBUF_DEFLATE_SHIFT;
@@ -176,16 +220,13 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
if (compression != 0) {
/* Initialize the ZLib stream */
if (deflateInit(&file->zs, compression) != Z_OK) {
- error = git__throw(GIT_EZLIB, "Failed to initialize zlib");
+ giterr_set(GITERR_ZLIB, "Failed to initialize zlib");
goto cleanup;
}
/* Allocate the Zlib cache buffer */
file->z_buf = git__malloc(file->buf_size);
- if (file->z_buf == NULL){
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(file->z_buf);
/* Never flush */
file->flush_mode = Z_NO_FLUSH;
@@ -200,106 +241,101 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
/* Open the file as temporary for locking */
file->fd = git_futils_mktmp(&tmp_path, path);
+
if (file->fd < 0) {
git_buf_free(&tmp_path);
- error = GIT_EOSERR;
goto cleanup;
}
+ file->fd_is_open = true;
/* No original path */
file->path_original = NULL;
file->path_lock = git_buf_detach(&tmp_path);
-
- if (file->path_lock == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(file->path_lock);
} else {
path_len = strlen(path);
/* Save the original path of the file */
file->path_original = git__strdup(path);
- if (file->path_original == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(file->path_original);
/* create the locking path by appending ".lock" to the original */
file->path_lock = git__malloc(path_len + GIT_FILELOCK_EXTLENGTH);
- if (file->path_lock == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(file->path_lock);
memcpy(file->path_lock, file->path_original, path_len);
memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);
/* open the file for locking */
- if ((error = lock_file(file, flags)) < GIT_SUCCESS)
+ if (lock_file(file, flags) < 0)
goto cleanup;
}
- return GIT_SUCCESS;
+ return 0;
cleanup:
git_filebuf_cleanup(file);
- return git__rethrow(error, "Failed to open file buffer for '%s'", path);
+ return -1;
}
int git_filebuf_hash(git_oid *oid, git_filebuf *file)
{
- int error;
-
assert(oid && file && file->digest);
- if ((error = flush_buffer(file)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to get hash for file");
+ flush_buffer(file);
+
+ if (verify_last_error(file) < 0)
+ return -1;
git_hash_final(oid, file->digest);
git_hash_free_ctx(file->digest);
file->digest = NULL;
- return GIT_SUCCESS;
+ return 0;
}
int git_filebuf_commit_at(git_filebuf *file, const char *path, mode_t mode)
{
git__free(file->path_original);
file->path_original = git__strdup(path);
- if (file->path_original == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(file->path_original);
return git_filebuf_commit(file, mode);
}
int git_filebuf_commit(git_filebuf *file, mode_t mode)
{
- int error;
-
/* temporary files cannot be committed */
assert(file && file->path_original);
file->flush_mode = Z_FINISH;
- if ((error = flush_buffer(file)) < GIT_SUCCESS)
- goto cleanup;
+ flush_buffer(file);
+
+ if (verify_last_error(file) < 0)
+ goto on_error;
p_close(file->fd);
file->fd = -1;
+ file->fd_is_open = false;
if (p_chmod(file->path_lock, mode)) {
- error = git__throw(GIT_EOSERR, "Failed to chmod locked file before committing");
- goto cleanup;
+ giterr_set(GITERR_OS, "Failed to set attributes for file at '%s'", file->path_lock);
+ goto on_error;
}
p_unlink(file->path_original);
- error = p_rename(file->path_lock, file->path_original);
+ if (p_rename(file->path_lock, file->path_original) < 0) {
+ giterr_set(GITERR_OS, "Failed to rename lockfile to '%s'", file->path_original);
+ goto on_error;
+ }
+
+ git_filebuf_cleanup(file);
+ return 0;
-cleanup:
+on_error:
git_filebuf_cleanup(file);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to commit locked file from buffer");
- return GIT_SUCCESS;
+ return -1;
}
GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len)
@@ -310,22 +346,25 @@ GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len)
int git_filebuf_write(git_filebuf *file, const void *buff, size_t len)
{
- int error;
const unsigned char *buf = buff;
+ ENSURE_BUF_OK(file);
+
+ if (file->do_not_buffer)
+ return file->write(file, (void *)buff, len);
+
for (;;) {
size_t space_left = file->buf_size - file->buf_pos;
/* cache if it's small */
if (space_left > len) {
add_to_cache(file, buf, len);
- return GIT_SUCCESS;
+ return 0;
}
add_to_cache(file, buf, space_left);
-
- if ((error = flush_buffer(file)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to write to buffer");
+ if (flush_buffer(file) < 0)
+ return -1;
len -= space_left;
buf += space_left;
@@ -334,32 +373,37 @@ int git_filebuf_write(git_filebuf *file, const void *buff, size_t len)
int git_filebuf_reserve(git_filebuf *file, void **buffer, size_t len)
{
- int error;
size_t space_left = file->buf_size - file->buf_pos;
*buffer = NULL;
- if (len > file->buf_size)
- return GIT_ENOMEM;
+ ENSURE_BUF_OK(file);
+
+ if (len > file->buf_size) {
+ file->last_error = BUFERR_MEM;
+ return -1;
+ }
if (space_left <= len) {
- if ((error = flush_buffer(file)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to reserve buffer");
+ if (flush_buffer(file) < 0)
+ return -1;
}
*buffer = (file->buffer + file->buf_pos);
file->buf_pos += len;
- return GIT_SUCCESS;
+ return 0;
}
int git_filebuf_printf(git_filebuf *file, const char *format, ...)
{
va_list arglist;
size_t space_left;
- int len, error;
+ int len, res;
char *tmp_buffer;
+ ENSURE_BUF_OK(file);
+
space_left = file->buf_size - file->buf_pos;
do {
@@ -367,24 +411,28 @@ int git_filebuf_printf(git_filebuf *file, const char *format, ...)
len = p_vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist);
va_end(arglist);
- if (len < 0)
- return git__throw(GIT_EOSERR, "Failed to format string");
+ if (len < 0) {
+ file->last_error = BUFERR_MEM;
+ return -1;
+ }
if ((size_t)len + 1 <= space_left) {
file->buf_pos += len;
- return GIT_SUCCESS;
+ return 0;
}
- if ((error = flush_buffer(file)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to output to buffer");
+ if (flush_buffer(file) < 0)
+ return -1;
space_left = file->buf_size - file->buf_pos;
} while ((size_t)len + 1 <= space_left);
tmp_buffer = git__malloc(len + 1);
- if (!tmp_buffer)
- return GIT_ENOMEM;
+ if (!tmp_buffer) {
+ file->last_error = BUFERR_MEM;
+ return -1;
+ }
va_start(arglist, format);
len = p_vsnprintf(tmp_buffer, len + 1, format, arglist);
@@ -392,12 +440,13 @@ int git_filebuf_printf(git_filebuf *file, const char *format, ...)
if (len < 0) {
git__free(tmp_buffer);
- return git__throw(GIT_EOSERR, "Failed to format string");
+ file->last_error = BUFERR_MEM;
+ return -1;
}
- error = git_filebuf_write(file, tmp_buffer, len);
+ res = git_filebuf_write(file, tmp_buffer, len);
git__free(tmp_buffer);
- return error;
+ return res;
}
diff --git a/src/filebuf.h b/src/filebuf.h
index 371215391..72563b57a 100644
--- a/src/filebuf.h
+++ b/src/filebuf.h
@@ -19,7 +19,8 @@
#define GIT_FILEBUF_APPEND (1 << 2)
#define GIT_FILEBUF_FORCE (1 << 3)
#define GIT_FILEBUF_TEMPORARY (1 << 4)
-#define GIT_FILEBUF_DEFLATE_SHIFT (5)
+#define GIT_FILEBUF_DO_NOT_BUFFER (1 << 5)
+#define GIT_FILEBUF_DEFLATE_SHIFT (6)
#define GIT_FILELOCK_EXTENSION ".lock\0"
#define GIT_FILELOCK_EXTLENGTH 6
@@ -40,25 +41,37 @@ struct git_filebuf {
size_t buf_size, buf_pos;
git_file fd;
+ bool fd_is_open;
+ bool do_not_buffer;
+ int last_error;
};
typedef struct git_filebuf git_filebuf;
#define GIT_FILEBUF_INIT {0}
-/* The git_filebuf object lifecycle is:
+/*
+ * The git_filebuf object lifecycle is:
* - Allocate git_filebuf, preferably using GIT_FILEBUF_INIT.
+ *
* - Call git_filebuf_open() to initialize the filebuf for use.
+ *
* - Make as many calls to git_filebuf_write(), git_filebuf_printf(),
- * git_filebuf_reserve() as you like.
+ * git_filebuf_reserve() as you like. The error codes for these
+ * functions don't need to be checked. They are stored internally
+ * by the file buffer.
+ *
* - While you are writing, you may call git_filebuf_hash() to get
- * the hash of all you have written so far.
+ * the hash of all you have written so far. This function will
+ * fail if any of the previous writes to the buffer failed.
+ *
* - To close the git_filebuf, you may call git_filebuf_commit() or
* git_filebuf_commit_at() to save the file, or
* git_filebuf_cleanup() to abandon the file. All of these will
- * clear the git_filebuf object.
+ * free the git_filebuf object. Likewise, all of these will fail
+ * if any of the previous writes to the buffer failed, and set
+ * an error code accordingly.
*/
-
int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len);
int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len);
int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
diff --git a/src/fileops.c b/src/fileops.c
index 856823afb..bf95f769c 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -10,25 +10,19 @@
int git_futils_mkpath2file(const char *file_path, const mode_t mode)
{
- int error;
+ int result = 0;
git_buf target_folder = GIT_BUF_INIT;
- error = git_path_dirname_r(&target_folder, file_path);
- if (error < GIT_SUCCESS) {
- git_buf_free(&target_folder);
- return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path);
- } else {
- /* reset error */
- error = GIT_SUCCESS;
- }
+ if (git_path_dirname_r(&target_folder, file_path) < 0)
+ return -1;
/* Does the containing folder exist? */
- if (git_path_isdir(target_folder.ptr) != GIT_SUCCESS)
+ if (git_path_isdir(target_folder.ptr) == false)
/* Let's create the tree structure */
- error = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
+ result = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
git_buf_free(&target_folder);
- return error;
+ return result;
}
int git_futils_mktmp(git_buf *path_out, const char *filename)
@@ -39,42 +33,82 @@ int git_futils_mktmp(git_buf *path_out, const char *filename)
git_buf_puts(path_out, "_git2_XXXXXX");
if (git_buf_oom(path_out))
- return git__rethrow(git_buf_lasterror(path_out),
- "Failed to create temporary file for %s", filename);
+ return -1;
- if ((fd = p_mkstemp(path_out->ptr)) < 0)
- return git__throw(GIT_EOSERR, "Failed to create temporary file %s", path_out->ptr);
+ if ((fd = p_mkstemp(path_out->ptr)) < 0) {
+ giterr_set(GITERR_OS,
+ "Failed to create temporary file '%s'", path_out->ptr);
+ return -1;
+ }
return fd;
}
int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode)
{
- if (git_futils_mkpath2file(path, dirmode) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to create file %s", path);
+ int fd;
+
+ if (git_futils_mkpath2file(path, dirmode) < 0)
+ return -1;
- return p_creat(path, mode);
+ fd = p_creat(path, mode);
+ if (fd < 0) {
+ giterr_set(GITERR_OS, "Failed to create file '%s'", path);
+ return -1;
+ }
+
+ return fd;
}
int git_futils_creat_locked(const char *path, const mode_t mode)
{
- int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
- return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to create locked file. Could not open %s", path);
+ int fd;
+
+#ifdef GIT_WIN32
+ wchar_t* buf;
+
+ buf = gitwin_to_utf16(path);
+ fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
+ git__free(buf);
+#else
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
+#endif
+
+ if (fd < 0) {
+ giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
+ return -1;
+ }
+
+ return fd;
}
int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode)
{
- if (git_futils_mkpath2file(path, dirmode) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to create locked file %s", path);
+ if (git_futils_mkpath2file(path, dirmode) < 0)
+ return -1;
return git_futils_creat_locked(path, mode);
}
+int git_futils_open_ro(const char *path)
+{
+ int fd = p_open(path, O_RDONLY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ fd = GIT_ENOTFOUND;
+ giterr_set(GITERR_OS, "Failed to open '%s'", path);
+ }
+ return fd;
+}
+
git_off_t git_futils_filesize(git_file fd)
{
struct stat sb;
- if (p_fstat(fd, &sb))
- return GIT_ERROR;
+
+ if (p_fstat(fd, &sb)) {
+ giterr_set(GITERR_OS, "Failed to stat file descriptor");
+ return -1;
+ }
return sb.st_size;
}
@@ -85,10 +119,10 @@ mode_t git_futils_canonical_mode(mode_t raw_mode)
return S_IFREG | GIT_CANONICAL_PERMS(raw_mode);
else if (S_ISLNK(raw_mode))
return S_IFLNK;
- else if (S_ISDIR(raw_mode))
- return S_IFDIR;
else if (S_ISGITLINK(raw_mode))
return S_IFGITLINK;
+ else if (S_ISDIR(raw_mode))
+ return S_IFDIR;
else
return 0;
}
@@ -104,13 +138,13 @@ int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime,
if (updated != NULL)
*updated = 0;
- if ((fd = p_open(path, O_RDONLY)) < 0) {
- return git__throw(GIT_ENOTFOUND, "Failed to read file '%s': %s", path, strerror(errno));
- }
+ if ((fd = git_futils_open_ro(path)) < 0)
+ return fd;
if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
- close(fd);
- return git__throw(GIT_EOSERR, "Failed to stat file '%s'", path);
+ p_close(fd);
+ giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
+ return -1;
}
/*
@@ -118,7 +152,7 @@ int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime,
* has been modified.
*/
if (mtime != NULL && *mtime >= st.st_mtime) {
- close(fd);
+ p_close(fd);
return 0;
}
@@ -130,8 +164,8 @@ int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime,
git_buf_clear(buf);
if (git_buf_grow(buf, len + 1) < 0) {
- close(fd);
- return GIT_ENOMEM;
+ p_close(fd);
+ return -1;
}
buf->ptr[len] = '\0';
@@ -140,8 +174,9 @@ int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime,
ssize_t read_size = p_read(fd, buf->ptr, len);
if (read_size < 0) {
- close(fd);
- return git__throw(GIT_EOSERR, "Failed to read from FD");
+ p_close(fd);
+ giterr_set(GITERR_OS, "Failed to read descriptor for '%s'", path);
+ return -1;
}
len -= read_size;
@@ -166,10 +201,15 @@ int git_futils_readbuffer(git_buf *buf, const char *path)
int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
{
- if (git_futils_mkpath2file(to, dirmode) < GIT_SUCCESS)
- return GIT_EOSERR; /* The callee already takes care of setting the correct error message. */
+ if (git_futils_mkpath2file(to, dirmode) < 0)
+ return -1;
+
+ if (p_rename(from, to) < 0) {
+ giterr_set(GITERR_OS, "Failed to rename '%s' to '%s'", from, to);
+ return -1;
+ }
- return p_rename(from, to); /* The callee already takes care of setting the correct error message. */
+ return 0;
}
int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
@@ -179,11 +219,19 @@ int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
int git_futils_mmap_ro_file(git_map *out, const char *path)
{
- git_file fd = p_open(path, O_RDONLY /* | O_NOATIME */);
- git_off_t len = git_futils_filesize(fd);
+ git_file fd = git_futils_open_ro(path);
+ git_off_t len;
int result;
- if (!git__is_sizet(len))
- return git__throw(GIT_ERROR, "File `%s` too large to mmap", path);
+
+ if (fd < 0)
+ return fd;
+
+ len = git_futils_filesize(fd);
+ if (!git__is_sizet(len)) {
+ giterr_set(GITERR_OS, "File `%s` too large to mmap", path);
+ return -1;
+ }
+
result = git_futils_mmap_ro(out, fd, 0, (size_t)len);
p_close(fd);
return result;
@@ -196,20 +244,21 @@ void git_futils_mmap_free(git_map *out)
int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
{
- int error, root_path_offset;
+ int root_path_offset;
git_buf make_path = GIT_BUF_INIT;
size_t start;
char *pp, *sp;
+ bool failed = false;
if (base != NULL) {
start = strlen(base);
- error = git_buf_joinpath(&make_path, base, path);
+ if (git_buf_joinpath(&make_path, base, path) < 0)
+ return -1;
} else {
start = 0;
- error = git_buf_puts(&make_path, path);
+ if (git_buf_puts(&make_path, path) < 0)
+ return -1;
}
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create `%s` tree structure", path);
pp = make_path.ptr + start;
@@ -217,14 +266,13 @@ int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
if (root_path_offset > 0)
pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */
- while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != NULL) {
- if (sp != pp && git_path_isdir(make_path.ptr) < GIT_SUCCESS) {
+ while (!failed && (sp = strchr(pp, '/')) != NULL) {
+ if (sp != pp && git_path_isdir(make_path.ptr) == false) {
*sp = 0;
- error = p_mkdir(make_path.ptr, mode);
/* Do not choke while trying to recreate an existing directory */
- if (errno == EEXIST)
- error = GIT_SUCCESS;
+ if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
+ failed = true;
*sp = '/';
}
@@ -232,53 +280,76 @@ int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
pp = sp + 1;
}
- if (*pp != '\0' && error == GIT_SUCCESS) {
- error = p_mkdir(make_path.ptr, mode);
- if (errno == EEXIST)
- error = GIT_SUCCESS;
+ if (*pp != '\0' && !failed) {
+ if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
+ failed = true;
}
git_buf_free(&make_path);
- if (error < GIT_SUCCESS)
- return git__throw(error, "Failed to recursively create `%s` tree structure", path);
+ if (failed) {
+ giterr_set(GITERR_OS,
+ "Failed to create directory structure at '%s'", path);
+ return -1;
+ }
- return GIT_SUCCESS;
+ return 0;
}
static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
{
- int error = GIT_SUCCESS;
- int force = *(int *)opaque;
+ git_directory_removal_type removal_type = *(git_directory_removal_type *)opaque;
+
+ assert(removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY
+ || removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS
+ || removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS);
- if (git_path_isdir(path->ptr) == GIT_SUCCESS) {
- error = git_path_direach(path, _rmdir_recurs_foreach, opaque);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to remove directory `%s`", path->ptr);
- return p_rmdir(path->ptr);
+ if (git_path_isdir(path->ptr) == true) {
+ if (git_path_direach(path, _rmdir_recurs_foreach, opaque) < 0)
+ return -1;
- } else if (force) {
- return p_unlink(path->ptr);
+ if (p_rmdir(path->ptr) < 0) {
+ if (removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS && errno == ENOTEMPTY)
+ return 0;
+
+ giterr_set(GITERR_OS, "Could not remove directory '%s'", path->ptr);
+ return -1;
+ }
+
+ return 0;
}
- return git__rethrow(error, "Failed to remove directory. `%s` is not empty", path->ptr);
+ if (removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS) {
+ if (p_unlink(path->ptr) < 0) {
+ giterr_set(GITERR_OS, "Could not remove directory. File '%s' cannot be removed", path->ptr);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ if (removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY) {
+ giterr_set(GITERR_OS, "Could not remove directory. File '%s' still present", path->ptr);
+ return -1;
+ }
+
+ return 0;
}
-int git_futils_rmdir_r(const char *path, int force)
+int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type)
{
int error;
git_buf p = GIT_BUF_INIT;
error = git_buf_sets(&p, path);
- if (error == GIT_SUCCESS)
- error = _rmdir_recurs_foreach(&force, &p);
+ if (!error)
+ error = _rmdir_recurs_foreach(&removal_type, &p);
git_buf_free(&p);
return error;
}
int git_futils_find_global_file(git_buf *path, const char *filename)
{
- int error;
const char *home = getenv("HOME");
#ifdef GIT_WIN32
@@ -286,19 +357,21 @@ int git_futils_find_global_file(git_buf *path, const char *filename)
home = getenv("USERPROFILE");
#endif
- if (home == NULL)
- return git__throw(GIT_EOSERR, "Failed to open global %s file. "
- "Cannot locate the user's home directory.", filename);
+ if (home == NULL) {
+ giterr_set(GITERR_OS, "Global file lookup failed. "
+ "Cannot locate the user's home directory");
+ return -1;
+ }
- if ((error = git_buf_joinpath(path, home, filename)) < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(path, home, filename) < 0)
+ return -1;
- if (git_path_exists(path->ptr) < GIT_SUCCESS) {
+ if (git_path_exists(path->ptr) == false) {
git_buf_clear(path);
return GIT_ENOTFOUND;
}
- return GIT_SUCCESS;
+ return 0;
}
#ifdef GIT_WIN32
@@ -315,9 +388,8 @@ static const win32_path *win32_system_root(void)
const wchar_t *root_tmpl = L"%PROGRAMFILES%\\Git\\etc\\";
s_root.len = ExpandEnvironmentStringsW(root_tmpl, NULL, 0);
-
if (s_root.len <= 0) {
- git__throw(GIT_EOSERR, "Failed to expand environment strings");
+ giterr_set(GITERR_OS, "Failed to expand environment strings");
return NULL;
}
@@ -326,7 +398,7 @@ static const win32_path *win32_system_root(void)
return NULL;
if (ExpandEnvironmentStringsW(root_tmpl, s_root.path, s_root.len) != s_root.len) {
- git__throw(GIT_EOSERR, "Failed to expand environment strings");
+ giterr_set(GITERR_OS, "Failed to expand environment strings");
git__free(s_root.path);
s_root.path = NULL;
return NULL;
@@ -338,7 +410,7 @@ static const win32_path *win32_system_root(void)
static int win32_find_system_file(git_buf *path, const char *filename)
{
- int error = GIT_SUCCESS;
+ int error = 0;
const win32_path *root = win32_system_root();
size_t len;
wchar_t *file_utf16 = NULL, *scan;
@@ -349,8 +421,7 @@ static int win32_find_system_file(git_buf *path, const char *filename)
/* allocate space for wchar_t path to file */
file_utf16 = git__calloc(root->len + len + 2, sizeof(wchar_t));
- if (!file_utf16)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(file_utf16);
/* append root + '\\' + filename as wchar_t */
memcpy(file_utf16, root->path, root->len * sizeof(wchar_t));
@@ -360,7 +431,7 @@ static int win32_find_system_file(git_buf *path, const char *filename)
if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) !=
(int)len + 1) {
- error = git__throw(GIT_EOSERR, "Failed to build file path");
+ error = -1;
goto cleanup;
}
@@ -376,9 +447,8 @@ static int win32_find_system_file(git_buf *path, const char *filename)
/* convert to utf8 */
if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL)
- error = GIT_ENOMEM;
-
- if (file_utf8) {
+ error = -1;
+ else {
git_path_mkposix(file_utf8);
git_buf_attach(path, file_utf8, 0);
}
@@ -392,11 +462,11 @@ cleanup:
int git_futils_find_system_file(git_buf *path, const char *filename)
{
- if (git_buf_joinpath(path, "/etc", filename) < GIT_SUCCESS)
- return git_buf_lasterror(path);
+ if (git_buf_joinpath(path, "/etc", filename) < 0)
+ return -1;
- if (git_path_exists(path->ptr) == GIT_SUCCESS)
- return GIT_SUCCESS;
+ if (git_path_exists(path->ptr) == true)
+ return 0;
git_buf_clear(path);
diff --git a/src/fileops.h b/src/fileops.h
index ab57b6f38..be619d620 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -26,7 +26,7 @@ extern int git_futils_readbuffer_updated(git_buf *obj, const char *path, time_t
* These are custom filesystem-related helper methods. They are
* rather high level, and wrap the underlying POSIX methods
*
- * All these methods return GIT_SUCCESS on success,
+ * All these methods return 0 on success,
* or an error code on failure and an error message is set.
*/
@@ -58,10 +58,25 @@ extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t m
*/
extern int git_futils_mkpath2file(const char *path, const mode_t mode);
+typedef enum {
+ GIT_DIRREMOVAL_EMPTY_HIERARCHY = 0,
+ GIT_DIRREMOVAL_FILES_AND_DIRS = 1,
+ GIT_DIRREMOVAL_ONLY_EMPTY_DIRS = 2,
+} git_directory_removal_type;
+
/**
* Remove path and any files and directories beneath it.
+ *
+ * @param path Path to to top level directory to process.
+ *
+ * @param removal_type GIT_DIRREMOVAL_EMPTY_HIERARCHY to remove a hierarchy
+ * of empty directories (will fail if any file is found), GIT_DIRREMOVAL_FILES_AND_DIRS
+ * to remove a hierarchy of files and folders, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS to only remove
+ * empty directories (no failure on file encounter).
+ *
+ * @return 0 on success; -1 on error.
*/
-extern int git_futils_rmdir_r(const char *path, int force);
+extern int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type);
/**
* Create and open a temporary file with a `_git2_` suffix.
@@ -77,6 +92,11 @@ extern int git_futils_mktmp(git_buf *path_out, const char *filename);
extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode);
/**
+ * Open a file readonly and set error if needed.
+ */
+extern int git_futils_open_ro(const char *path);
+
+/**
* Get the filesize in bytes of a file
*/
extern git_off_t git_futils_filesize(git_file fd);
@@ -103,8 +123,8 @@ extern mode_t git_futils_canonical_mode(mode_t raw_mode);
* @param begin first byte to map, this should be page aligned.
* @param end number of bytes to map.
* @return
- * - GIT_SUCCESS on success;
- * - GIT_EOSERR on an unspecified OS related error.
+ * - 0 on success;
+ * - -1 on error.
*/
extern int git_futils_mmap_ro(
git_map *out,
@@ -118,8 +138,9 @@ extern int git_futils_mmap_ro(
* @param out buffer to populate with the mapping information.
* @param path path to file to be opened.
* @return
- * - GIT_SUCCESS on success;
- * - GIT_EOSERR on an unspecified OS related error.
+ * - 0 on success;
+ * - GIT_ENOTFOUND if not found;
+ * - -1 on an unspecified OS related error.
*/
extern int git_futils_mmap_ro_file(
git_map *out,
@@ -137,9 +158,9 @@ extern void git_futils_mmap_free(git_map *map);
* @param pathbuf buffer to write the full path into
* @param filename name of file to find in the home directory
* @return
- * - GIT_SUCCESS if found;
+ * - 0 if found;
* - GIT_ENOTFOUND if not found;
- * - GIT_EOSERR on an unspecified OS related error.
+ * - -1 on an unspecified OS related error.
*/
extern int git_futils_find_global_file(git_buf *path, const char *filename);
@@ -149,9 +170,9 @@ extern int git_futils_find_global_file(git_buf *path, const char *filename);
* @param pathbuf buffer to write the full path into
* @param filename name of file to find in the home directory
* @return
- * - GIT_SUCCESS if found;
+ * - 0 if found;
* - GIT_ENOTFOUND if not found;
- * - GIT_EOSERR on an unspecified OS related error.
+ * - -1 on an unspecified OS related error.
*/
extern int git_futils_find_system_file(git_buf *path, const char *filename);
diff --git a/src/filter.c b/src/filter.c
index f0ee1ad39..d6c2e1c97 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -19,13 +19,13 @@ void git_text_gather_stats(git_text_stats *stats, const git_buf *text)
memset(stats, 0, sizeof(*stats));
- for (i = 0; i < text->size; i++) {
+ for (i = 0; i < git_buf_len(text); i++) {
unsigned char c = text->ptr[i];
if (c == '\r') {
stats->cr++;
- if (i + 1 < text->size && text->ptr[i + 1] == '\n')
+ if (i + 1 < git_buf_len(text) && text->ptr[i + 1] == '\n')
stats->crlf++;
}
@@ -59,7 +59,7 @@ void git_text_gather_stats(git_text_stats *stats, const git_buf *text)
}
/* If file ends with EOF then don't count this EOF as non-printable. */
- if (text->size >= 1 && text->ptr[text->size - 1] == '\032')
+ if (git_buf_len(text) >= 1 && text->ptr[text->size - 1] == '\032')
stats->nonprintable--;
}
@@ -95,8 +95,8 @@ int git_filters_load(git_vector *filters, git_repository *repo, const char *path
if (error < GIT_SUCCESS)
return error;
} else {
- return git__throw(GIT_ENOTIMPLEMENTED,
- "Worktree filters are not implemented yet");
+ giterr_set(GITERR_INVALID, "Worktree filters are not implemented yet");
+ return GIT_ENOTIMPLEMENTED;
}
return (int)filters->length;
@@ -111,7 +111,7 @@ void git_filters_free(git_vector *filters)
if (filter->do_free != NULL)
filter->do_free(filter);
else
- free(filter);
+ git__free(filter);
}
git_vector_free(filters);
@@ -127,14 +127,14 @@ int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
src = 0;
- if (source->size == 0) {
+ if (git_buf_len(source) == 0) {
git_buf_clear(dest);
return GIT_SUCCESS;
}
/* Pre-grow the destination buffer to more or less the size
* we expect it to have */
- if (git_buf_grow(dest, source->size) < 0)
+ if (git_buf_grow(dest, git_buf_len(source)) < 0)
return GIT_ENOMEM;
for (i = 0; i < filters->length; ++i) {
diff --git a/src/global.c b/src/global.c
index b10fabc61..368c6c664 100644
--- a/src/global.c
+++ b/src/global.c
@@ -62,7 +62,7 @@ git_global_st *git__global_state(void)
if ((ptr = TlsGetValue(_tls_index)) != NULL)
return ptr;
- ptr = malloc(sizeof(git_global_st));
+ ptr = git__malloc(sizeof(git_global_st));
if (!ptr)
return NULL;
@@ -78,7 +78,7 @@ static int _tls_init = 0;
static void cb__free_status(void *st)
{
- free(st);
+ git__free(st);
}
void git_threads_init(void)
@@ -103,7 +103,7 @@ git_global_st *git__global_state(void)
if ((ptr = pthread_getspecific(_tls_key)) != NULL)
return ptr;
- ptr = malloc(sizeof(git_global_st));
+ ptr = git__malloc(sizeof(git_global_st));
if (!ptr)
return NULL;
diff --git a/src/global.h b/src/global.h
index 0c1e3289c..2b525ce07 100644
--- a/src/global.h
+++ b/src/global.h
@@ -14,6 +14,9 @@ typedef struct {
char last[1024];
} error;
+ git_error *last_error;
+ git_error error_t;
+
git_mwindow_ctl mem_ctl;
} git_global_st;
diff --git a/src/hashtable.c b/src/hashtable.c
deleted file mode 100644
index 73a6336c4..000000000
--- a/src/hashtable.c
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * 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 "common.h"
-#include "repository.h"
-#include "commit.h"
-
-#define MAX_LOOPS 5
-static const double max_load_factor = 0.65;
-
-static int resize_to(git_hashtable *self, size_t new_size);
-static int set_size(git_hashtable *self, size_t new_size);
-static git_hashtable_node *node_with_hash(git_hashtable *self, const void *key, int hash_id);
-static void node_swap_with(git_hashtable_node *self, git_hashtable_node *other);
-static int node_insert(git_hashtable *self, git_hashtable_node *new_node);
-static int insert_nodes(git_hashtable *self, git_hashtable_node *old_nodes, size_t old_size);
-
-static int resize_to(git_hashtable *self, size_t new_size)
-{
- git_hashtable_node *old_nodes = self->nodes;
- size_t old_size = self->size;
-
- self->is_resizing = 1;
-
- do {
- self->size = new_size;
- self->size_mask = new_size - 1;
- self->key_count = 0;
- self->nodes = git__calloc(1, sizeof(git_hashtable_node) * self->size);
-
- if (self->nodes == NULL)
- return GIT_ENOMEM;
-
- if (insert_nodes(self, old_nodes, old_size) == 0)
- self->is_resizing = 0;
- else {
- new_size *= 2;
- git__free(self->nodes);
- }
- } while(self->is_resizing);
-
- git__free(old_nodes);
- return GIT_SUCCESS;
-}
-
-static int set_size(git_hashtable *self, size_t new_size)
-{
- self->nodes = git__realloc(self->nodes, new_size * sizeof(git_hashtable_node));
- if (self->nodes == NULL)
- return GIT_ENOMEM;
-
- if (new_size > self->size) {
- memset(&self->nodes[self->size], 0x0, (new_size - self->size) * sizeof(git_hashtable_node));
- }
-
- self->size = new_size;
- self->size_mask = new_size - 1;
- return GIT_SUCCESS;
-}
-
-static git_hashtable_node *node_with_hash(git_hashtable *self, const void *key, int hash_id)
-{
- size_t pos = self->hash(key, hash_id) & self->size_mask;
- return git_hashtable_node_at(self->nodes, pos);
-}
-
-static void node_swap_with(git_hashtable_node *self, git_hashtable_node *other)
-{
- git_hashtable_node tmp = *self;
- *self = *other;
- *other = tmp;
-}
-
-static int node_insert(git_hashtable *self, git_hashtable_node *new_node)
-{
- int iteration, hash_id;
-
- for (iteration = 0; iteration < MAX_LOOPS; iteration++) {
- for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
- git_hashtable_node *node;
- node = node_with_hash(self, new_node->key, hash_id);
- node_swap_with(new_node, node);
- if(new_node->key == 0x0){
- self->key_count++;
- return GIT_SUCCESS;
- }
- }
- }
-
- if (self->is_resizing)
- return git__throw(GIT_EBUSY, "Failed to insert node. Hashtable is currently resizing");
-
- resize_to(self, self->size * 2);
- git_hashtable_insert(self, new_node->key, new_node->value);
- return GIT_SUCCESS;
-}
-
-static int insert_nodes(git_hashtable *self, git_hashtable_node *old_nodes, size_t old_size)
-{
- size_t i;
-
- for (i = 0; i < old_size; ++i) {
- git_hashtable_node *node = git_hashtable_node_at(old_nodes, i);
- if (node->key && git_hashtable_insert(self, node->key, node->value) < GIT_SUCCESS)
- return GIT_ENOMEM;
- }
-
- return GIT_SUCCESS;
-}
-
-git_hashtable *git_hashtable_alloc(size_t min_size,
- git_hash_ptr hash,
- git_hash_keyeq_ptr key_eq)
-{
- git_hashtable *table;
-
- assert(hash && key_eq);
-
- if ((table = git__malloc(sizeof(git_hashtable))) == NULL)
- return NULL;
-
- memset(table, 0x0, sizeof(git_hashtable));
-
- if (min_size < 8)
- min_size = 8;
-
- /* round up size to closest power of 2 */
- min_size--;
- min_size |= min_size >> 1;
- min_size |= min_size >> 2;
- min_size |= min_size >> 4;
- min_size |= min_size >> 8;
- min_size |= min_size >> 16;
-
- table->hash = hash;
- table->key_equal = key_eq;
-
- set_size(table, min_size + 1);
-
- return table;
-}
-
-void git_hashtable_clear(git_hashtable *self)
-{
- assert(self);
-
- memset(self->nodes, 0x0, sizeof(git_hashtable_node) * self->size);
- self->key_count = 0;
-}
-
-void git_hashtable_free(git_hashtable *self)
-{
- assert(self);
-
- git__free(self->nodes);
- git__free(self);
-}
-
-
-int git_hashtable_insert2(git_hashtable *self, const void *key, void *value, void **old_value)
-{
- int hash_id;
- git_hashtable_node *node;
-
- assert(self && self->nodes);
-
- *old_value = NULL;
-
- for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
- node = node_with_hash(self, key, hash_id);
-
- if (!node->key) {
- node->key = key;
- node->value = value;
- self->key_count++;
- return GIT_SUCCESS;
- }
-
- if (key == node->key || self->key_equal(key, node->key) == 0) {
- *old_value = node->value;
- node->key = key;
- node->value = value;
- return GIT_SUCCESS;
- }
- }
-
- /* no space in table; must do cuckoo dance */
- {
- git_hashtable_node x;
- x.key = key;
- x.value = value;
- return node_insert(self, &x);
- }
-}
-
-void *git_hashtable_lookup(git_hashtable *self, const void *key)
-{
- int hash_id;
- git_hashtable_node *node;
-
- assert(self && self->nodes);
-
- for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
- node = node_with_hash(self, key, hash_id);
- if (node->key && self->key_equal(key, node->key) == 0)
- return node->value;
- }
-
- return NULL;
-}
-
-int git_hashtable_remove2(git_hashtable *self, const void *key, void **old_value)
-{
- int hash_id;
- git_hashtable_node *node;
-
- assert(self && self->nodes);
-
- for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
- node = node_with_hash(self, key, hash_id);
- if (node->key && self->key_equal(key, node->key) == 0) {
- *old_value = node->value;
- node->key = NULL;
- node->value = NULL;
- self->key_count--;
- return GIT_SUCCESS;
- }
- }
-
- return git__throw(GIT_ENOTFOUND, "Entry not found in hash table");
-}
-
-int git_hashtable_merge(git_hashtable *self, git_hashtable *other)
-{
- if (resize_to(self, (self->size + other->size) * 2) < GIT_SUCCESS)
- return GIT_ENOMEM;
-
- return insert_nodes(self, other->nodes, other->key_count);
-}
-
-
-/**
- * Standard string
- */
-uint32_t git_hash__strhash_cb(const void *key, int hash_id)
-{
- static uint32_t hash_seeds[GIT_HASHTABLE_HASHES] = {
- 2147483647,
- 0x5d20bb23,
- 0x7daaab3c
- };
-
- return git__hash(key, strlen((const char *)key), hash_seeds[hash_id]);
-}
diff --git a/src/hashtable.h b/src/hashtable.h
deleted file mode 100644
index e09965965..000000000
--- a/src/hashtable.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.
- */
-#ifndef INCLUDE_hashtable_h__
-#define INCLUDE_hashtable_h__
-
-#include "git2/common.h"
-#include "git2/oid.h"
-#include "git2/odb.h"
-#include "common.h"
-
-#define GIT_HASHTABLE_HASHES 3
-
-typedef uint32_t (*git_hash_ptr)(const void *, int hash_id);
-typedef int (*git_hash_keyeq_ptr)(const void *key_a, const void *key_b);
-
-struct git_hashtable_node {
- const void *key;
- void *value;
-};
-
-struct git_hashtable {
- struct git_hashtable_node *nodes;
-
- size_t size_mask;
- size_t size;
- size_t key_count;
-
- int is_resizing;
-
- git_hash_ptr hash;
- git_hash_keyeq_ptr key_equal;
-};
-
-typedef struct git_hashtable_node git_hashtable_node;
-typedef struct git_hashtable git_hashtable;
-
-git_hashtable *git_hashtable_alloc(size_t min_size,
- git_hash_ptr hash,
- git_hash_keyeq_ptr key_eq);
-void *git_hashtable_lookup(git_hashtable *h, const void *key);
-int git_hashtable_remove2(git_hashtable *table, const void *key, void **old_value);
-
-GIT_INLINE(int) git_hashtable_remove(git_hashtable *table, const void *key)
-{
- void *_unused;
- return git_hashtable_remove2(table, key, &_unused);
-}
-
-
-void git_hashtable_free(git_hashtable *h);
-void git_hashtable_clear(git_hashtable *h);
-int git_hashtable_merge(git_hashtable *self, git_hashtable *other);
-
-int git_hashtable_insert2(git_hashtable *h, const void *key, void *value, void **old_value);
-
-GIT_INLINE(int) git_hashtable_insert(git_hashtable *h, const void *key, void *value)
-{
- void *_unused;
- return git_hashtable_insert2(h, key, value, &_unused);
-}
-
-#define git_hashtable_node_at(nodes, pos) ((git_hashtable_node *)(&nodes[pos]))
-
-#define GIT_HASHTABLE__FOREACH(self,block) { \
- unsigned int _c; \
- git_hashtable_node *_n = (self)->nodes; \
- for (_c = (self)->size; _c > 0; _c--, _n++) { \
- if (!_n->key) continue; block } }
-
-#define GIT_HASHTABLE_FOREACH(self, pkey, pvalue, code)\
- GIT_HASHTABLE__FOREACH(self,{(pkey)=_n->key;(pvalue)=_n->value;code;})
-
-#define GIT_HASHTABLE_FOREACH_KEY(self, pkey, code)\
- GIT_HASHTABLE__FOREACH(self,{(pkey)=_n->key;code;})
-
-#define GIT_HASHTABLE_FOREACH_VALUE(self, pvalue, code)\
- GIT_HASHTABLE__FOREACH(self,{(pvalue)=_n->value;code;})
-
-#define GIT_HASHTABLE_FOREACH_DELETE() {\
- _node->key = NULL; _node->value = NULL; _self->key_count--;\
-}
-
-/*
- * If you want a hashtable with standard string keys, you can
- * just pass git_hash__strcmp_cb and git_hash__strhash_cb to
- * git_hashtable_alloc.
- */
-#define git_hash__strcmp_cb git__strcmp_cb
-extern uint32_t git_hash__strhash_cb(const void *key, int hash_id);
-
-#endif
diff --git a/src/ignore.c b/src/ignore.c
index a3bf0a282..20b96c602 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -1,53 +1,54 @@
#include "ignore.h"
#include "path.h"
-#include "git2/config.h"
#define GIT_IGNORE_INTERNAL "[internal]exclude"
#define GIT_IGNORE_FILE_INREPO "info/exclude"
#define GIT_IGNORE_FILE ".gitignore"
-#define GIT_IGNORE_CONFIG "core.excludesfile"
static int load_ignore_file(
git_repository *repo, const char *path, git_attr_file *ignores)
{
- int error = GIT_SUCCESS;
+ int error;
git_buf fbuf = GIT_BUF_INIT;
git_attr_fnmatch *match = NULL;
const char *scan = NULL;
char *context = NULL;
- if (ignores->path == NULL)
- error = git_attr_file__set_path(repo, path, ignores);
+ if (ignores->path == NULL) {
+ if (git_attr_file__set_path(repo, path, ignores) < 0)
+ return -1;
+ }
if (git__suffixcmp(ignores->path, GIT_IGNORE_FILE) == 0) {
context = git__strndup(ignores->path,
strlen(ignores->path) - strlen(GIT_IGNORE_FILE));
- if (!context) error = GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(context);
}
- if (error == GIT_SUCCESS)
- error = git_futils_readbuffer(&fbuf, path);
+ error = git_futils_readbuffer(&fbuf, path);
scan = fbuf.ptr;
- while (error == GIT_SUCCESS && *scan) {
- if (!match && !(match = git__calloc(1, sizeof(git_attr_fnmatch)))) {
- error = GIT_ENOMEM;
- break;
+ while (!error && *scan) {
+ if (!match) {
+ match = git__calloc(1, sizeof(*match));
+ GITERR_CHECK_ALLOC(match);
}
- if (!(error = git_attr_fnmatch__parse(match, context, &scan))) {
+ if (!(error = git_attr_fnmatch__parse(
+ match, ignores->pool, context, &scan)))
+ {
match->flags = match->flags | GIT_ATTR_FNMATCH_IGNORE;
scan = git__next_line(scan);
error = git_vector_insert(&ignores->rules, match);
}
- if (error != GIT_SUCCESS) {
+ if (error != 0) {
git__free(match->pattern);
match->pattern = NULL;
if (error == GIT_ENOTFOUND)
- error = GIT_SUCCESS;
+ error = 0;
} else {
match = NULL; /* vector now "owns" the match */
}
@@ -57,9 +58,6 @@ static int load_ignore_file(
git__free(match);
git__free(context);
- if (error != GIT_SUCCESS)
- git__rethrow(error, "Could not open ignore file '%s'", path);
-
return error;
}
@@ -74,8 +72,7 @@ static int push_one_ignore(void *ref, git_buf *path)
int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ignores)
{
- int error = GIT_SUCCESS;
- git_config *cfg;
+ int error = 0;
const char *workdir = git_repository_workdir(repo);
assert(ignores);
@@ -83,63 +80,52 @@ int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ig
ignores->repo = repo;
git_buf_init(&ignores->dir, 0);
ignores->ign_internal = NULL;
- git_vector_init(&ignores->ign_path, 8, NULL);
- git_vector_init(&ignores->ign_global, 2, NULL);
- if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS)
+ if ((error = git_vector_init(&ignores->ign_path, 8, NULL)) < 0 ||
+ (error = git_vector_init(&ignores->ign_global, 2, NULL)) < 0 ||
+ (error = git_attr_cache__init(repo)) < 0)
goto cleanup;
- if ((error = git_path_find_dir(&ignores->dir, path, workdir)) < GIT_SUCCESS)
+ /* translate path into directory within workdir */
+ if ((error = git_path_find_dir(&ignores->dir, path, workdir)) < 0)
goto cleanup;
/* set up internals */
error = git_attr_cache__lookup_or_create_file(
repo, GIT_IGNORE_INTERNAL, NULL, NULL, &ignores->ign_internal);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
/* load .gitignore up the path */
error = git_path_walk_up(&ignores->dir, workdir, push_one_ignore, ignores);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
/* load .git/info/exclude */
error = push_ignore(repo, &ignores->ign_global,
- repo->path_repository, GIT_IGNORE_FILE_INREPO);
- if (error < GIT_SUCCESS)
+ git_repository_path(repo), GIT_IGNORE_FILE_INREPO);
+ if (error < 0)
goto cleanup;
/* load core.excludesfile */
- if ((error = git_repository_config(&cfg, repo)) == GIT_SUCCESS) {
- const char *core_ignore;
- error = git_config_get_string(cfg, GIT_IGNORE_CONFIG, &core_ignore);
- if (error == GIT_SUCCESS && core_ignore != NULL)
- error = push_ignore(repo, &ignores->ign_global, NULL, core_ignore);
- else {
- error = GIT_SUCCESS;
- git_clearerror(); /* don't care if attributesfile is not set */
- }
- git_config_free(cfg);
- }
+ if (git_repository_attr_cache(repo)->cfg_excl_file != NULL)
+ error = push_ignore(repo, &ignores->ign_global, NULL,
+ git_repository_attr_cache(repo)->cfg_excl_file);
cleanup:
- if (error < GIT_SUCCESS) {
+ if (error < 0)
git_ignore__free(ignores);
- git__rethrow(error, "Could not get ignore files for '%s'", path);
- }
return error;
}
int git_ignore__push_dir(git_ignores *ign, const char *dir)
{
- int error = git_buf_joinpath(&ign->dir, ign->dir.ptr, dir);
-
- if (error == GIT_SUCCESS)
- error = push_ignore(
+ if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0)
+ return -1;
+ else
+ return push_ignore(
ign->repo, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
-
- return error;
}
int git_ignore__pop_dir(git_ignores *ign)
@@ -150,7 +136,7 @@ int git_ignore__pop_dir(git_ignores *ign)
git_vector_pop(&ign->ign_path);
git_buf_rtruncate_at_char(&ign->dir, '/');
}
- return GIT_SUCCESS;
+ return 0;
}
void git_ignore__free(git_ignores *ignores)
@@ -161,54 +147,52 @@ void git_ignore__free(git_ignores *ignores)
git_buf_free(&ignores->dir);
}
-static int ignore_lookup_in_rules(
+static bool ignore_lookup_in_rules(
git_vector *rules, git_attr_path *path, int *ignored)
{
unsigned int j;
git_attr_fnmatch *match;
git_vector_rforeach(rules, j, match) {
- if (git_attr_fnmatch__match(match, path) == GIT_SUCCESS) {
+ if (git_attr_fnmatch__match(match, path)) {
*ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0);
- return GIT_SUCCESS;
+ return true;
}
}
- return GIT_ENOTFOUND;
+ return false;
}
int git_ignore__lookup(git_ignores *ignores, const char *pathname, int *ignored)
{
- int error;
unsigned int i;
git_attr_file *file;
git_attr_path path;
- if ((error = git_attr_path__init(
- &path, pathname, git_repository_workdir(ignores->repo))) < GIT_SUCCESS)
- return git__rethrow(error, "Could not get attribute for '%s'", pathname);
+ if (git_attr_path__init(
+ &path, pathname, git_repository_workdir(ignores->repo)) < 0)
+ return -1;
- /* first process builtins */
- error = ignore_lookup_in_rules(
- &ignores->ign_internal->rules, &path, ignored);
- if (error == GIT_SUCCESS)
- return error;
+ /* first process builtins - success means path was found */
+ if (ignore_lookup_in_rules(
+ &ignores->ign_internal->rules, &path, ignored))
+ goto cleanup;
/* next process files in the path */
git_vector_foreach(&ignores->ign_path, i, file) {
- error = ignore_lookup_in_rules(&file->rules, &path, ignored);
- if (error == GIT_SUCCESS)
- return error;
+ if (ignore_lookup_in_rules(&file->rules, &path, ignored))
+ goto cleanup;
}
/* last process global ignores */
git_vector_foreach(&ignores->ign_global, i, file) {
- error = ignore_lookup_in_rules(&file->rules, &path, ignored);
- if (error == GIT_SUCCESS)
- return error;
+ if (ignore_lookup_in_rules(&file->rules, &path, ignored))
+ goto cleanup;
}
*ignored = 0;
- return GIT_SUCCESS;
+cleanup:
+ git_attr_path__free(&path);
+ return 0;
}
diff --git a/src/index.c b/src/index.c
index 5ac99de3e..216ede777 100644
--- a/src/index.c
+++ b/src/index.c
@@ -135,22 +135,17 @@ int git_index_open(git_index **index_out, const char *index_path)
assert(index_out && index_path);
- index = git__malloc(sizeof(git_index));
- if (index == NULL)
- return GIT_ENOMEM;
-
- memset(index, 0x0, sizeof(git_index));
+ index = git__calloc(1, sizeof(git_index));
+ GITERR_CHECK_ALLOC(index);
index->index_file_path = git__strdup(index_path);
- if (index->index_file_path == NULL) {
- git__free(index);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(index->index_file_path);
- git_vector_init(&index->entries, 32, index_cmp);
+ if (git_vector_init(&index->entries, 32, index_cmp) < 0)
+ return -1;
/* Check if index file is stored on disk already */
- if (git_path_exists(index->index_file_path) == 0)
+ if (git_path_exists(index->index_file_path) == true)
index->on_disk = 1;
*index_out = index;
@@ -215,36 +210,35 @@ void git_index_clear(git_index *index)
int git_index_read(git_index *index)
{
- int error = GIT_SUCCESS, updated;
+ int error, updated;
git_buf buffer = GIT_BUF_INIT;
time_t mtime;
assert(index->index_file_path);
- if (!index->on_disk || git_path_exists(index->index_file_path) < 0) {
+ if (!index->on_disk || git_path_exists(index->index_file_path) == false) {
git_index_clear(index);
index->on_disk = 0;
- return GIT_SUCCESS;
+ return 0;
}
/* We don't want to update the mtime if we fail to parse the index */
mtime = index->last_modified;
- error = git_futils_readbuffer_updated(&buffer, index->index_file_path, &mtime, &updated);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read index");
+ error = git_futils_readbuffer_updated(
+ &buffer, index->index_file_path, &mtime, &updated);
+ if (error < 0)
+ return error;
if (updated) {
git_index_clear(index);
error = parse_index(index, buffer.ptr, buffer.size);
- if (error == GIT_SUCCESS)
+ if (!error)
index->last_modified = mtime;
git_buf_free(&buffer);
}
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to parse index");
return error;
}
@@ -256,23 +250,24 @@ int git_index_write(git_index *index)
git_vector_sort(&index->entries);
- if ((error = git_filebuf_open(&file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to write index");
+ if ((error = git_filebuf_open(
+ &file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < 0)
+ return error;
- if ((error = write_index(index, &file)) < GIT_SUCCESS) {
+ if ((error = write_index(index, &file)) < 0) {
git_filebuf_cleanup(&file);
- return git__rethrow(error, "Failed to write index");
+ return error;
}
- if ((error = git_filebuf_commit(&file, GIT_INDEX_FILE_MODE)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to write index");
+ if ((error = git_filebuf_commit(&file, GIT_INDEX_FILE_MODE)) < 0)
+ return error;
if (p_stat(index->index_file_path, &indexst) == 0) {
index->last_modified = indexst.st_mtime;
index->on_disk = 1;
}
- return GIT_SUCCESS;
+ return 0;
}
unsigned int git_index_entrycount(git_index *index)
@@ -293,6 +288,20 @@ git_index_entry *git_index_get(git_index *index, unsigned int n)
return git_vector_get(&index->entries, n);
}
+void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry)
+{
+ entry->ctime.seconds = (git_time_t)st->st_ctime;
+ entry->mtime.seconds = (git_time_t)st->st_mtime;
+ /* entry->mtime.nanoseconds = st->st_mtimensec; */
+ /* entry->ctime.nanoseconds = st->st_ctimensec; */
+ entry->dev = st->st_rdev;
+ entry->ino = st->st_ino;
+ entry->mode = index_create_mode(st->st_mode);
+ entry->uid = st->st_uid;
+ entry->gid = st->st_gid;
+ entry->file_size = st->st_size;
+}
+
static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path, int stage)
{
git_index_entry *entry = NULL;
@@ -302,25 +311,20 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const
git_buf full_path = GIT_BUF_INIT;
int error;
- if (INDEX_OWNER(index) == NULL)
- return git__throw(GIT_EBAREINDEX,
- "Failed to initialize entry. Repository is bare");
-
- if (stage < 0 || stage > 3)
- return git__throw(GIT_ERROR,
- "Failed to initialize entry. Invalid stage %i", stage);
+ assert(stage >= 0 && stage <= 3);
- workdir = git_repository_workdir(INDEX_OWNER(index));
- if (workdir == NULL)
- return git__throw(GIT_EBAREINDEX,
- "Failed to initialize entry. Cannot resolved workdir");
+ if (INDEX_OWNER(index) == NULL ||
+ (workdir = git_repository_workdir(INDEX_OWNER(index))) == NULL)
+ {
+ giterr_set(GITERR_INDEX,
+ "Could not initialize index entry. Repository is bare");
+ return -1;
+ }
- error = git_buf_joinpath(&full_path, workdir, rel_path);
- if (error < GIT_SUCCESS)
+ if ((error = git_buf_joinpath(&full_path, workdir, rel_path)) < 0)
return error;
- if (p_lstat(full_path.ptr, &st) < 0) {
- error = git__throw(GIT_ENOTFOUND, "Failed to initialize entry. '%s' cannot be opened. %s", full_path.ptr, strerror(errno));
+ if ((error = git_path_lstat(full_path.ptr, &st)) < 0) {
git_buf_free(&full_path);
return error;
}
@@ -332,34 +336,21 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const
*/
/* write the blob to disk and get the oid */
- if ((error = git_blob_create_fromfile(&oid, INDEX_OWNER(index), rel_path)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to initialize index entry");
+ if ((error = git_blob_create_fromfile(&oid, INDEX_OWNER(index), rel_path)) < 0)
+ return error;
entry = git__calloc(1, sizeof(git_index_entry));
- if (!entry)
- return GIT_ENOMEM;
-
- entry->ctime.seconds = (git_time_t)st.st_ctime;
- entry->mtime.seconds = (git_time_t)st.st_mtime;
- /* entry.mtime.nanoseconds = st.st_mtimensec; */
- /* entry.ctime.nanoseconds = st.st_ctimensec; */
- entry->dev= st.st_rdev;
- entry->ino = st.st_ino;
- entry->mode = index_create_mode(st.st_mode);
- entry->uid = st.st_uid;
- entry->gid = st.st_gid;
- entry->file_size = st.st_size;
- entry->oid = oid;
+ GITERR_CHECK_ALLOC(entry);
+ git_index__init_entry_from_stat(&st, entry);
+
+ entry->oid = oid;
entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT);
entry->path = git__strdup(rel_path);
- if (entry->path == NULL) {
- git__free(entry);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(entry->path);
*entry_out = entry;
- return GIT_SUCCESS;
+ return 0;
}
static git_index_entry *index_entry_dup(const git_index_entry *source_entry)
@@ -394,10 +385,7 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
int position;
git_index_entry **entry_array;
- assert(index && entry);
-
- if (entry->path == NULL)
- return git__throw(GIT_EMISSINGOBJDATA, "Failed to insert into index. Entry has no path");
+ assert(index && entry && entry->path != NULL);
/* make sure that the path length flag is correct */
path_length = strlen(entry->path);
@@ -413,12 +401,8 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
* replacing is not requested: just insert entry at the end;
* the index is no longer sorted
*/
- if (!replace) {
- if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS)
- return GIT_ENOMEM;
-
- return GIT_SUCCESS;
- }
+ if (!replace)
+ return git_vector_insert(&index->entries, entry);
/* look if an entry with this path already exists */
position = git_index_find(index, entry->path);
@@ -427,12 +411,8 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
* if no entry exists add the entry at the end;
* the index is no longer sorted
*/
- if (position == GIT_ENOTFOUND) {
- if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS)
- return GIT_ENOMEM;
-
- return GIT_SUCCESS;
- }
+ if (position == GIT_ENOTFOUND)
+ return git_vector_insert(&index->entries, entry);
/* exists, replace it */
entry_array = (git_index_entry **) index->entries.contents;
@@ -440,7 +420,7 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
git__free(entry_array[position]);
entry_array[position] = entry;
- return GIT_SUCCESS;
+ return 0;
}
static int index_add(git_index *index, const char *path, int stage, int replace)
@@ -448,20 +428,15 @@ static int index_add(git_index *index, const char *path, int stage, int replace)
git_index_entry *entry = NULL;
int ret;
- ret = index_entry_init(&entry, index, path, stage);
- if (ret)
- goto err;
-
- ret = index_insert(index, entry, replace);
- if (ret)
- goto err;
+ if ((ret = index_entry_init(&entry, index, path, stage)) < 0 ||
+ (ret = index_insert(index, entry, replace)) < 0)
+ {
+ index_entry_free(entry);
+ return ret;
+ }
git_tree_cache_invalidate_path(index->tree, entry->path);
-
- return ret;
-err:
- index_entry_free(entry);
- return git__rethrow(ret, "Failed to append to index");
+ return 0;
}
int git_index_add(git_index *index, const char *path, int stage)
@@ -474,28 +449,23 @@ int git_index_append(git_index *index, const char *path, int stage)
return index_add(index, path, stage, 0);
}
-static int index_add2(git_index *index, const git_index_entry *source_entry,
- int replace)
+static int index_add2(
+ git_index *index, const git_index_entry *source_entry, int replace)
{
git_index_entry *entry = NULL;
int ret;
entry = index_entry_dup(source_entry);
- if (entry == NULL) {
- ret = GIT_ENOMEM;
- goto err;
- }
+ if (entry == NULL)
+ return -1;
- ret = index_insert(index, entry, replace);
- if (ret)
- goto err;
+ if ((ret = index_insert(index, entry, replace)) < 0) {
+ index_entry_free(entry);
+ return ret;
+ }
git_tree_cache_invalidate_path(index->tree, entry->path);
-
- return ret;
-err:
- index_entry_free(entry);
- return git__rethrow(ret, "Failed to append to index");
+ return 0;
}
int git_index_add2(git_index *index, const git_index_entry *source_entry)
@@ -514,13 +484,14 @@ int git_index_remove(git_index *index, int position)
git_index_entry *entry;
git_vector_sort(&index->entries);
+
entry = git_vector_get(&index->entries, position);
if (entry != NULL)
git_tree_cache_invalidate_path(index->tree, entry->path);
error = git_vector_remove(&index->entries, (unsigned int)position);
- if (error == GIT_SUCCESS)
+ if (!error)
index_entry_free(entry);
return error;
@@ -536,7 +507,8 @@ void git_index_uniq(git_index *index)
git_vector_uniq(&index->entries);
}
-const git_index_entry_unmerged *git_index_get_unmerged_bypath(git_index *index, const char *path)
+const git_index_entry_unmerged *git_index_get_unmerged_bypath(
+ git_index *index, const char *path)
{
int pos;
assert(index && path);
@@ -544,75 +516,87 @@ const git_index_entry_unmerged *git_index_get_unmerged_bypath(git_index *index,
if (!index->unmerged.length)
return NULL;
- if ((pos = git_vector_bsearch2(&index->unmerged, unmerged_srch, path)) < GIT_SUCCESS)
+ if ((pos = git_vector_bsearch2(&index->unmerged, unmerged_srch, path)) < 0)
return NULL;
return git_vector_get(&index->unmerged, pos);
}
-const git_index_entry_unmerged *git_index_get_unmerged_byindex(git_index *index, unsigned int n)
+const git_index_entry_unmerged *git_index_get_unmerged_byindex(
+ git_index *index, unsigned int n)
{
assert(index);
return git_vector_get(&index->unmerged, n);
}
+static int index_error_invalid(const char *message)
+{
+ giterr_set(GITERR_INDEX, "Invalid data in index - %s", message);
+ return -1;
+}
+
static int read_unmerged(git_index *index, const char *buffer, size_t size)
{
const char *endptr;
size_t len;
int i;
- git_vector_init(&index->unmerged, 16, unmerged_cmp);
+ if (git_vector_init(&index->unmerged, 16, unmerged_cmp) < 0)
+ return -1;
while (size) {
git_index_entry_unmerged *lost;
len = strlen(buffer) + 1;
if (size <= len)
- return git__throw(GIT_ERROR, "Failed to read unmerged entries");
+ return index_error_invalid("reading unmerged entries");
- if ((lost = git__malloc(sizeof(git_index_entry_unmerged))) == NULL)
- return GIT_ENOMEM;
+ lost = git__malloc(sizeof(git_index_entry_unmerged));
+ GITERR_CHECK_ALLOC(lost);
- if (git_vector_insert(&index->unmerged, lost) < GIT_SUCCESS)
- return git__throw(GIT_ERROR, "Failed to read unmerged entries");
+ if (git_vector_insert(&index->unmerged, lost) < 0)
+ return -1;
+ /* read NUL-terminated pathname for entry */
lost->path = git__strdup(buffer);
- if (!lost->path)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(lost->path);
size -= len;
buffer += len;
+ /* read 3 ASCII octal numbers for stage entries */
for (i = 0; i < 3; i++) {
int tmp;
- if (git__strtol32(&tmp, buffer, &endptr, 8) < GIT_SUCCESS ||
- !endptr || endptr == buffer || *endptr || (unsigned)tmp > UINT_MAX)
- return GIT_ERROR;
+ if (git__strtol32(&tmp, buffer, &endptr, 8) < 0 ||
+ !endptr || endptr == buffer || *endptr ||
+ (unsigned)tmp > UINT_MAX)
+ return index_error_invalid("reading unmerged entry stage");
lost->mode[i] = tmp;
len = (endptr + 1) - buffer;
if (size <= len)
- return git__throw(GIT_ERROR, "Failed to read unmerged entries");
+ return index_error_invalid("reading unmerged entry stage");
size -= len;
buffer += len;
}
+ /* read up to 3 OIDs for stage entries */
for (i = 0; i < 3; i++) {
if (!lost->mode[i])
continue;
if (size < 20)
- return git__throw(GIT_ERROR, "Failed to read unmerged entries");
+ return index_error_invalid("reading unmerged entry oid");
+
git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer);
size -= 20;
buffer += 20;
}
}
- return GIT_SUCCESS;
+ return 0;
}
static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size)
@@ -658,7 +642,7 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe
path_end = memchr(path_ptr, '\0', buffer_size);
if (path_end == NULL)
- return 0;
+ return 0;
path_length = path_end - path_ptr;
}
@@ -683,15 +667,15 @@ static int read_header(struct index_header *dest, const void *buffer)
dest->signature = ntohl(source->signature);
if (dest->signature != INDEX_HEADER_SIG)
- return GIT_EOBJCORRUPTED;
+ return index_error_invalid("incorrect header signature");
dest->version = ntohl(source->version);
if (dest->version != INDEX_VERSION_NUMBER_EXT &&
dest->version != INDEX_VERSION_NUMBER)
- return GIT_EOBJCORRUPTED;
+ return index_error_invalid("incorrect header version");
dest->entry_count = ntohl(source->entry_count);
- return GIT_SUCCESS;
+ return 0;
}
static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size)
@@ -714,10 +698,10 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer
if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') {
/* tree cache */
if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) {
- if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size) < GIT_SUCCESS)
+ if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size) < 0)
return 0;
} else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {
- if (read_unmerged(index, buffer + 8, dest.extension_size) < GIT_SUCCESS)
+ if (read_unmerged(index, buffer + 8, dest.extension_size) < 0)
return 0;
}
/* else, unsupported extension. We cannot parse this, but we can skip
@@ -739,21 +723,21 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
#define seek_forward(_increase) { \
if (_increase >= buffer_size) \
- return git__throw(GIT_EOBJCORRUPTED, "Failed to seek forward. Buffer size exceeded"); \
+ return index_error_invalid("ran out of data while parsing"); \
buffer += _increase; \
buffer_size -= _increase;\
}
if (buffer_size < INDEX_HEADER_SIZE + INDEX_FOOTER_SIZE)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Buffer too small");
+ return index_error_invalid("insufficient buffer space");
/* Precalculate the SHA1 of the files's contents -- we'll match it to
* the provided SHA1 in the footer */
git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE);
/* Parse header */
- if (read_header(&header, buffer) < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Header is corrupted");
+ if (read_header(&header, buffer) < 0)
+ return -1;
seek_forward(INDEX_HEADER_SIZE);
@@ -765,23 +749,22 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
git_index_entry *entry;
entry = git__malloc(sizeof(git_index_entry));
- if (entry == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(entry);
entry_size = read_entry(entry, buffer, buffer_size);
/* 0 bytes read means an object corruption */
if (entry_size == 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Entry size is zero");
+ return index_error_invalid("invalid entry");
- if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (git_vector_insert(&index->entries, entry) < 0)
+ return -1;
seek_forward(entry_size);
}
if (i != header.entry_count)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Header entries changed while parsing");
+ return index_error_invalid("header entries changed while parsing");
/* There's still space for some extensions! */
while (buffer_size > INDEX_FOOTER_SIZE) {
@@ -791,43 +774,43 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
/* see if we have read any bytes from the extension */
if (extension_size == 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Extension size is zero");
+ return index_error_invalid("extension size is zero");
seek_forward(extension_size);
}
if (buffer_size != INDEX_FOOTER_SIZE)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Buffer size does not match index footer size");
+ return index_error_invalid("buffer size does not match index footer size");
/* 160-bit SHA-1 over the content of the index file before this checksum. */
git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer);
if (git_oid_cmp(&checksum_calculated, &checksum_expected) != 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Calculated checksum does not match expected checksum");
+ return index_error_invalid("calculated checksum does not match expected");
#undef seek_forward
/* force sorting in the vector: the entries are
* assured to be sorted on the index */
index->entries.sorted = 1;
- return GIT_SUCCESS;
+ return 0;
}
static int is_index_extended(git_index *index)
{
unsigned int i, extended;
+ git_index_entry *entry;
extended = 0;
- for (i = 0; i < index->entries.length; ++i) {
- git_index_entry *entry;
- entry = git_vector_get(&index->entries, i);
+ git_vector_foreach(&index->entries, i, entry) {
entry->flags &= ~GIT_IDXENTRY_EXTENDED;
if (entry->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS) {
extended++;
entry->flags |= GIT_IDXENTRY_EXTENDED;
}
}
+
return extended;
}
@@ -845,8 +828,8 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
else
disk_size = short_entry_size(path_len);
- if (git_filebuf_reserve(file, &mem, disk_size) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (git_filebuf_reserve(file, &mem, disk_size) < 0)
+ return -1;
ondisk = (struct entry_short *)mem;
@@ -888,7 +871,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
memcpy(path, entry->path, path_len);
- return GIT_SUCCESS;
+ return 0;
}
static int write_entries(git_index *index, git_filebuf *file)
@@ -898,16 +881,15 @@ static int write_entries(git_index *index, git_filebuf *file)
for (i = 0; i < index->entries.length; ++i) {
git_index_entry *entry;
entry = git_vector_get(&index->entries, i);
- if (write_disk_entry(file, entry) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (write_disk_entry(file, entry) < 0)
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
static int write_index(git_index *index, git_filebuf *file)
{
- int error = GIT_SUCCESS;
git_oid hash_final;
struct index_header header;
@@ -922,11 +904,11 @@ static int write_index(git_index *index, git_filebuf *file)
header.version = htonl(is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER);
header.entry_count = htonl(index->entries.length);
- git_filebuf_write(file, &header, sizeof(struct index_header));
+ if (git_filebuf_write(file, &header, sizeof(struct index_header)) < 0)
+ return -1;
- error = write_entries(index, file);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to write index");
+ if (write_entries(index, file) < 0)
+ return -1;
/* TODO: write extensions (tree cache) */
@@ -934,9 +916,7 @@ static int write_index(git_index *index, git_filebuf *file)
git_filebuf_hash(&hash_final, file);
/* write it at the end of the file */
- git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ);
-
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write index");
+ return git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ);
}
int git_index_entry_stage(const git_index_entry *entry)
@@ -946,36 +926,30 @@ int git_index_entry_stage(const git_index_entry *entry)
static int read_tree_cb(const char *root, git_tree_entry *tentry, void *data)
{
- int ret = GIT_SUCCESS;
git_index *index = data;
git_index_entry *entry = NULL;
git_buf path = GIT_BUF_INIT;
if (entry_is_tree(tentry))
- goto exit;
+ return 0;
- ret = git_buf_joinpath(&path, root, tentry->filename);
- if (ret < GIT_SUCCESS)
- goto exit;
+ if (git_buf_joinpath(&path, root, tentry->filename) < 0)
+ return -1;
entry = git__calloc(1, sizeof(git_index_entry));
- if (!entry) {
- ret = GIT_ENOMEM;
- goto exit;
- }
+ GITERR_CHECK_ALLOC(entry);
entry->mode = tentry->attr;
entry->oid = tentry->oid;
entry->path = git_buf_detach(&path);
-
- ret = index_insert(index, entry, 0);
-
-exit:
git_buf_free(&path);
- if (ret < GIT_SUCCESS)
+ if (index_insert(index, entry, 0) < 0) {
index_entry_free(entry);
- return ret;
+ return -1;
+ }
+
+ return 0;
}
int git_index_read_tree(git_index *index, git_tree *tree)
diff --git a/src/index.h b/src/index.h
index 4f036526f..e745c8f69 100644
--- a/src/index.h
+++ b/src/index.h
@@ -31,4 +31,6 @@ struct git_index {
git_vector unmerged;
};
+extern void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry);
+
#endif
diff --git a/src/indexer.c b/src/indexer.c
index de1e5dc52..d2e492c39 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -30,8 +30,6 @@ struct entry {
struct git_indexer {
struct git_pack_file *pack;
- struct stat st;
- struct git_pack_header hdr;
size_t nr_objects;
git_vector objects;
git_filebuf file;
@@ -39,27 +37,89 @@ struct git_indexer {
git_oid hash;
};
+struct git_indexer_stream {
+ unsigned int parsed_header :1,
+ opened_pack;
+ struct git_pack_file *pack;
+ git_filebuf pack_file;
+ git_filebuf index_file;
+ git_off_t off;
+ size_t nr_objects;
+ git_vector objects;
+ git_vector deltas;
+ unsigned int fanout[256];
+ git_oid hash;
+};
+
+struct delta_info {
+ git_off_t delta_off;
+};
+
const git_oid *git_indexer_hash(git_indexer *idx)
{
return &idx->hash;
}
-static int parse_header(git_indexer *idx)
+const git_oid *git_indexer_stream_hash(git_indexer_stream *idx)
+{
+ return &idx->hash;
+}
+
+static int open_pack(struct git_pack_file **out, const char *filename)
+{
+ size_t namelen;
+ struct git_pack_file *pack;
+ struct stat st;
+ int fd;
+
+ namelen = strlen(filename);
+ pack = git__calloc(1, sizeof(struct git_pack_file) + namelen + 1);
+ GITERR_CHECK_ALLOC(pack);
+
+ memcpy(pack->pack_name, filename, namelen + 1);
+
+ if (p_stat(filename, &st) < 0) {
+ giterr_set(GITERR_OS, "Failed to stat packfile.");
+ goto cleanup;
+ }
+
+ if ((fd = p_open(pack->pack_name, O_RDONLY)) < 0) {
+ giterr_set(GITERR_OS, "Failed to open packfile.");
+ goto cleanup;
+ }
+
+ pack->mwf.fd = fd;
+ pack->mwf.size = (git_off_t)st.st_size;
+
+ *out = pack;
+ return 0;
+
+cleanup:
+ git__free(pack);
+ return -1;
+}
+
+static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack)
{
int error;
/* Verify we recognize this pack file format. */
- if ((error = p_read(idx->pack->mwf.fd, &idx->hdr, sizeof(idx->hdr))) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read in pack header");
-
- if (idx->hdr.hdr_signature != ntohl(PACK_SIGNATURE))
- return git__throw(GIT_EOBJCORRUPTED, "Wrong pack signature");
+ if ((error = p_read(pack->mwf.fd, hdr, sizeof(*hdr))) < 0) {
+ giterr_set(GITERR_OS, "Failed to read in pack header");
+ return error;
+ }
- if (!pack_version_ok(idx->hdr.hdr_version))
- return git__throw(GIT_EOBJCORRUPTED, "Wrong pack version");
+ if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) {
+ giterr_set(GITERR_INVALID, "Wrong pack signature");
+ return -1;
+ }
+ if (!pack_version_ok(hdr->hdr_version)) {
+ giterr_set(GITERR_INVALID, "Wrong pack version");
+ return -1;
+ }
- return GIT_SUCCESS;
+ return 0;
}
static int objects_cmp(const void *a, const void *b)
@@ -78,78 +138,484 @@ static int cache_cmp(const void *a, const void *b)
return git_oid_cmp(&ea->sha1, &eb->sha1);
}
+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";
+ int error;
-int git_indexer_new(git_indexer **out, const char *packname)
+ idx = git__calloc(1, sizeof(git_indexer_stream));
+ GITERR_CHECK_ALLOC(idx);
+
+ error = git_buf_joinpath(&path, prefix, suff);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_filebuf_open(&idx->pack_file, path.ptr,
+ GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER);
+ git_buf_free(&path);
+ if (error < 0)
+ goto cleanup;
+
+ *out = idx;
+ return 0;
+
+cleanup:
+ git_buf_free(&path);
+ git_filebuf_cleanup(&idx->pack_file);
+ git__free(idx);
+ return -1;
+}
+
+/* Try to store the delta so we can try to resolve it later */
+static int store_delta(git_indexer_stream *idx)
{
- git_indexer *idx;
- size_t namelen;
- int ret, error;
+ 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;
- assert(out && packname);
+ /*
+ * 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;
- if (git_path_root(packname) < 0)
- return git__throw(GIT_EINVALIDPATH, "Path is not absolute");
+ git_mwindow_close(&w);
- idx = git__malloc(sizeof(git_indexer));
- if (idx == NULL)
- return GIT_ENOMEM;
+ /* 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;
- memset(idx, 0x0, sizeof(*idx));
+ if (type == GIT_OBJ_REF_DELTA) {
+ idx->off += GIT_OID_RAWSZ;
+ } else {
+ git_off_t base_off;
- namelen = strlen(packname);
- idx->pack = git__malloc(sizeof(struct git_pack_file) + namelen + 1);
- if (idx->pack == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
+ base_off = get_delta_base(idx->pack, &w, &idx->off, type, entry_start);
+ git_mwindow_close(&w);
+ if (base_off < 0)
+ return (int)base_off;
+ }
+
+ error = packfile_unpack_compressed(&obj, idx->pack, &w, &idx->off, entry_size, type);
+ if (error == GIT_ESHORTBUFFER) {
+ idx->off = entry_start;
+ return GIT_ESHORTBUFFER;
+ } else if (error < 0){
+ return -1;
+ }
+
+ delta = git__calloc(1, sizeof(struct delta_info));
+ GITERR_CHECK_ALLOC(delta);
+ delta->delta_off = entry_start;
+
+ git__free(obj.data);
+
+ if (git_vector_insert(&idx->deltas, delta) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t entry_start)
+{
+ int i;
+ git_oid oid;
+ void *packed;
+ size_t entry_size;
+ unsigned int left;
+ struct entry *entry;
+ git_mwindow *w = NULL;
+ git_mwindow_file *mwf = &idx->pack->mwf;
+ struct git_pack_entry *pentry;
+
+ entry = git__calloc(1, sizeof(*entry));
+ GITERR_CHECK_ALLOC(entry);
+
+ if (entry_start > UINT31_MAX) {
+ entry->offset = UINT32_MAX;
+ entry->offset_long = entry_start;
+ } else {
+ entry->offset = (uint32_t)entry_start;
+ }
+
+ /* FIXME: Parse the object instead of hashing it */
+ if (git_odb__hashobj(&oid, obj) < 0) {
+ giterr_set(GITERR_INVALID, "Failed to hash object");
+ return -1;
+ }
+
+ pentry = git__malloc(sizeof(struct git_pack_entry));
+ GITERR_CHECK_ALLOC(pentry);
+
+ git_oid_cpy(&pentry->sha1, &oid);
+ pentry->offset = entry_start;
+ if (git_vector_insert(&idx->pack->cache, pentry) < 0)
+ goto on_error;
+
+ git_oid_cpy(&entry->oid, &oid);
+ entry->crc = crc32(0L, Z_NULL, 0);
+
+ entry_size = (size_t)(idx->off - entry_start);
+ packed = git_mwindow_open(mwf, &w, entry_start, entry_size, &left);
+ if (packed == NULL)
+ goto on_error;
+
+ entry->crc = htonl(crc32(entry->crc, packed, (uInt)entry_size));
+ git_mwindow_close(&w);
+
+ /* Add the object to the list */
+ if (git_vector_insert(&idx->objects, entry) < 0)
+ goto on_error;
+
+ for (i = oid.id[0]; i < 256; ++i) {
+ idx->fanout[i]++;
+ }
+
+ return 0;
+
+on_error:
+ git__free(entry);
+ git__free(pentry);
+ git__free(obj->data);
+ return -1;
+}
+
+int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_indexer_stats *stats)
+{
+ int error;
+ struct git_pack_header hdr;
+ size_t processed = stats->processed;
+ git_mwindow_file *mwf = &idx->pack->mwf;
+
+ assert(idx && data && stats);
+
+ if (git_filebuf_write(&idx->pack_file, data, size) < 0)
+ return -1;
+
+ /* Make sure we set the new size of the pack */
+ if (idx->opened_pack) {
+ idx->pack->mwf.size += size;
+ //printf("\nadding %zu for %zu\n", size, idx->pack->mwf.size);
+ } else {
+ if (open_pack(&idx->pack, idx->pack_file.path_lock) < 0)
+ return -1;
+ idx->opened_pack = 1;
+ mwf = &idx->pack->mwf;
+ if (git_mwindow_file_register(&idx->pack->mwf) < 0)
+ return -1;
+
+ return 0;
+ }
+
+ if (!idx->parsed_header) {
+ if ((unsigned)idx->pack->mwf.size < sizeof(hdr))
+ return 0;
+
+ if (parse_header(&hdr, idx->pack) < 0)
+ return -1;
+
+ idx->parsed_header = 1;
+ idx->nr_objects = ntohl(hdr.hdr_entries);
+ idx->off = sizeof(struct git_pack_header);
+
+ /* for now, limit to 2^32 objects */
+ assert(idx->nr_objects == (size_t)((unsigned int)idx->nr_objects));
+
+ if (git_vector_init(&idx->pack->cache, (unsigned int)idx->nr_objects, cache_cmp) < 0)
+ return -1;
+
+ idx->pack->has_cache = 1;
+ if (git_vector_init(&idx->objects, (unsigned int)idx->nr_objects, objects_cmp) < 0)
+ return -1;
+
+ if (git_vector_init(&idx->deltas, (unsigned int)(idx->nr_objects / 2), NULL) < 0)
+ return -1;
+
+ stats->total = (unsigned int)idx->nr_objects;
+ stats->processed = 0;
+ }
+
+ /* Now that we have data in the pack, let's try to parse it */
+
+ /* As the file grows any windows we try to use will be out of date */
+ git_mwindow_free_all(mwf);
+ while (processed < idx->nr_objects) {
+ git_rawobj obj;
+ git_off_t entry_start = idx->off;
+
+ if (idx->pack->mwf.size <= idx->off + 20)
+ return 0;
+
+ error = git_packfile_unpack(&obj, idx->pack, &idx->off);
+ if (error == GIT_ESHORTBUFFER) {
+ idx->off = entry_start;
+ return 0;
+ }
+
+ if (error < 0) {
+ idx->off = entry_start;
+ error = store_delta(idx);
+ if (error == GIT_ESHORTBUFFER)
+ return 0;
+ if (error < 0)
+ return error;
+
+ continue;
+ }
+
+ if (hash_and_save(idx, &obj, entry_start) < 0)
+ goto on_error;
+
+ git__free(obj.data);
+
+ stats->processed = (unsigned int)++processed;
}
- memset(idx->pack, 0x0, sizeof(struct git_pack_file));
- memcpy(idx->pack->pack_name, packname, namelen + 1);
+ return 0;
- ret = p_stat(packname, &idx->st);
- if (ret < 0) {
- if (errno == ENOENT)
- error = git__throw(GIT_ENOTFOUND, "Failed to stat packfile. File not found");
+on_error:
+ git_mwindow_free_all(mwf);
+ return -1;
+}
+
+static int index_path_stream(git_buf *path, git_indexer_stream *idx, const char *suffix)
+{
+ const char prefix[] = "pack-";
+ size_t slash = (size_t)path->size;
+
+ /* search backwards for '/' */
+ while (slash > 0 && path->ptr[slash - 1] != '/')
+ slash--;
+
+ if (git_buf_grow(path, slash + 1 + strlen(prefix) +
+ GIT_OID_HEXSZ + strlen(suffix) + 1) < 0)
+ return -1;
+
+ git_buf_truncate(path, slash);
+ git_buf_puts(path, prefix);
+ git_oid_fmt(path->ptr + git_buf_len(path), &idx->hash);
+ path->size += GIT_OID_HEXSZ;
+ git_buf_puts(path, suffix);
+
+ return git_buf_oom(path) ? -1 : 0;
+}
+
+static int resolve_deltas(git_indexer_stream *idx, git_indexer_stats *stats)
+{
+ unsigned int i;
+ struct delta_info *delta;
+
+ git_vector_foreach(&idx->deltas, i, delta) {
+ git_rawobj obj;
+
+ idx->off = delta->delta_off;
+ if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
+ return -1;
+
+ if (hash_and_save(idx, &obj, delta->delta_off) < 0)
+ return -1;
+
+ git__free(obj.data);
+ stats->processed++;
+ }
+
+ return 0;
+}
+
+int git_indexer_stream_finalize(git_indexer_stream *idx, git_indexer_stats *stats)
+{
+ git_mwindow *w = NULL;
+ unsigned int i, long_offsets = 0, left;
+ struct git_pack_idx_header hdr;
+ git_buf filename = GIT_BUF_INIT;
+ struct entry *entry;
+ void *packfile_hash;
+ git_oid file_hash;
+ SHA_CTX ctx;
+
+ if (idx->deltas.length > 0)
+ if (resolve_deltas(idx, stats) < 0)
+ return -1;
+
+ git_vector_sort(&idx->objects);
+
+ git_buf_sets(&filename, idx->pack->pack_name);
+ git_buf_truncate(&filename, filename.size - strlen("pack"));
+ git_buf_puts(&filename, "idx");
+ if (git_buf_oom(&filename))
+ return -1;
+
+ if (git_filebuf_open(&idx->index_file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS) < 0)
+ goto on_error;
+
+ /* Write out the header */
+ hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
+ hdr.idx_version = htonl(2);
+ git_filebuf_write(&idx->index_file, &hdr, sizeof(hdr));
+
+ /* Write out the fanout table */
+ for (i = 0; i < 256; ++i) {
+ uint32_t n = htonl(idx->fanout[i]);
+ git_filebuf_write(&idx->index_file, &n, sizeof(n));
+ }
+
+ /* Write out the object names (SHA-1 hashes) */
+ SHA1_Init(&ctx);
+ git_vector_foreach(&idx->objects, i, entry) {
+ git_filebuf_write(&idx->index_file, &entry->oid, sizeof(git_oid));
+ SHA1_Update(&ctx, &entry->oid, GIT_OID_RAWSZ);
+ }
+ SHA1_Final(idx->hash.id, &ctx);
+
+ /* Write out the CRC32 values */
+ git_vector_foreach(&idx->objects, i, entry) {
+ git_filebuf_write(&idx->index_file, &entry->crc, sizeof(uint32_t));
+ }
+
+ /* Write out the offsets */
+ git_vector_foreach(&idx->objects, i, entry) {
+ uint32_t n;
+
+ if (entry->offset == UINT32_MAX)
+ n = htonl(0x80000000 | long_offsets++);
else
- error = git__throw(GIT_EOSERR, "Failed to stat packfile.");
+ n = htonl(entry->offset);
- goto cleanup;
+ git_filebuf_write(&idx->index_file, &n, sizeof(uint32_t));
}
- ret = p_open(idx->pack->pack_name, O_RDONLY);
- if (ret < 0) {
- error = git__throw(GIT_EOSERR, "Failed to open packfile");
- goto cleanup;
+ /* Write out the long offsets */
+ git_vector_foreach(&idx->objects, i, entry) {
+ uint32_t split[2];
+
+ if (entry->offset != UINT32_MAX)
+ continue;
+
+ split[0] = htonl(entry->offset_long >> 32);
+ split[1] = htonl(entry->offset_long & 0xffffffff);
+
+ git_filebuf_write(&idx->index_file, &split, sizeof(uint32_t) * 2);
}
- idx->pack->mwf.fd = ret;
- idx->pack->mwf.size = (git_off_t)idx->st.st_size;
+ /* Write out the packfile trailer */
+ packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
+ if (packfile_hash == NULL) {
+ git_mwindow_close(&w);
+ goto on_error;
+ }
- error = parse_header(idx);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to parse packfile header");
- goto cleanup;
+ memcpy(&file_hash, packfile_hash, GIT_OID_RAWSZ);
+ git_mwindow_close(&w);
+
+ git_filebuf_write(&idx->index_file, &file_hash, sizeof(git_oid));
+
+ /* Write out the packfile trailer to the idx file as well */
+ if (git_filebuf_hash(&file_hash, &idx->index_file) < 0)
+ goto on_error;
+
+ git_filebuf_write(&idx->index_file, &file_hash, sizeof(git_oid));
+
+ /* Figure out what the final name should be */
+ if (index_path_stream(&filename, idx, ".idx") < 0)
+ goto on_error;
+
+ /* Commit file */
+ if (git_filebuf_commit_at(&idx->index_file, filename.ptr, GIT_PACK_FILE_MODE) < 0)
+ goto on_error;
+
+ git_mwindow_free_all(&idx->pack->mwf);
+
+ if (index_path_stream(&filename, idx, ".pack") < 0)
+ goto on_error;
+ /* And don't forget to rename the packfile to its new place. */
+ if (git_filebuf_commit_at(&idx->pack_file, filename.ptr, GIT_PACK_FILE_MODE) < 0)
+ return -1;
+
+ git_buf_free(&filename);
+ return 0;
+
+on_error:
+ git_mwindow_free_all(&idx->pack->mwf);
+ git_filebuf_cleanup(&idx->index_file);
+ git_buf_free(&filename);
+ return -1;
+}
+
+void git_indexer_stream_free(git_indexer_stream *idx)
+{
+ unsigned int i;
+ struct entry *e;
+ struct git_pack_entry *pe;
+ struct delta_info *delta;
+
+ if (idx == NULL)
+ return;
+
+ p_close(idx->pack->mwf.fd);
+ git_vector_foreach(&idx->objects, i, e)
+ git__free(e);
+ git_vector_free(&idx->objects);
+ git_vector_foreach(&idx->pack->cache, i, pe)
+ git__free(pe);
+ git_vector_free(&idx->pack->cache);
+ git_vector_foreach(&idx->deltas, i, delta)
+ git__free(delta);
+ git_vector_free(&idx->deltas);
+ git__free(idx->pack);
+ git__free(idx);
+}
+
+int git_indexer_new(git_indexer **out, const char *packname)
+{
+ git_indexer *idx;
+ struct git_pack_header hdr;
+ int error;
+
+ assert(out && packname);
+
+ if (git_path_root(packname) < 0) {
+ giterr_set(GITERR_INVALID, "Path is not absolute");
+ return -1;
}
- idx->nr_objects = ntohl(idx->hdr.hdr_entries);
+ idx = git__calloc(1, sizeof(git_indexer));
+ GITERR_CHECK_ALLOC(idx);
+
+ open_pack(&idx->pack, packname);
+
+ if ((error = parse_header(&hdr, idx->pack)) < 0)
+ goto cleanup;
+
+ idx->nr_objects = ntohl(hdr.hdr_entries);
- error = git_vector_init(&idx->pack->cache, idx->nr_objects, cache_cmp);
- if (error < GIT_SUCCESS)
+ /* for now, limit to 2^32 objects */
+ assert(idx->nr_objects == (size_t)((unsigned int)idx->nr_objects));
+
+ error = git_vector_init(&idx->pack->cache, (unsigned int)idx->nr_objects, cache_cmp);
+ if (error < 0)
goto cleanup;
idx->pack->has_cache = 1;
- error = git_vector_init(&idx->objects, idx->nr_objects, objects_cmp);
- if (error < GIT_SUCCESS)
+ error = git_vector_init(&idx->objects, (unsigned int)idx->nr_objects, objects_cmp);
+ if (error < 0)
goto cleanup;
*out = idx;
- return GIT_SUCCESS;
+ return 0;
cleanup:
git_indexer_free(idx);
- return error;
+ return -1;
}
static int index_path(git_buf *path, git_indexer *idx)
@@ -162,16 +628,16 @@ static int index_path(git_buf *path, git_indexer *idx)
slash--;
if (git_buf_grow(path, slash + 1 + strlen(prefix) +
- GIT_OID_HEXSZ + strlen(suffix) + 1) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ GIT_OID_HEXSZ + strlen(suffix) + 1) < 0)
+ return -1;
git_buf_truncate(path, slash);
git_buf_puts(path, prefix);
- git_oid_fmt(path->ptr + path->size, &idx->hash);
+ git_oid_fmt(path->ptr + git_buf_len(path), &idx->hash);
path->size += GIT_OID_HEXSZ;
git_buf_puts(path, suffix);
- return git_buf_lasterror(path);
+ return git_buf_oom(path) ? -1 : 0;
}
int git_indexer_write(git_indexer *idx)
@@ -191,26 +657,25 @@ int git_indexer_write(git_indexer *idx)
git_buf_sets(&filename, idx->pack->pack_name);
git_buf_truncate(&filename, filename.size - strlen("pack"));
git_buf_puts(&filename, "idx");
-
- if ((error = git_buf_lasterror(&filename)) < GIT_SUCCESS)
- goto cleanup;
+ if (git_buf_oom(&filename))
+ return -1;
error = git_filebuf_open(&idx->file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
/* Write out the header */
hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
hdr.idx_version = htonl(2);
error = git_filebuf_write(&idx->file, &hdr, sizeof(hdr));
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
/* Write out the fanout table */
for (i = 0; i < 256; ++i) {
uint32_t n = htonl(idx->fanout[i]);
error = git_filebuf_write(&idx->file, &n, sizeof(n));
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
}
@@ -219,7 +684,7 @@ int git_indexer_write(git_indexer *idx)
git_vector_foreach(&idx->objects, i, entry) {
error = git_filebuf_write(&idx->file, &entry->oid, sizeof(git_oid));
SHA1_Update(&ctx, &entry->oid, GIT_OID_RAWSZ);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
}
SHA1_Final(idx->hash.id, &ctx);
@@ -227,7 +692,7 @@ int git_indexer_write(git_indexer *idx)
/* Write out the CRC32 values */
git_vector_foreach(&idx->objects, i, entry) {
error = git_filebuf_write(&idx->file, &entry->crc, sizeof(uint32_t));
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
}
@@ -241,7 +706,7 @@ int git_indexer_write(git_indexer *idx)
n = htonl(entry->offset);
error = git_filebuf_write(&idx->file, &n, sizeof(uint32_t));
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
}
@@ -256,16 +721,16 @@ int git_indexer_write(git_indexer *idx)
split[1] = htonl(entry->offset_long & 0xffffffff);
error = git_filebuf_write(&idx->file, &split, sizeof(uint32_t) * 2);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
}
/* Write out the packfile trailer */
- packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->st.st_size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
+ packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
git_mwindow_close(&w);
if (packfile_hash == NULL) {
- error = git__rethrow(GIT_ENOMEM, "Failed to open window to packfile hash");
+ error = -1;
goto cleanup;
}
@@ -274,19 +739,21 @@ int git_indexer_write(git_indexer *idx)
git_mwindow_close(&w);
error = git_filebuf_write(&idx->file, &file_hash, sizeof(git_oid));
+ if (error < 0)
+ goto cleanup;
/* Write out the index sha */
error = git_filebuf_hash(&file_hash, &idx->file);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
error = git_filebuf_write(&idx->file, &file_hash, sizeof(git_oid));
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
/* Figure out what the final name should be */
error = index_path(&filename, idx);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
/* Commit file */
@@ -294,7 +761,7 @@ int git_indexer_write(git_indexer *idx)
cleanup:
git_mwindow_free_all(&idx->pack->mwf);
- if (error < GIT_SUCCESS)
+ if (error < 0)
git_filebuf_cleanup(&idx->file);
git_buf_free(&filename);
@@ -304,7 +771,7 @@ cleanup:
int git_indexer_run(git_indexer *idx, git_indexer_stats *stats)
{
git_mwindow_file *mwf;
- off_t off = sizeof(struct git_pack_header);
+ git_off_t off = sizeof(struct git_pack_header);
int error;
struct entry *entry;
unsigned int left, processed;
@@ -313,10 +780,10 @@ int git_indexer_run(git_indexer *idx, git_indexer_stats *stats)
mwf = &idx->pack->mwf;
error = git_mwindow_file_register(mwf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to register mwindow file");
+ if (error < 0)
+ return error;
- stats->total = idx->nr_objects;
+ stats->total = (unsigned int)idx->nr_objects;
stats->processed = processed = 0;
while (processed < idx->nr_objects) {
@@ -325,62 +792,62 @@ int git_indexer_run(git_indexer *idx, git_indexer_stats *stats)
struct git_pack_entry *pentry;
git_mwindow *w = NULL;
int i;
- off_t entry_start = off;
+ git_off_t entry_start = off;
void *packed;
size_t entry_size;
+ char fmt[GIT_OID_HEXSZ] = {0};
- entry = git__malloc(sizeof(struct entry));
- memset(entry, 0x0, sizeof(struct entry));
+ entry = git__calloc(1, sizeof(*entry));
+ GITERR_CHECK_ALLOC(entry);
if (off > UINT31_MAX) {
entry->offset = UINT32_MAX;
entry->offset_long = off;
} else {
- entry->offset = off;
+ entry->offset = (uint32_t)off;
}
error = git_packfile_unpack(&obj, idx->pack, &off);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to unpack object");
+ if (error < 0)
goto cleanup;
- }
/* FIXME: Parse the object instead of hashing it */
error = git_odb__hashobj(&oid, &obj);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to hash object");
+ if (error < 0) {
+ giterr_set(GITERR_INVALID, "Failed to hash object");
goto cleanup;
}
pentry = git__malloc(sizeof(struct git_pack_entry));
if (pentry == NULL) {
- error = GIT_ENOMEM;
+ error = -1;
goto cleanup;
}
+
git_oid_cpy(&pentry->sha1, &oid);
pentry->offset = entry_start;
+ git_oid_fmt(fmt, &oid);
+ printf("adding %s to cache\n", fmt);
error = git_vector_insert(&idx->pack->cache, pentry);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
git_oid_cpy(&entry->oid, &oid);
entry->crc = crc32(0L, Z_NULL, 0);
- entry_size = off - entry_start;
+ entry_size = (size_t)(off - entry_start);
packed = git_mwindow_open(mwf, &w, entry_start, entry_size, &left);
if (packed == NULL) {
- error = git__rethrow(error, "Failed to open window to read packed data");
+ error = -1;
goto cleanup;
}
- entry->crc = htonl(crc32(entry->crc, packed, entry_size));
+ entry->crc = htonl(crc32(entry->crc, packed, (uInt)entry_size));
git_mwindow_close(&w);
/* Add the object to the list */
error = git_vector_insert(&idx->objects, entry);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to add entry to list");
+ if (error < 0)
goto cleanup;
- }
for (i = oid.id[0]; i < 256; ++i) {
idx->fanout[i]++;
diff --git a/src/iterator.c b/src/iterator.c
index c026c3c09..3a3be1755 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -9,6 +9,7 @@
#include "tree.h"
#include "ignore.h"
#include "buffer.h"
+#include "git2/submodule.h"
typedef struct tree_iterator_frame tree_iterator_frame;
struct tree_iterator_frame {
@@ -34,25 +35,24 @@ static const git_tree_entry *tree_iterator__tree_entry(tree_iterator *ti)
static int tree_iterator__current(
git_iterator *self, const git_index_entry **entry)
{
- int error;
tree_iterator *ti = (tree_iterator *)self;
const git_tree_entry *te = tree_iterator__tree_entry(ti);
*entry = NULL;
if (te == NULL)
- return GIT_SUCCESS;
+ return 0;
ti->entry.mode = te->attr;
git_oid_cpy(&ti->entry.oid, &te->oid);
- error = git_buf_joinpath(&ti->path, ti->path.ptr, te->filename);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
+ return -1;
+
ti->entry.path = ti->path.ptr;
*entry = &ti->entry;
- return GIT_SUCCESS;
+ return 0;
}
static int tree_iterator__at_end(git_iterator *self)
@@ -63,7 +63,8 @@ static int tree_iterator__at_end(git_iterator *self)
static tree_iterator_frame *tree_iterator__alloc_frame(git_tree *tree)
{
tree_iterator_frame *tf = git__calloc(1, sizeof(tree_iterator_frame));
- tf->tree = tree;
+ if (tf != NULL)
+ tf->tree = tree;
return tf;
}
@@ -75,24 +76,22 @@ static int tree_iterator__expand_tree(tree_iterator *ti)
tree_iterator_frame *tf;
while (te != NULL && entry_is_tree(te)) {
- error = git_tree_lookup(&subtree, ti->repo, &te->oid);
- if (error != GIT_SUCCESS)
+ if ((error = git_tree_lookup(&subtree, ti->repo, &te->oid)) < 0)
return error;
if ((tf = tree_iterator__alloc_frame(subtree)) == NULL)
- return GIT_ENOMEM;
+ return -1;
tf->next = ti->stack;
ti->stack = tf;
- error = git_buf_joinpath(&ti->path, ti->path.ptr, te->filename);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
+ return -1;
te = tree_iterator__tree_entry(ti);
}
- return GIT_SUCCESS;
+ return 0;
}
static void tree_iterator__pop_frame(tree_iterator *ti)
@@ -107,7 +106,7 @@ static void tree_iterator__pop_frame(tree_iterator *ti)
static int tree_iterator__advance(
git_iterator *self, const git_index_entry **entry)
{
- int error = GIT_SUCCESS;
+ int error = 0;
tree_iterator *ti = (tree_iterator *)self;
const git_tree_entry *te = NULL;
@@ -123,13 +122,12 @@ static int tree_iterator__advance(
break;
tree_iterator__pop_frame(ti);
- git_buf_rtruncate_at_char(&ti->path, '/');
}
if (te && entry_is_tree(te))
error = tree_iterator__expand_tree(ti);
- if (error == GIT_SUCCESS && entry != NULL)
+ if (!error && entry != NULL)
error = tree_iterator__current(self, entry);
return error;
@@ -158,8 +156,7 @@ int git_iterator_for_tree(
{
int error;
tree_iterator *ti = git__calloc(1, sizeof(tree_iterator));
- if (!ti)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(ti);
ti->base.type = GIT_ITERATOR_TREE;
ti->base.current = tree_iterator__current;
@@ -170,11 +167,10 @@ int git_iterator_for_tree(
ti->repo = repo;
ti->stack = tree_iterator__alloc_frame(tree);
- if ((error = tree_iterator__expand_tree(ti)) < GIT_SUCCESS)
+ if ((error = tree_iterator__expand_tree(ti)) < 0)
git_iterator_free((git_iterator *)ti);
else
*iter = (git_iterator *)ti;
-
return error;
}
@@ -190,7 +186,7 @@ static int index_iterator__current(
{
index_iterator *ii = (index_iterator *)self;
*entry = git_index_get(ii->index, ii->current);
- return GIT_SUCCESS;
+ return 0;
}
static int index_iterator__at_end(git_iterator *self)
@@ -207,14 +203,14 @@ static int index_iterator__advance(
ii->current++;
if (entry)
*entry = git_index_get(ii->index, ii->current);
- return GIT_SUCCESS;
+ return 0;
}
static int index_iterator__reset(git_iterator *self)
{
index_iterator *ii = (index_iterator *)self;
ii->current = 0;
- return GIT_SUCCESS;
+ return 0;
}
static void index_iterator__free(git_iterator *self)
@@ -228,8 +224,7 @@ int git_iterator_for_index(git_repository *repo, git_iterator **iter)
{
int error;
index_iterator *ii = git__calloc(1, sizeof(index_iterator));
- if (!ii)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(ii);
ii->base.type = GIT_ITERATOR_INDEX;
ii->base.current = index_iterator__current;
@@ -239,7 +234,7 @@ int git_iterator_for_index(git_repository *repo, git_iterator **iter)
ii->base.free = index_iterator__free;
ii->current = 0;
- if ((error = git_repository_index(&ii->index, repo)) < GIT_SUCCESS)
+ if ((error = git_repository_index(&ii->index, repo)) < 0)
git__free(ii);
else
*iter = (git_iterator *)ii;
@@ -269,8 +264,8 @@ static workdir_iterator_frame *workdir_iterator__alloc_frame(void)
{
workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
if (wf == NULL)
- return wf;
- if (git_vector_init(&wf->entries, 0, git_path_with_stat_cmp) != GIT_SUCCESS) {
+ return NULL;
+ if (git_vector_init(&wf->entries, 0, git_path_with_stat_cmp) != 0) {
git__free(wf);
return NULL;
}
@@ -294,11 +289,10 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi)
{
int error;
workdir_iterator_frame *wf = workdir_iterator__alloc_frame();
- if (wf == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(wf);
error = git_path_dirload_with_stat(wi->path.ptr, wi->root_len, &wf->entries);
- if (error < GIT_SUCCESS || wf->entries.length == 0) {
+ if (error < 0 || wf->entries.length == 0) {
workdir_iterator__free_frame(wf);
return GIT_ENOTFOUND;
}
@@ -309,7 +303,7 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi)
/* only push new ignores if this is not top level directory */
if (wi->stack->next != NULL) {
- int slash_pos = git_buf_rfind_next(&wi->path, '/');
+ ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/');
(void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]);
}
@@ -321,7 +315,7 @@ static int workdir_iterator__current(
{
workdir_iterator *wi = (workdir_iterator *)self;
*entry = (wi->entry.path == NULL) ? NULL : &wi->entry;
- return GIT_SUCCESS;
+ return 0;
}
static int workdir_iterator__at_end(git_iterator *self)
@@ -341,7 +335,7 @@ static int workdir_iterator__advance(
*entry = NULL;
if (wi->entry.path == NULL)
- return GIT_SUCCESS;
+ return 0;
while ((wf = wi->stack) != NULL) {
next = git_vector_get(&wf->entries, ++wf->index);
@@ -359,13 +353,13 @@ static int workdir_iterator__advance(
if (wi->stack == NULL) {
memset(&wi->entry, 0, sizeof(wi->entry));
- return GIT_SUCCESS;
+ return 0;
}
}
error = workdir_iterator__update_entry(wi);
- if (error == GIT_SUCCESS && entry != NULL)
+ if (!error && entry != NULL)
error = workdir_iterator__current(self, entry);
return error;
@@ -382,7 +376,7 @@ static int workdir_iterator__reset(git_iterator *self)
}
if (wi->stack)
wi->stack->index = 0;
- return GIT_SUCCESS;
+ return 0;
}
static void workdir_iterator__free(git_iterator *self)
@@ -401,13 +395,11 @@ static void workdir_iterator__free(git_iterator *self)
static int workdir_iterator__update_entry(workdir_iterator *wi)
{
- int error;
git_path_with_stat *ps = git_vector_get(&wi->stack->entries, wi->stack->index);
git_buf_truncate(&wi->path, wi->root_len);
- error = git_buf_put(&wi->path, ps->path, ps->path_len);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)
+ return -1;
memset(&wi->entry, 0, sizeof(wi->entry));
wi->entry.path = ps->path;
@@ -419,39 +411,48 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
/* if there is an error processing the entry, treat as ignored */
wi->is_ignored = 1;
- /* TODO: remove shared code for struct stat conversion with index.c */
- wi->entry.ctime.seconds = (git_time_t)ps->st.st_ctime;
- wi->entry.mtime.seconds = (git_time_t)ps->st.st_mtime;
- wi->entry.dev = ps->st.st_rdev;
- wi->entry.ino = ps->st.st_ino;
+ git_index__init_entry_from_stat(&ps->st, &wi->entry);
+
+ /* need different mode here to keep directories during iteration */
wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
- wi->entry.uid = ps->st.st_uid;
- wi->entry.gid = ps->st.st_gid;
- wi->entry.file_size = ps->st.st_size;
/* if this is a file type we don't handle, treat as ignored */
if (wi->entry.mode == 0)
- return GIT_SUCCESS;
+ return 0;
/* okay, we are far enough along to look up real ignore rule */
- error = git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored);
- if (error != GIT_SUCCESS)
- return GIT_SUCCESS;
+ if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0)
+ return 0; /* if error, ignore it and ignore file */
/* detect submodules */
- if (S_ISDIR(wi->entry.mode) &&
- git_path_contains(&wi->path, DOT_GIT) == GIT_SUCCESS)
- wi->entry.mode = S_IFGITLINK;
+ if (S_ISDIR(wi->entry.mode)) {
+ bool is_submodule = git_path_contains(&wi->path, DOT_GIT);
+
+ /* if there is no .git, still check submodules data */
+ if (!is_submodule) {
+ int res = git_submodule_lookup(NULL, wi->repo, wi->entry.path);
+ is_submodule = (res == 0);
+ if (res == GIT_ENOTFOUND)
+ giterr_clear();
+ }
- return GIT_SUCCESS;
+ /* if submodule, mark as GITLINK and remove trailing slash */
+ if (is_submodule) {
+ size_t len = strlen(wi->entry.path);
+ assert(wi->entry.path[len - 1] == '/');
+ wi->entry.path[len - 1] = '\0';
+ wi->entry.mode = S_IFGITLINK;
+ }
+ }
+
+ return 0;
}
int git_iterator_for_workdir(git_repository *repo, git_iterator **iter)
{
int error;
workdir_iterator *wi = git__calloc(1, sizeof(workdir_iterator));
- if (!wi)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(wi);
wi->base.type = GIT_ITERATOR_WORKDIR;
wi->base.current = workdir_iterator__current;
@@ -461,19 +462,17 @@ int git_iterator_for_workdir(git_repository *repo, git_iterator **iter)
wi->base.free = workdir_iterator__free;
wi->repo = repo;
- error = git_buf_sets(&wi->path, git_repository_workdir(repo));
- if (error == GIT_SUCCESS)
- error = git_path_to_dir(&wi->path);
- if (error == GIT_SUCCESS)
- error = git_ignore__for_path(repo, "", &wi->ignores);
- if (error != GIT_SUCCESS) {
+ if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||
+ git_path_to_dir(&wi->path) < 0 ||
+ git_ignore__for_path(repo, "", &wi->ignores) < 0)
+ {
git__free(wi);
- return error;
+ return -1;
}
wi->root_len = wi->path.size;
- if ((error = workdir_iterator__expand_dir(wi)) < GIT_SUCCESS)
+ if ((error = workdir_iterator__expand_dir(wi)) < 0)
git_iterator_free((git_iterator *)wi);
else
*iter = (git_iterator *)wi;
@@ -487,7 +486,7 @@ int git_iterator_current_tree_entry(
{
*tree_entry = (iter->type != GIT_ITERATOR_TREE) ? NULL :
tree_iterator__tree_entry((tree_iterator *)iter);
- return GIT_SUCCESS;
+ return 0;
}
int git_iterator_current_is_ignored(git_iterator *iter)
@@ -502,12 +501,14 @@ int git_iterator_advance_into_directory(
workdir_iterator *wi = (workdir_iterator *)iter;
if (iter->type == GIT_ITERATOR_WORKDIR &&
- wi->entry.path && S_ISDIR(wi->entry.mode))
+ wi->entry.path &&
+ S_ISDIR(wi->entry.mode) &&
+ !S_ISGITLINK(wi->entry.mode))
{
- if (workdir_iterator__expand_dir(wi) < GIT_SUCCESS)
+ if (workdir_iterator__expand_dir(wi) < 0)
/* if error loading or if empty, skip the directory. */
return workdir_iterator__advance(iter, entry);
}
- return entry ? git_iterator_current(iter, entry) : GIT_SUCCESS;
+ return entry ? git_iterator_current(iter, entry) : 0;
}
diff --git a/src/khash.h b/src/khash.h
new file mode 100644
index 000000000..bd67fe1f7
--- /dev/null
+++ b/src/khash.h
@@ -0,0 +1,608 @@
+/* The MIT License
+
+ Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+/*
+ An example:
+
+#include "khash.h"
+KHASH_MAP_INIT_INT(32, char)
+int main() {
+ int ret, is_missing;
+ khiter_t k;
+ khash_t(32) *h = kh_init(32);
+ k = kh_put(32, h, 5, &ret);
+ kh_value(h, k) = 10;
+ k = kh_get(32, h, 10);
+ is_missing = (k == kh_end(h));
+ k = kh_get(32, h, 5);
+ kh_del(32, h, k);
+ for (k = kh_begin(h); k != kh_end(h); ++k)
+ if (kh_exist(h, k)) kh_value(h, k) = 1;
+ kh_destroy(32, h);
+ return 0;
+}
+*/
+
+/*
+ 2011-12-29 (0.2.7):
+
+ * Minor code clean up; no actual effect.
+
+ 2011-09-16 (0.2.6):
+
+ * The capacity is a power of 2. This seems to dramatically improve the
+ speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
+
+ - http://code.google.com/p/ulib/
+ - http://nothings.org/computer/judy/
+
+ * Allow to optionally use linear probing which usually has better
+ performance for random input. Double hashing is still the default as it
+ is more robust to certain non-random input.
+
+ * Added Wang's integer hash function (not used by default). This hash
+ function is more robust to certain non-random input.
+
+ 2011-02-14 (0.2.5):
+
+ * Allow to declare global functions.
+
+ 2009-09-26 (0.2.4):
+
+ * Improve portability
+
+ 2008-09-19 (0.2.3):
+
+ * Corrected the example
+ * Improved interfaces
+
+ 2008-09-11 (0.2.2):
+
+ * Improved speed a little in kh_put()
+
+ 2008-09-10 (0.2.1):
+
+ * Added kh_clear()
+ * Fixed a compiling error
+
+ 2008-09-02 (0.2.0):
+
+ * Changed to token concatenation which increases flexibility.
+
+ 2008-08-31 (0.1.2):
+
+ * Fixed a bug in kh_get(), which has not been tested previously.
+
+ 2008-08-31 (0.1.1):
+
+ * Added destructor
+*/
+
+
+#ifndef __AC_KHASH_H
+#define __AC_KHASH_H
+
+/*!
+ @header
+
+ Generic hash table library.
+ */
+
+#define AC_VERSION_KHASH_H "0.2.6"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* compipler specific configuration */
+
+#if UINT_MAX == 0xffffffffu
+typedef unsigned int khint32_t;
+#elif ULONG_MAX == 0xffffffffu
+typedef unsigned long khint32_t;
+#endif
+
+#if ULONG_MAX == ULLONG_MAX
+typedef unsigned long khint64_t;
+#else
+typedef unsigned long long khint64_t;
+#endif
+
+#ifdef _MSC_VER
+#define inline __inline
+#endif
+
+typedef khint32_t khint_t;
+typedef khint_t khiter_t;
+
+#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
+#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
+#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
+#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
+#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
+#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
+#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
+
+#ifdef KHASH_LINEAR
+#define __ac_inc(k, m) 1
+#else
+#define __ac_inc(k, m) (((k)>>3 ^ (k)<<3) | 1) & (m)
+#endif
+
+#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
+
+#ifndef kroundup32
+#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
+#endif
+
+#ifndef kcalloc
+#define kcalloc(N,Z) calloc(N,Z)
+#endif
+#ifndef kmalloc
+#define kmalloc(Z) malloc(Z)
+#endif
+#ifndef krealloc
+#define krealloc(P,Z) realloc(P,Z)
+#endif
+#ifndef kfree
+#define kfree(P) free(P)
+#endif
+
+static const double __ac_HASH_UPPER = 0.77;
+
+#define __KHASH_TYPE(name, khkey_t, khval_t) \
+ typedef struct { \
+ khint_t n_buckets, size, n_occupied, upper_bound; \
+ khint32_t *flags; \
+ khkey_t *keys; \
+ khval_t *vals; \
+ } kh_##name##_t;
+
+#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
+ extern kh_##name##_t *kh_init_##name(void); \
+ extern void kh_destroy_##name(kh_##name##_t *h); \
+ extern void kh_clear_##name(kh_##name##_t *h); \
+ extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
+ extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
+ extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
+ extern void kh_del_##name(kh_##name##_t *h, khint_t x);
+
+#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+ SCOPE kh_##name##_t *kh_init_##name(void) { \
+ return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
+ } \
+ SCOPE void kh_destroy_##name(kh_##name##_t *h) \
+ { \
+ if (h) { \
+ kfree((void *)h->keys); kfree(h->flags); \
+ kfree((void *)h->vals); \
+ kfree(h); \
+ } \
+ } \
+ SCOPE void kh_clear_##name(kh_##name##_t *h) \
+ { \
+ if (h && h->flags) { \
+ memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
+ h->size = h->n_occupied = 0; \
+ } \
+ } \
+ SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
+ { \
+ if (h->n_buckets) { \
+ khint_t inc, k, i, last, mask; \
+ mask = h->n_buckets - 1; \
+ k = __hash_func(key); i = k & mask; \
+ inc = __ac_inc(k, mask); last = i; /* inc==1 for linear probing */ \
+ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+ i = (i + inc) & mask; \
+ if (i == last) return h->n_buckets; \
+ } \
+ return __ac_iseither(h->flags, i)? h->n_buckets : i; \
+ } else return 0; \
+ } \
+ SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
+ { /* This function uses 0.25*n_bucktes bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
+ khint32_t *new_flags = 0; \
+ khint_t j = 1; \
+ { \
+ kroundup32(new_n_buckets); \
+ if (new_n_buckets < 4) new_n_buckets = 4; \
+ if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
+ else { /* hash table size to be changed (shrink or expand); rehash */ \
+ new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+ if (!new_flags) return -1; \
+ memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+ if (h->n_buckets < new_n_buckets) { /* expand */ \
+ khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
+ if (!new_keys) return -1; \
+ h->keys = new_keys; \
+ if (kh_is_map) { \
+ khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+ if (!new_vals) return -1; \
+ h->vals = new_vals; \
+ } \
+ } /* otherwise shrink */ \
+ } \
+ } \
+ if (j) { /* rehashing is needed */ \
+ for (j = 0; j != h->n_buckets; ++j) { \
+ if (__ac_iseither(h->flags, j) == 0) { \
+ khkey_t key = h->keys[j]; \
+ khval_t val; \
+ khint_t new_mask; \
+ new_mask = new_n_buckets - 1; \
+ if (kh_is_map) val = h->vals[j]; \
+ __ac_set_isdel_true(h->flags, j); \
+ while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
+ khint_t inc, k, i; \
+ k = __hash_func(key); \
+ i = k & new_mask; \
+ inc = __ac_inc(k, new_mask); \
+ while (!__ac_isempty(new_flags, i)) i = (i + inc) & new_mask; \
+ __ac_set_isempty_false(new_flags, i); \
+ if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
+ { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
+ if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
+ __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
+ } else { /* write the element and jump out of the loop */ \
+ h->keys[i] = key; \
+ if (kh_is_map) h->vals[i] = val; \
+ break; \
+ } \
+ } \
+ } \
+ } \
+ if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
+ h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
+ if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+ } \
+ kfree(h->flags); /* free the working space */ \
+ h->flags = new_flags; \
+ h->n_buckets = new_n_buckets; \
+ h->n_occupied = h->size; \
+ h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
+ } \
+ return 0; \
+ } \
+ SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
+ { \
+ khint_t x; \
+ if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
+ if (h->n_buckets > (h->size<<1)) { \
+ if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
+ *ret = -1; return h->n_buckets; \
+ } \
+ } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
+ *ret = -1; return h->n_buckets; \
+ } \
+ } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
+ { \
+ khint_t inc, k, i, site, last, mask = h->n_buckets - 1; \
+ x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
+ if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
+ else { \
+ inc = __ac_inc(k, mask); last = i; \
+ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+ if (__ac_isdel(h->flags, i)) site = i; \
+ i = (i + inc) & mask; \
+ if (i == last) { x = site; break; } \
+ } \
+ if (x == h->n_buckets) { \
+ if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
+ else x = i; \
+ } \
+ } \
+ } \
+ if (__ac_isempty(h->flags, x)) { /* not present at all */ \
+ h->keys[x] = key; \
+ __ac_set_isboth_false(h->flags, x); \
+ ++h->size; ++h->n_occupied; \
+ *ret = 1; \
+ } else if (__ac_isdel(h->flags, x)) { /* deleted */ \
+ h->keys[x] = key; \
+ __ac_set_isboth_false(h->flags, x); \
+ ++h->size; \
+ *ret = 2; \
+ } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
+ return x; \
+ } \
+ SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
+ { \
+ if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
+ __ac_set_isdel_true(h->flags, x); \
+ --h->size; \
+ } \
+ }
+
+#define KHASH_DECLARE(name, khkey_t, khval_t) \
+ __KHASH_TYPE(name, khkey_t, khval_t) \
+ __KHASH_PROTOTYPES(name, khkey_t, khval_t)
+
+#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+ __KHASH_TYPE(name, khkey_t, khval_t) \
+ __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)
+
+/* --- BEGIN OF HASH FUNCTIONS --- */
+
+/*! @function
+ @abstract Integer hash function
+ @param key The integer [khint32_t]
+ @return The hash value [khint_t]
+ */
+#define kh_int_hash_func(key) (khint32_t)(key)
+/*! @function
+ @abstract Integer comparison function
+ */
+#define kh_int_hash_equal(a, b) ((a) == (b))
+/*! @function
+ @abstract 64-bit integer hash function
+ @param key The integer [khint64_t]
+ @return The hash value [khint_t]
+ */
+#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
+/*! @function
+ @abstract 64-bit integer comparison function
+ */
+#define kh_int64_hash_equal(a, b) ((a) == (b))
+/*! @function
+ @abstract const char* hash function
+ @param s Pointer to a null terminated string
+ @return The hash value
+ */
+static 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;
+ return h;
+}
+/*! @function
+ @abstract Another interface to const char* hash function
+ @param key Pointer to a null terminated string [const char*]
+ @return The hash value [khint_t]
+ */
+#define kh_str_hash_func(key) __ac_X31_hash_string(key)
+/*! @function
+ @abstract Const char* comparison function
+ */
+#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
+
+static inline khint_t __ac_Wang_hash(khint_t key)
+{
+ key += ~(key << 15);
+ key ^= (key >> 10);
+ key += (key << 3);
+ key ^= (key >> 6);
+ key += ~(key << 11);
+ key ^= (key >> 16);
+ return key;
+}
+#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key)
+
+/* --- END OF HASH FUNCTIONS --- */
+
+/* Other convenient macros... */
+
+/*!
+ @abstract Type of the hash table.
+ @param name Name of the hash table [symbol]
+ */
+#define khash_t(name) kh_##name##_t
+
+/*! @function
+ @abstract Initiate a hash table.
+ @param name Name of the hash table [symbol]
+ @return Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_init(name) kh_init_##name()
+
+/*! @function
+ @abstract Destroy a hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_destroy(name, h) kh_destroy_##name(h)
+
+/*! @function
+ @abstract Reset a hash table without deallocating memory.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_clear(name, h) kh_clear_##name(h)
+
+/*! @function
+ @abstract Resize a hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param s New size [khint_t]
+ */
+#define kh_resize(name, h, s) kh_resize_##name(h, s)
+
+/*! @function
+ @abstract Insert a key to the hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param k Key [type of keys]
+ @param r Extra return code: 0 if the key is present in the hash table;
+ 1 if the bucket is empty (never used); 2 if the element in
+ the bucket has been deleted [int*]
+ @return Iterator to the inserted element [khint_t]
+ */
+#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
+
+/*! @function
+ @abstract Retrieve a key from the hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param k Key [type of keys]
+ @return Iterator to the found element, or kh_end(h) is the element is absent [khint_t]
+ */
+#define kh_get(name, h, k) kh_get_##name(h, k)
+
+/*! @function
+ @abstract Remove a key from the hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param k Iterator to the element to be deleted [khint_t]
+ */
+#define kh_del(name, h, k) kh_del_##name(h, k)
+
+/*! @function
+ @abstract Test whether a bucket contains data.
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param x Iterator to the bucket [khint_t]
+ @return 1 if containing data; 0 otherwise [int]
+ */
+#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
+
+/*! @function
+ @abstract Get key given an iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param x Iterator to the bucket [khint_t]
+ @return Key [type of keys]
+ */
+#define kh_key(h, x) ((h)->keys[x])
+
+/*! @function
+ @abstract Get value given an iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param x Iterator to the bucket [khint_t]
+ @return Value [type of values]
+ @discussion For hash sets, calling this results in segfault.
+ */
+#define kh_val(h, x) ((h)->vals[x])
+
+/*! @function
+ @abstract Alias of kh_val()
+ */
+#define kh_value(h, x) ((h)->vals[x])
+
+/*! @function
+ @abstract Get the start iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return The start iterator [khint_t]
+ */
+#define kh_begin(h) (khint_t)(0)
+
+/*! @function
+ @abstract Get the end iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return The end iterator [khint_t]
+ */
+#define kh_end(h) ((h)->n_buckets)
+
+/*! @function
+ @abstract Get the number of elements in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return Number of elements in the hash table [khint_t]
+ */
+#define kh_size(h) ((h)->size)
+
+/*! @function
+ @abstract Get the number of buckets in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return Number of buckets in the hash table [khint_t]
+ */
+#define kh_n_buckets(h) ((h)->n_buckets)
+
+/*! @function
+ @abstract Iterate over the entries in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param kvar Variable to which key will be assigned
+ @param vvar Variable to which value will be assigned
+ @param code Block of code to execute
+ */
+#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
+ for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
+ if (!kh_exist(h,__i)) continue; \
+ (kvar) = kh_key(h,__i); \
+ (vvar) = kh_val(h,__i); \
+ code; \
+ } }
+
+/*! @function
+ @abstract Iterate over the values in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param vvar Variable to which value will be assigned
+ @param code Block of code to execute
+ */
+#define kh_foreach_value(h, vvar, code) { khint_t __i; \
+ for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
+ if (!kh_exist(h,__i)) continue; \
+ (vvar) = kh_val(h,__i); \
+ code; \
+ } }
+
+/* More conenient interfaces */
+
+/*! @function
+ @abstract Instantiate a hash set containing integer keys
+ @param name Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT(name) \
+ KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing integer keys
+ @param name Name of the hash table [symbol]
+ @param khval_t Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT(name, khval_t) \
+ KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing 64-bit integer keys
+ @param name Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT64(name) \
+ KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing 64-bit integer keys
+ @param name Name of the hash table [symbol]
+ @param khval_t Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT64(name, khval_t) \
+ KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
+
+typedef const char *kh_cstr_t;
+/*! @function
+ @abstract Instantiate a hash map containing const char* keys
+ @param name Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_STR(name) \
+ KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing const char* keys
+ @param name Name of the hash table [symbol]
+ @param khval_t Type of values [type]
+ */
+#define KHASH_MAP_INIT_STR(name, khval_t) \
+ KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
+
+#endif /* __AC_KHASH_H */
diff --git a/src/map.h b/src/map.h
index 0b070fa15..96d879547 100644
--- a/src/map.h
+++ b/src/map.h
@@ -31,6 +31,11 @@ typedef struct { /* memory mapped buffer */
#endif
} git_map;
+#define GIT_MMAP_VALIDATE(out, len, prot, flags) do { \
+ assert(out != NULL && len > 0); \
+ assert((prot & GIT_PROT_WRITE) || (prot & GIT_PROT_READ)); \
+ assert((flags & GIT_MAP_FIXED) == 0); } while (0)
+
extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset);
extern int p_munmap(git_map *map);
diff --git a/src/mwindow.c b/src/mwindow.c
index f657d9d34..fa5549021 100644
--- a/src/mwindow.c
+++ b/src/mwindow.c
@@ -89,6 +89,7 @@ void git_mwindow_scan_lru(
{
git_mwindow *w, *w_l;
+ puts("LRU");
for (w_l = NULL, w = mwf->windows; w; w = w->next) {
if (!w->inuse_cnt) {
/*
@@ -115,7 +116,7 @@ static int git_mwindow_close_lru(git_mwindow_file *mwf)
unsigned int i;
git_mwindow *lru_w = NULL, *lru_l = NULL, **list = &mwf->windows;
- /* FIMXE: Does this give us any advantage? */
+ /* FIXME: Does this give us any advantage? */
if(mwf->windows)
git_mwindow_scan_lru(mwf, &lru_w, &lru_l);
@@ -127,22 +128,23 @@ static int git_mwindow_close_lru(git_mwindow_file *mwf)
list = &cur->windows;
}
- if (lru_w) {
- ctl->mapped -= lru_w->window_map.len;
- git_futils_mmap_free(&lru_w->window_map);
+ if (!lru_w) {
+ giterr_set(GITERR_OS, "Failed to close memory window. Couldn't find LRU");
+ return -1;
+ }
- if (lru_l)
- lru_l->next = lru_w->next;
- else
- *list = lru_w->next;
+ ctl->mapped -= lru_w->window_map.len;
+ git_futils_mmap_free(&lru_w->window_map);
- git__free(lru_w);
- ctl->open_windows--;
+ if (lru_l)
+ lru_l->next = lru_w->next;
+ else
+ *list = lru_w->next;
- return GIT_SUCCESS;
- }
+ git__free(lru_w);
+ ctl->open_windows--;
- return git__throw(GIT_ERROR, "Failed to close memory window. Couln't find LRU");
+ return 0;
}
static git_mwindow *new_window(
@@ -158,7 +160,7 @@ static git_mwindow *new_window(
w = git__malloc(sizeof(*w));
if (w == NULL)
- return w;
+ return NULL;
memset(w, 0x0, sizeof(*w));
w->offset = (offset / walign) * walign;
@@ -170,7 +172,7 @@ static git_mwindow *new_window(
ctl->mapped += (size_t)len;
while (_mw_options.mapped_limit < ctl->mapped &&
- git_mwindow_close_lru(mwf) == GIT_SUCCESS) /* nop */;
+ git_mwindow_close_lru(mwf) == 0) /* nop */;
/*
* We treat _mw_options.mapped_limit as a soft limit. If we can't find a
@@ -178,8 +180,10 @@ static git_mwindow *new_window(
* window.
*/
- if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < GIT_SUCCESS)
- goto cleanup;
+ if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
+ git__free(w);
+ return NULL;
+ }
ctl->mmap_calls++;
ctl->open_windows++;
@@ -191,10 +195,6 @@ static git_mwindow *new_window(
ctl->peak_open_windows = ctl->open_windows;
return w;
-
-cleanup:
- git__free(w);
- return NULL;
}
/*
@@ -205,14 +205,13 @@ unsigned char *git_mwindow_open(
git_mwindow_file *mwf,
git_mwindow **cursor,
git_off_t offset,
- int extra,
+ size_t extra,
unsigned int *left)
{
git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;
git_mwindow *w = *cursor;
- if (!w || !(git_mwindow_contains(w, offset) &&
- git_mwindow_contains(w, offset + extra))) {
+ if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) {
if (w) {
w->inuse_cnt--;
}
@@ -221,6 +220,7 @@ unsigned char *git_mwindow_open(
if (git_mwindow_contains(w, offset) &&
git_mwindow_contains(w, offset + extra))
break;
+ }
}
/*
@@ -248,17 +248,17 @@ unsigned char *git_mwindow_open(
if (left)
*left = (unsigned int)(w->window_map.len - offset);
+ fflush(stdout);
return (unsigned char *) w->window_map.data + offset;
}
int git_mwindow_file_register(git_mwindow_file *mwf)
{
git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;
- int error;
if (ctl->windowfiles.length == 0 &&
- (error = git_vector_init(&ctl->windowfiles, 8, NULL)) < GIT_SUCCESS)
- return error;
+ git_vector_init(&ctl->windowfiles, 8, NULL) < 0)
+ return -1;
return git_vector_insert(&ctl->windowfiles, mwf);
}
diff --git a/src/mwindow.h b/src/mwindow.h
index 94bfb5d61..058027251 100644
--- a/src/mwindow.h
+++ b/src/mwindow.h
@@ -15,8 +15,8 @@ typedef struct git_mwindow {
struct git_mwindow *next;
git_map window_map;
git_off_t offset;
- unsigned int last_used;
- unsigned int inuse_cnt;
+ size_t last_used;
+ size_t inuse_cnt;
} git_mwindow;
typedef struct git_mwindow_file {
@@ -37,7 +37,7 @@ typedef struct git_mwindow_ctl {
int git_mwindow_contains(git_mwindow *win, git_off_t offset);
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, int extra, unsigned int *left);
+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);
void git_mwindow_close(git_mwindow **w_cursor);
diff --git a/src/netops.c b/src/netops.c
index 4b307af45..e2fec0b48 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -25,8 +25,28 @@
#include "common.h"
#include "netops.h"
#include "posix.h"
+#include "buffer.h"
-void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, int fd)
+#ifdef GIT_WIN32
+static void net_set_error(const char *str)
+{
+ int size, error = WSAGetLastError();
+ LPSTR err_str = NULL;
+
+ size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, error, 0, (LPSTR)&err_str, 0, 0);
+
+ giterr_set(GITERR_NET, "%s: $s", str, err_str);
+ LocalFree(err_str);
+}
+#else
+static void net_set_error(const char *str)
+{
+ giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
+}
+#endif
+
+void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd)
{
memset(buf, 0x0, sizeof(gitno_buffer));
memset(data, 0x0, len);
@@ -40,14 +60,13 @@ int gitno_recv(gitno_buffer *buf)
{
int ret;
- ret = recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);
- if (ret < 0)
- return git__throw(GIT_EOSERR, "Failed to receive data: %s", strerror(errno));
- if (ret == 0) /* Orderly shutdown, so exit */
- return GIT_SUCCESS;
+ ret = p_recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);
+ if (ret < 0) {
+ net_set_error("Error receiving socket data");
+ return -1;
+ }
buf->offset += ret;
-
return ret;
}
@@ -74,52 +93,43 @@ void gitno_consume_n(gitno_buffer *buf, size_t cons)
buf->offset -= cons;
}
-int gitno_connect(const char *host, const char *port)
+GIT_SOCKET gitno_connect(const char *host, const char *port)
{
- struct addrinfo *info, *p;
+ struct addrinfo *info = NULL, *p;
struct addrinfo hints;
- int ret, error = GIT_SUCCESS;
- GIT_SOCKET s;
+ int ret;
+ GIT_SOCKET s = INVALID_SOCKET;
memset(&hints, 0x0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
- ret = getaddrinfo(host, port, &hints, &info);
- if (ret != 0) {
- error = GIT_EOSERR;
- info = NULL;
- goto cleanup;
+ if ((ret = getaddrinfo(host, port, &hints, &info)) < 0) {
+ giterr_set(GITERR_NET, "Failed to resolve address for %s: %s", host, gai_strerror(ret));
+ return INVALID_SOCKET;
}
for (p = info; p != NULL; p = p->ai_next) {
s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
-#ifdef GIT_WIN32
if (s == INVALID_SOCKET) {
-#else
- if (s < 0) {
-#endif
- error = GIT_EOSERR;
- goto cleanup;
+ net_set_error("error creating socket");
+ break;
}
- ret = connect(s, p->ai_addr, p->ai_addrlen);
- /* If we can't connect, try the next one */
- if (ret < 0) {
- continue;
- }
+ if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
+ break;
- /* Return the socket */
- error = s;
- goto cleanup;
+ /* If we can't connect, try the next one */
+ gitno_close(s);
+ s = INVALID_SOCKET;
}
/* Oops, we couldn't connect to any address */
- error = git__throw(GIT_EOSERR, "Failed to connect: %s", strerror(errno));
+ if (s == INVALID_SOCKET && p == NULL)
+ giterr_set(GITERR_OS, "Failed to connect to %s", host);
-cleanup:
freeaddrinfo(info);
- return error;
+ return s;
}
int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags)
@@ -130,14 +140,16 @@ int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags)
while (off < len) {
errno = 0;
- ret = send(s, msg + off, len - off, flags);
- if (ret < 0)
- return git__throw(GIT_EOSERR, "Error sending data: %s", strerror(errno));
+ ret = p_send(s, msg + off, len - off, flags);
+ if (ret < 0) {
+ net_set_error("Error sending data");
+ return -1;
+ }
off += ret;
}
- return off;
+ return (int)off;
}
@@ -165,35 +177,31 @@ int gitno_select_in(gitno_buffer *buf, long int sec, long int usec)
FD_SET(buf->fd, &fds);
/* The select(2) interface is silly */
- return select(buf->fd + 1, &fds, NULL, NULL, &tv);
+ return select((int)buf->fd + 1, &fds, NULL, NULL, &tv);
}
int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port)
{
char *colon, *slash, *delim;
- int error = GIT_SUCCESS;
colon = strchr(url, ':');
slash = strchr(url, '/');
- if (slash == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Malformed URL: missing /");
+ if (slash == NULL) {
+ giterr_set(GITERR_NET, "Malformed URL: missing /");
+ return -1;
+ }
if (colon == NULL) {
*port = git__strdup(default_port);
} else {
*port = git__strndup(colon + 1, slash - colon - 1);
}
- if (*port == NULL)
- return GIT_ENOMEM;;
-
+ GITERR_CHECK_ALLOC(*port);
delim = colon == NULL ? slash : colon;
*host = git__strndup(url, delim - url);
- if (*host == NULL) {
- git__free(*port);
- error = GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(*host);
- return error;
+ return 0;
}
diff --git a/src/netops.h b/src/netops.h
index 01ad9714f..f370019ff 100644
--- a/src/netops.h
+++ b/src/netops.h
@@ -7,11 +7,7 @@
#ifndef INCLUDE_netops_h__
#define INCLUDE_netops_h__
-#ifndef GIT_WIN32
-typedef int GIT_SOCKET;
-#else
-typedef SOCKET GIT_SOCKET;
-#endif
+#include "posix.h"
typedef struct gitno_buffer {
char *data;
@@ -20,12 +16,12 @@ typedef struct gitno_buffer {
GIT_SOCKET fd;
} gitno_buffer;
-void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, int fd);
+void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd);
int gitno_recv(gitno_buffer *buf);
void gitno_consume(gitno_buffer *buf, const char *ptr);
void gitno_consume_n(gitno_buffer *buf, size_t cons);
-int gitno_connect(const char *host, const char *port);
+GIT_SOCKET gitno_connect(const char *host, const char *port);
int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags);
int gitno_close(GIT_SOCKET s);
int gitno_send_chunk_size(int s, size_t len);
diff --git a/src/notes.c b/src/notes.c
index 68554c36f..05c70c643 100644
--- a/src/notes.c
+++ b/src/notes.c
@@ -21,11 +21,8 @@ static int find_subtree(git_tree **subtree, const git_oid *root,
*subtree = NULL;
error = git_tree_lookup(&tree, repo, root);
- if (error < GIT_SUCCESS) {
- if (error == GIT_ENOTFOUND)
- return error; /* notes tree doesn't exist yet */
- return git__rethrow(error, "Failed to open notes tree");
- }
+ if (error < 0)
+ return error;
for (i=0; i<git_tree_entrycount(tree); i++) {
entry = git_tree_entry_byindex(tree, i);
@@ -56,7 +53,7 @@ static int find_subtree(git_tree **subtree, const git_oid *root,
}
*subtree = tree;
- return GIT_SUCCESS;
+ return 0;
}
static int find_blob(git_oid *blob, git_tree *tree, const char *target)
@@ -71,7 +68,7 @@ static int find_blob(git_oid *blob, git_tree *tree, const char *target)
/* found matching note object - return */
git_oid_cpy(blob, git_tree_entry_id(entry));
- return GIT_SUCCESS;
+ return 0;
}
}
return GIT_ENOTFOUND;
@@ -93,18 +90,17 @@ static int note_write(git_oid *out, git_repository *repo,
if (tree_sha) {
error = find_subtree(&tree, tree_sha, repo, target, &fanout);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup subtree");
+ if (error < 0)
+ return error;
error = find_blob(&oid, tree, target + fanout);
- if (error < GIT_SUCCESS && error != GIT_ENOTFOUND) {
- git_tree_free(tree);
- return git__throw(GIT_ENOTFOUND, "Failed to read subtree %s", target);
- }
-
- if (error == GIT_SUCCESS) {
+ if (error != GIT_ENOTFOUND) {
git_tree_free(tree);
- return git__throw(GIT_EEXISTS, "Note for `%s` exists already", target);
+ if (!error) {
+ giterr_set(GITERR_REPOSITORY, "Note for '%s' exists already", target);
+ error = GIT_EEXISTS;
+ }
+ return error;
}
}
@@ -113,8 +109,8 @@ static int note_write(git_oid *out, git_repository *repo,
error = git_treebuilder_create(&tb, tree);
git_tree_free(tree);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create treebuilder");
+ if (error < 0)
+ return error;
if (!tree_sha)
/* no notes tree yet - create fanout */
@@ -122,18 +118,18 @@ static int note_write(git_oid *out, git_repository *repo,
/* create note object */
error = git_blob_create_frombuffer(&oid, repo, note, strlen(note));
- if (error < GIT_SUCCESS) {
+ if (error < 0) {
git_treebuilder_free(tb);
- return git__rethrow(error, "Failed to create note object");
+ return error;
}
error = git_treebuilder_insert(&entry, tb, target + fanout, &oid, 0100644);
- if (error < GIT_SUCCESS) {
+ if (error < 0) {
/* libgit2 doesn't support object removal (gc) yet */
/* we leave an orphaned blob object behind - TODO */
git_treebuilder_free(tb);
- return git__rethrow(error, "Failed to insert note object");
+ return error;
}
if (out)
@@ -142,8 +138,8 @@ static int note_write(git_oid *out, git_repository *repo,
error = git_treebuilder_write(&oid, repo, tb);
git_treebuilder_free(tb);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to write notes tree");
+ if (error < 0)
+ return 0;
if (!tree_sha) {
/* create fanout subtree */
@@ -153,27 +149,28 @@ static int note_write(git_oid *out, git_repository *repo,
subtree[2] = '\0';
error = git_treebuilder_create(&tb, NULL);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create treebuilder");
+ if (error < 0)
+ return error;
error = git_treebuilder_insert(NULL, tb, subtree, &oid, 0040000);
- if (error < GIT_SUCCESS) {
+ if (error < 0) {
git_treebuilder_free(tb);
- return git__rethrow(error, "Failed to insert note object");
+ return error;
}
error = git_treebuilder_write(&oid, repo, tb);
+
git_treebuilder_free(tb);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to write notes tree");
+ if (error < 0)
+ return error;
}
/* create new notes commit */
error = git_tree_lookup(&tree, repo, &oid);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to open new notes tree");
+ if (error < 0)
+ return error;
error = git_commit_create(&oid, repo, notes_ref, author, committer,
NULL, GIT_NOTES_DEFAULT_MSG_ADD,
@@ -181,10 +178,7 @@ static int note_write(git_oid *out, git_repository *repo,
git_tree_free(tree);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create new notes commit");
-
- return GIT_SUCCESS;
+ return error;
}
static int note_lookup(git_note **out, git_repository *repo,
@@ -197,31 +191,25 @@ static int note_lookup(git_note **out, git_repository *repo,
git_note *note;
error = find_subtree(&tree, tree_sha, repo, target, &fanout);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup subtree");
+ if (error < 0)
+ return error;
error = find_blob(&oid, tree, target + fanout);
- if (error < GIT_SUCCESS) {
- git_tree_free(tree);
- return git__throw(GIT_ENOTFOUND, "No note found for object %s",
- target);
- }
+
git_tree_free(tree);
+ if (error < 0)
+ return error;
error = git_blob_lookup(&blob, repo, &oid);
- if (error < GIT_SUCCESS)
- return git__throw(GIT_ERROR, "Failed to lookup note object");
+ if (error < 0)
+ return error;
note = git__malloc(sizeof(git_note));
- if (note == NULL) {
- git_blob_free(blob);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(note);
git_oid_cpy(&note->oid, &oid);
note->message = git__strdup(git_blob_rawcontent(blob));
- if (note->message == NULL)
- error = GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(note->message);
*out = note;
@@ -240,39 +228,30 @@ static int note_remove(git_repository *repo,
git_treebuilder *tb;
error = find_subtree(&tree, tree_sha, repo, target, &fanout);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup subtree");
+ if (error < 0)
+ return error;
error = find_blob(&oid, tree, target + fanout);
- if (error < GIT_SUCCESS) {
- git_tree_free(tree);
- return git__throw(GIT_ENOTFOUND, "No note found for object %s",
- target);
- }
+ if (!error)
+ error = git_treebuilder_create(&tb, tree);
- error = git_treebuilder_create(&tb, tree);
git_tree_free(tree);
-
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create treebuilder");
+ if (error < 0)
+ return error;
error = git_treebuilder_remove(tb, target + fanout);
- if (error < GIT_SUCCESS) {
- git_treebuilder_free(tb);
- return git__rethrow(error, "Failed to remove entry from notes tree");
- }
+ if (!error)
+ error = git_treebuilder_write(&oid, repo, tb);
- error = git_treebuilder_write(&oid, repo, tb);
git_treebuilder_free(tb);
-
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to write notes tree");
+ if (error < 0)
+ return error;
/* create new notes commit */
error = git_tree_lookup(&tree, repo, &oid);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to open new notes tree");
+ if (error < 0)
+ return error;
error = git_commit_create(&oid, repo, notes_ref, author, committer,
NULL, GIT_NOTES_DEFAULT_MSG_RM,
@@ -280,9 +259,6 @@ static int note_remove(git_repository *repo,
git_tree_free(tree);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create new notes commit");
-
return error;
}
@@ -301,8 +277,8 @@ int git_note_read(git_note **out, git_repository *repo,
notes_ref = GIT_NOTES_DEFAULT_REF;
error = git_reference_lookup(&ref, repo, notes_ref);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup reference `%s`", notes_ref);
+ if (error < 0)
+ return error;
assert(git_reference_type(ref) == GIT_REF_OID);
@@ -311,27 +287,26 @@ int git_note_read(git_note **out, git_repository *repo,
git_reference_free(ref);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to find notes commit object");
+ if (error < 0)
+ return error;
sha = git_commit_tree_oid(commit);
git_commit_free(commit);
target = git_oid_allocfmt(oid);
- if (target == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(target);
error = note_lookup(out, repo, sha, target);
git__free(target);
- return error == GIT_SUCCESS ? GIT_SUCCESS :
- git__rethrow(error, "Failed to read note");
+ return error;
}
-int git_note_create(git_oid *out, git_repository *repo,
- git_signature *author, git_signature *committer,
- const char *notes_ref, const git_oid *oid,
- const char *note)
+int git_note_create(
+ git_oid *out, git_repository *repo,
+ git_signature *author, git_signature *committer,
+ const char *notes_ref, const git_oid *oid,
+ const char *note)
{
int error, nparents = 0;
char *target;
@@ -343,10 +318,10 @@ int git_note_create(git_oid *out, git_repository *repo,
notes_ref = GIT_NOTES_DEFAULT_REF;
error = git_reference_lookup(&ref, repo, notes_ref);
- if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
- return git__rethrow(error, "Failed to lookup reference `%s`", notes_ref);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return error;
- if (error == GIT_SUCCESS) {
+ if (!error) {
assert(git_reference_type(ref) == GIT_REF_OID);
/* lookup existing notes tree oid */
@@ -355,16 +330,15 @@ int git_note_create(git_oid *out, git_repository *repo,
git_reference_free(ref);
error = git_commit_lookup(&commit, repo, &sha);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to find notes commit object");
+ if (error < 0)
+ return error;
git_oid_cpy(&sha, git_commit_tree_oid(commit));
nparents++;
}
target = git_oid_allocfmt(oid);
- if (target == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(target);
error = note_write(out, repo, author, committer, notes_ref,
note, nparents ? &sha : NULL, target,
@@ -372,8 +346,7 @@ int git_note_create(git_oid *out, git_repository *repo,
git__free(target);
git_commit_free(commit);
- return error == GIT_SUCCESS ? GIT_SUCCESS :
- git__rethrow(error, "Failed to write note");
+ return error;
}
int git_note_remove(git_repository *repo, const char *notes_ref,
@@ -390,8 +363,8 @@ int git_note_remove(git_repository *repo, const char *notes_ref,
notes_ref = GIT_NOTES_DEFAULT_REF;
error = git_reference_lookup(&ref, repo, notes_ref);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup reference `%s`", notes_ref);
+ if (error < 0)
+ return error;
assert(git_reference_type(ref) == GIT_REF_OID);
@@ -399,22 +372,20 @@ int git_note_remove(git_repository *repo, const char *notes_ref,
git_reference_free(ref);
error = git_commit_lookup(&commit, repo, &sha);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to find notes commit object");
+ if (error < 0)
+ return error;
git_oid_cpy(&sha, git_commit_tree_oid(commit));
target = git_oid_allocfmt(oid);
- if (target == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(target);
error = note_remove(repo, author, committer, notes_ref,
&sha, target, 1, &commit);
git__free(target);
git_commit_free(commit);
- return error == GIT_SUCCESS ? GIT_SUCCESS :
- git__rethrow(error, "Failed to read note");
+ return error;
}
const char * git_note_message(git_note *note)
diff --git a/src/object.c b/src/object.c
index 043001599..979fb40ca 100644
--- a/src/object.c
+++ b/src/object.c
@@ -68,7 +68,8 @@ static int create_object(git_object **object_out, git_otype type)
break;
default:
- return git__throw(GIT_EINVALIDTYPE, "The given type is invalid");
+ giterr_set(GITERR_INVALID, "The given type is invalid");
+ return -1;
}
object->type = type;
@@ -92,8 +93,7 @@ int git_object_lookup_prefix(
assert(repo && object_out && id);
if (len < GIT_OID_MINPREFIXLEN)
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX,
- "Failed to lookup object. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+ return GIT_EAMBIGUOUS;
error = git_repository_odb__weakptr(&odb, repo);
if (error < GIT_SUCCESS)
@@ -110,13 +110,12 @@ int git_object_lookup_prefix(
if (object != NULL) {
if (type != GIT_OBJ_ANY && type != object->type) {
git_object_free(object);
- return git__throw(GIT_EINVALIDTYPE,
- "Failed to lookup object. "
- "The given type does not match the type on the ODB");
+ giterr_set(GITERR_INVALID, "The given type does not match the type in ODB");
+ return -1;
}
*object_out = object;
- return GIT_SUCCESS;
+ return 0;
}
/* Object was not found in the cache, let's explore the backends.
@@ -147,18 +146,19 @@ int git_object_lookup_prefix(
error = git_odb_read_prefix(&odb_obj, odb, &short_oid, len);
}
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup object");
+ if (error < 0)
+ return -1;
if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) {
git_odb_object_free(odb_obj);
- return git__throw(GIT_EINVALIDTYPE, "Failed to lookup object. The given type does not match the type on the ODB");
+ giterr_set(GITERR_INVALID, "The given type does not match the type on the ODB");
+ return -1;
}
type = odb_obj->raw.type;
- if ((error = create_object(&object, type)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup object");
+ if (create_object(&object, type) < 0)
+ return -1;
/* Initialize parent object */
git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
@@ -187,13 +187,13 @@ int git_object_lookup_prefix(
git_odb_object_free(odb_obj);
- if (error < GIT_SUCCESS) {
+ if (error < 0) {
git_object__free(object);
- return git__rethrow(error, "Failed to lookup object");
+ return -1;
}
*object_out = git_cache_try_store(&repo->objects, object);
- return GIT_SUCCESS;
+ return 0;
}
int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) {
diff --git a/src/odb.c b/src/odb.c
index 81fc82ba8..2538b8a77 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -32,10 +32,7 @@ static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype o
{
const char *type_str = git_object_type2string(obj_type);
int len = p_snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);
-
- if (len < 0 || len >= (int)n)
- return git__throw(GIT_ERROR, "Cannot format object header. Length is out of bounds");
-
+ assert(len > 0 && len <= (int)n);
return len+1;
}
@@ -48,13 +45,11 @@ int git_odb__hashobj(git_oid *id, git_rawobj *obj)
assert(id && obj);
if (!git_object_typeisloose(obj->type))
- return git__throw(GIT_ERROR, "Failed to hash object. Wrong object type");
-
+ return -1;
if (!obj->data && obj->len != 0)
- return git__throw(GIT_ERROR, "Failed to hash object. No data given");
+ return -1;
- if ((hdrlen = format_object_header(header, sizeof(header), obj->len, obj->type)) < 0)
- return git__rethrow(hdrlen, "Failed to hash object");
+ hdrlen = format_object_header(header, sizeof(header), obj->len, obj->type);
vec[0].data = header;
vec[0].len = hdrlen;
@@ -63,7 +58,7 @@ int git_odb__hashobj(git_oid *id, git_rawobj *obj)
git_hash_vec(id, vec, 2);
- return GIT_SUCCESS;
+ return 0;
}
@@ -120,8 +115,6 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
git_hash_ctx *ctx;
hdr_len = format_object_header(hdr, sizeof(hdr), size, type);
- if (hdr_len < 0)
- return git__throw(GIT_ERROR, "Failed to format blob header. Length is out of bounds");
ctx = git_hash_new_ctx();
@@ -132,7 +125,8 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
if (read_len < 0) {
git_hash_free_ctx(ctx);
- return git__throw(GIT_EOSERR, "Error when reading file: %s", strerror(errno));
+ giterr_set(GITERR_OS, "Error reading file");
+ return -1;
}
git_hash_update(ctx, buffer, read_len);
@@ -142,69 +136,67 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
git_hash_final(out, ctx);
git_hash_free_ctx(ctx);
- return GIT_SUCCESS;
+ return 0;
}
int git_odb__hashlink(git_oid *out, const char *path)
{
struct stat st;
- int error;
git_off_t size;
+ int result;
- error = p_lstat(path, &st);
- if (error < 0)
- return git__throw(GIT_EOSERR, "Failed to stat blob. %s", strerror(errno));
+ if (git_path_lstat(path, &st) < 0)
+ return -1;
size = st.st_size;
- if (!git__is_sizet(size))
- return git__throw(GIT_EOSERR, "File size overflow for 32-bit systems");
+ if (!git__is_sizet(size)) {
+ giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
+ return -1;
+ }
if (S_ISLNK(st.st_mode)) {
char *link_data;
ssize_t read_len;
link_data = git__malloc((size_t)size);
- if (link_data == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(link_data);
read_len = p_readlink(path, link_data, (size_t)(size + 1));
- if (read_len != (ssize_t)size)
- return git__throw(GIT_EOSERR, "Failed to read symlink data");
+ if (read_len != (ssize_t)size) {
+ giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);
+ return -1;
+ }
- error = git_odb_hash(out, link_data, (size_t)size, GIT_OBJ_BLOB);
- free(link_data);
+ result = git_odb_hash(out, link_data, (size_t)size, GIT_OBJ_BLOB);
+ git__free(link_data);
} else {
- int fd;
-
- if ((fd = p_open(path, O_RDONLY)) < 0)
- return git__throw(GIT_ENOTFOUND, "Could not open '%s'", path);
-
- error = git_odb__hashfd(out, fd, (size_t)size, GIT_OBJ_BLOB);
+ int fd = git_futils_open_ro(path);
+ if (fd < 0)
+ return -1;
+ result = git_odb__hashfd(out, fd, (size_t)size, GIT_OBJ_BLOB);
p_close(fd);
}
- return error;
+ return result;
}
int git_odb_hashfile(git_oid *out, const char *path, git_otype type)
{
- int fd, error;
git_off_t size;
-
- if ((fd = p_open(path, O_RDONLY)) < 0)
- return git__throw(GIT_ENOTFOUND, "Could not open '%s'", path);
+ int result, fd = git_futils_open_ro(path);
+ if (fd < 0)
+ return fd;
if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) {
+ giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
p_close(fd);
- return git__throw(GIT_EOSERR,
- "File size overflow. The object is too big to fit in 32-bit mode");
+ return -1;
}
- error = git_odb__hashfd(out, fd, (size_t)size, type);
-
+ result = git_odb__hashfd(out, fd, (size_t)size, type);
p_close(fd);
- return error;
+ return result;
}
int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type)
@@ -242,11 +234,11 @@ static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t
fake_wstream *stream = (fake_wstream *)_stream;
if (stream->written + len > stream->size)
- return GIT_ENOMEM;
+ return -1;
memcpy(stream->buffer + stream->written, data, len);
stream->written += len;
- return GIT_SUCCESS;
+ return 0;
}
static void fake_wstream__free(git_odb_stream *_stream)
@@ -262,15 +254,14 @@ static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend
fake_wstream *stream;
stream = git__calloc(1, sizeof(fake_wstream));
- if (stream == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(stream);
stream->size = size;
stream->type = type;
stream->buffer = git__malloc(size);
if (stream->buffer == NULL) {
git__free(stream);
- return GIT_ENOMEM;
+ return -1;
}
stream->stream.backend = backend;
@@ -281,7 +272,7 @@ static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend
stream->stream.mode = GIT_STREAM_WRONLY;
*stream_p = (git_odb_stream *)stream;
- return GIT_SUCCESS;
+ return 0;
}
/***********************************************************
@@ -305,26 +296,19 @@ static int backend_sort_cmp(const void *a, const void *b)
int git_odb_new(git_odb **out)
{
- int error;
-
git_odb *db = git__calloc(1, sizeof(*db));
- if (!db)
- return GIT_ENOMEM;
-
- error = git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object);
- if (error < GIT_SUCCESS) {
- git__free(db);
- return git__rethrow(error, "Failed to create object database");
- }
+ GITERR_CHECK_ALLOC(db);
- if ((error = git_vector_init(&db->backends, 4, backend_sort_cmp)) < GIT_SUCCESS) {
+ if (git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object) < 0 ||
+ git_vector_init(&db->backends, 4, backend_sort_cmp) < 0)
+ {
git__free(db);
- return git__rethrow(error, "Failed to create object database");
+ return -1;
}
*out = db;
GIT_REFCOUNT_INC(db);
- return GIT_SUCCESS;
+ return 0;
}
static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int priority, int is_alternate)
@@ -333,12 +317,11 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio
assert(odb && backend);
- if (backend->odb != NULL && backend->odb != odb)
- return git__throw(GIT_EBUSY, "The backend is already owned by another ODB");
+ /* Check if the backend is already owned by another ODB */
+ assert(!backend->odb || backend->odb == odb);
internal = git__malloc(sizeof(backend_internal));
- if (internal == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(internal);
internal->backend = backend;
internal->priority = priority;
@@ -346,12 +329,12 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio
if (git_vector_insert(&odb->backends, internal) < 0) {
git__free(internal);
- return GIT_ENOMEM;
+ return -1;
}
git_vector_sort(&odb->backends);
internal->backend->odb = odb;
- return GIT_SUCCESS;
+ return 0;
}
int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority)
@@ -367,27 +350,18 @@ int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority)
static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates)
{
git_odb_backend *loose, *packed;
- int error;
/* add the loose object backend */
- error = git_odb_backend_loose(&loose, objects_dir, -1, 0);
- if (error < GIT_SUCCESS)
- return error;
-
- error = add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to add backend");
+ if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 ||
+ add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates) < 0)
+ return -1;
/* add the packed file backend */
- error = git_odb_backend_pack(&packed, objects_dir);
- if (error < GIT_SUCCESS)
- return error;
-
- error = add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to add backend");
+ if (git_odb_backend_pack(&packed, objects_dir) < 0 ||
+ add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates) < 0)
+ return -1;
- return GIT_SUCCESS;
+ return 0;
}
static int load_alternates(git_odb *odb, const char *objects_dir)
@@ -396,24 +370,22 @@ static int load_alternates(git_odb *odb, const char *objects_dir)
git_buf alternates_buf = GIT_BUF_INIT;
char *buffer;
const char *alternate;
- int error;
+ int result = 0;
- error = git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0)
+ return -1;
- if (git_path_exists(alternates_path.ptr) < GIT_SUCCESS) {
+ if (git_path_exists(alternates_path.ptr) == false) {
git_buf_free(&alternates_path);
- return GIT_SUCCESS;
+ return 0;
}
- if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < GIT_SUCCESS) {
+ if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < 0) {
git_buf_free(&alternates_path);
- return git__throw(GIT_EOSERR, "Failed to add backend. Can't read alternates");
+ return -1;
}
buffer = (char *)alternates_buf.ptr;
- error = GIT_SUCCESS;
/* add each alternate as a new backend; one alternate per line */
while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) {
@@ -422,48 +394,41 @@ static int load_alternates(git_odb *odb, const char *objects_dir)
/* relative path: build based on the current `objects` folder */
if (*alternate == '.') {
- error = git_buf_joinpath(&alternates_path, objects_dir, alternate);
- if (error < GIT_SUCCESS)
+ if ((result = git_buf_joinpath(&alternates_path, objects_dir, alternate)) < 0)
break;
alternate = git_buf_cstr(&alternates_path);
}
- if ((error = add_default_backends(odb, alternate, 1)) < GIT_SUCCESS)
+ if ((result = add_default_backends(odb, alternate, 1)) < 0)
break;
}
git_buf_free(&alternates_path);
git_buf_free(&alternates_buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to load alternates");
- return error;
+ return result;
}
int git_odb_open(git_odb **out, const char *objects_dir)
{
git_odb *db;
- int error;
assert(out && objects_dir);
*out = NULL;
- if ((error = git_odb_new(&db)) < 0)
- return git__rethrow(error, "Failed to open ODB");
-
- if ((error = add_default_backends(db, objects_dir, 0)) < GIT_SUCCESS)
- goto cleanup;
+ if (git_odb_new(&db) < 0)
+ return -1;
- if ((error = load_alternates(db, objects_dir)) < GIT_SUCCESS)
- goto cleanup;
+ if (add_default_backends(db, objects_dir, 0) < 0 ||
+ load_alternates(db, objects_dir) < 0)
+ {
+ git_odb_free(db);
+ return -1;
+ }
*out = db;
- return GIT_SUCCESS;
-
-cleanup:
- git_odb_free(db);
- return error; /* error already set - pass through */
+ return 0;
}
static void odb_free(git_odb *db)
@@ -497,13 +462,13 @@ int git_odb_exists(git_odb *db, const git_oid *id)
{
git_odb_object *object;
unsigned int i;
- int found = 0;
+ bool found = false;
assert(db && id);
if ((object = git_cache_get(&db->cache, id)) != NULL) {
git_odb_object_free(object);
- return 1;
+ return (int)true;
}
for (i = 0; i < db->backends.length && !found; ++i) {
@@ -514,7 +479,7 @@ int git_odb_exists(git_odb *db, const git_oid *id)
found = b->exists(b, id);
}
- return found;
+ return (int)found;
}
int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
@@ -529,7 +494,7 @@ int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git
*len_p = object->raw.len;
*type_p = object->raw.type;
git_odb_object_free(object);
- return GIT_SUCCESS;
+ return 0;
}
for (i = 0; i < db->backends.length && error < 0; ++i) {
@@ -540,23 +505,20 @@ int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git
error = b->read_header(len_p, type_p, b, id);
}
- if (error == GIT_EPASSTHROUGH)
- return GIT_SUCCESS;
+ if (!error || error == GIT_EPASSTHROUGH)
+ return 0;
/*
* no backend could read only the header.
* try reading the whole object and freeing the contents
*/
- if (error < 0) {
- if ((error = git_odb_read(&object, db, id)) < GIT_SUCCESS)
- return error; /* error already set - pass through */
+ if ((error = git_odb_read(&object, db, id)) < 0)
+ return error; /* error already set - pass along */
- *len_p = object->raw.len;
- *type_p = object->raw.type;
- git_odb_object_free(object);
- }
-
- return GIT_SUCCESS;
+ *len_p = object->raw.len;
+ *type_p = object->raw.type;
+ git_odb_object_free(object);
+ return 0;
}
int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
@@ -569,7 +531,7 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
*out = git_cache_get(&db->cache, id);
if (*out != NULL)
- return GIT_SUCCESS;
+ return 0;
for (i = 0; i < db->backends.length && error < 0; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
@@ -579,15 +541,19 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
error = b->read(&raw.data, &raw.len, &raw.type, b, id);
}
- if (error == GIT_EPASSTHROUGH || error == GIT_SUCCESS) {
- *out = git_cache_try_store(&db->cache, new_odb_object(id, &raw));
- return GIT_SUCCESS;
- }
+ /* TODO: If no backends are configured, this returns GIT_ENOTFOUND but
+ * will never have called giterr_set().
+ */
- return git__rethrow(error, "Failed to read object");
+ if (error && error != GIT_EPASSTHROUGH)
+ return error;
+
+ *out = git_cache_try_store(&db->cache, new_odb_object(id, &raw));
+ return 0;
}
-int git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len)
+int git_odb_read_prefix(
+ git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len)
{
unsigned int i;
int error = GIT_ENOTFOUND;
@@ -598,7 +564,7 @@ int git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_
assert(out && db);
if (len < GIT_OID_MINPREFIXLEN)
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to lookup object. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+ return git_odb__error_ambiguous("prefix length too short");
if (len > GIT_OID_HEXSZ)
len = GIT_OID_HEXSZ;
@@ -606,7 +572,7 @@ int git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_
if (len == GIT_OID_HEXSZ) {
*out = git_cache_get(&db->cache, short_id);
if (*out != NULL)
- return GIT_SUCCESS;
+ return 0;
}
for (i = 0; i < db->backends.length && found < 2; ++i) {
@@ -615,33 +581,24 @@ int git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_
if (b->read != NULL) {
error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, short_id, len);
- switch (error) {
- case GIT_SUCCESS:
+ if (!error)
found++;
- break;
- case GIT_ENOTFOUND:
- case GIT_EPASSTHROUGH:
- break;
- case GIT_EAMBIGUOUSOIDPREFIX:
- return git__rethrow(error, "Failed to read object. Ambiguous sha1 prefix");
- default:
- return git__rethrow(error, "Failed to read object");
- }
+ else if (error != GIT_ENOTFOUND && error != GIT_EPASSTHROUGH)
+ return error;
}
}
- if (found == 1) {
- *out = git_cache_try_store(&db->cache, new_odb_object(&full_oid, &raw));
- } else if (found > 1) {
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read object. Ambiguous sha1 prefix");
- } else {
- return git__throw(GIT_ENOTFOUND, "Failed to read object. Object not found");
- }
+ if (found == 0)
+ return git_odb__error_notfound("no match for prefix");
+ if (found > 1)
+ return git_odb__error_ambiguous("multiple matches for prefix");
- return GIT_SUCCESS;
+ *out = git_cache_try_store(&db->cache, new_odb_object(&full_oid, &raw));
+ return 0;
}
-int git_odb_write(git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type)
+int git_odb_write(
+ git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type)
{
unsigned int i;
int error = GIT_ERROR;
@@ -661,24 +618,25 @@ int git_odb_write(git_oid *oid, git_odb *db, const void *data, size_t len, git_o
error = b->write(oid, b, data, len, type);
}
- if (error == GIT_EPASSTHROUGH || error == GIT_SUCCESS)
- return GIT_SUCCESS;
+ if (!error || error == GIT_EPASSTHROUGH)
+ return 0;
/* if no backends were able to write the object directly, we try a streaming
* write to the backends; just write the whole object into the stream in one
* push */
- if ((error = git_odb_open_wstream(&stream, db, len, type)) == GIT_SUCCESS) {
- stream->write(stream, data, len);
- error = stream->finalize_write(oid, stream);
- stream->free(stream);
- return GIT_SUCCESS;
- }
+ if ((error = git_odb_open_wstream(&stream, db, len, type)) != 0)
+ return error;
+
+ stream->write(stream, data, len);
+ error = stream->finalize_write(oid, stream);
+ stream->free(stream);
- return git__rethrow(error, "Failed to write object");
+ return error;
}
-int git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_otype type)
+int git_odb_open_wstream(
+ git_odb_stream **stream, git_odb *db, size_t size, git_otype type)
{
unsigned int i;
int error = GIT_ERROR;
@@ -699,10 +657,10 @@ int git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_
error = init_fake_wstream(stream, b, size, type);
}
- if (error == GIT_EPASSTHROUGH || error == GIT_SUCCESS)
- return GIT_SUCCESS;
+ if (error == GIT_EPASSTHROUGH)
+ error = 0;
- return git__rethrow(error, "Failed to open write stream");
+ return error;
}
int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid)
@@ -720,9 +678,21 @@ int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oi
error = b->readstream(stream, b, oid);
}
- if (error == GIT_EPASSTHROUGH || error == GIT_SUCCESS)
- return GIT_SUCCESS;
+ if (error == GIT_EPASSTHROUGH)
+ error = 0;
- return git__rethrow(error, "Failed to open read stream");
+ return error;
+}
+
+int git_odb__error_notfound(const char *message)
+{
+ giterr_set(GITERR_ODB, "Object not found - %s", message);
+ return GIT_ENOTFOUND;
+}
+
+int git_odb__error_ambiguous(const char *message)
+{
+ giterr_set(GITERR_ODB, "Ambiguous SHA1 prefix - %s", message);
+ return GIT_EAMBIGUOUS;
}
diff --git a/src/odb.h b/src/odb.h
index 2f84ebea4..4c425c007 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -67,4 +67,14 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type);
*/
int git_odb__hashlink(git_oid *out, const char *path);
+/*
+ * Generate a GIT_ENOTFOUND error for the ODB.
+ */
+int git_odb__error_notfound(const char *message);
+
+/*
+ * Generate a GIT_EAMBIGUOUS error for the ODB.
+ */
+int git_odb__error_ambiguous(const char *message);
+
#endif
diff --git a/src/odb_loose.c b/src/odb_loose.c
index f5f6e35ac..d028deca5 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -61,17 +61,17 @@ static int object_file_name(git_buf *name, const char *dir, const git_oid *id)
git_buf_sets(name, dir);
/* expand length for 40 hex sha1 chars + 2 * '/' + '\0' */
- if (git_buf_grow(name, name->size + GIT_OID_HEXSZ + 3) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (git_buf_grow(name, git_buf_len(name) + GIT_OID_HEXSZ + 3) < 0)
+ return -1;
git_path_to_dir(name);
/* loose object filename: aa/aaa... (41 bytes) */
- git_oid_pathfmt(name->ptr + name->size, id);
+ git_oid_pathfmt(name->ptr + git_buf_len(name), id);
name->size += GIT_OID_HEXSZ + 1;
name->ptr[name->size] = '\0';
- return GIT_SUCCESS;
+ return 0;
}
@@ -81,7 +81,7 @@ static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)
unsigned char *data = (unsigned char *)obj->ptr;
size_t shift, size, used = 0;
- if (obj->size == 0)
+ if (git_buf_len(obj) == 0)
return 0;
c = data[used++];
@@ -90,7 +90,7 @@ static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)
size = c & 15;
shift = 4;
while (c & 0x80) {
- if (obj->size <= used)
+ if (git_buf_len(obj) <= used)
return 0;
if (sizeof(size_t) * 8 <= shift)
return 0;
@@ -182,7 +182,7 @@ static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len)
int status;
init_stream(s, out, len);
- set_stream_input(s, obj->ptr, obj->size);
+ set_stream_input(s, obj->ptr, git_buf_len(obj));
if ((status = inflateInit(s)) < Z_OK)
return status;
@@ -199,10 +199,12 @@ static int finish_inflate(z_stream *s)
inflateEnd(s);
- if ((status != Z_STREAM_END) || (s->avail_in != 0))
- return git__throw(GIT_ERROR, "Failed to finish inflation. Stream aborted prematurely");
+ if ((status != Z_STREAM_END) || (s->avail_in != 0)) {
+ giterr_set(GITERR_ZLIB, "Failed to finish ZLib inflation. Stream aborted prematurely");
+ return -1;
+ }
- return GIT_SUCCESS;
+ return 0;
}
static int is_zlib_compressed_data(unsigned char *data)
@@ -226,21 +228,24 @@ static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
zs.next_in = in;
zs.avail_in = (uInt)inlen;
- if (inflateInit(&zs) < Z_OK)
- return git__throw(GIT_ERROR, "Failed to inflate buffer");
+ if (inflateInit(&zs) < Z_OK) {
+ giterr_set(GITERR_ZLIB, "Failed to inflate buffer");
+ return -1;
+ }
while (status == Z_OK)
status = inflate(&zs, Z_FINISH);
inflateEnd(&zs);
- if ((status != Z_STREAM_END) /*|| (zs.avail_in != 0) */)
- return git__throw(GIT_ERROR, "Failed to inflate buffer. Stream aborted prematurely");
-
- if (zs.total_out != outlen)
- return git__throw(GIT_ERROR, "Failed to inflate buffer. Stream aborted prematurely");
+ if (status != Z_STREAM_END /* || zs.avail_in != 0 */ ||
+ zs.total_out != outlen)
+ {
+ giterr_set(GITERR_ZLIB, "Failed to inflate buffer. Stream aborted prematurely");
+ return -1;
+ }
- return GIT_SUCCESS;
+ return 0;
}
static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
@@ -297,24 +302,23 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
* read the object header, which is an (uncompressed)
* binary encoding of the object type and size.
*/
- if ((used = get_binary_object_header(&hdr, obj)) == 0)
- return git__throw(GIT_ERROR, "Failed to inflate loose object. Object has no header");
-
- if (!git_object_typeisloose(hdr.type))
- return git__throw(GIT_ERROR, "Failed to inflate loose object. Wrong object type");
+ if ((used = get_binary_object_header(&hdr, obj)) == 0 ||
+ !git_object_typeisloose(hdr.type)) {
+ giterr_set(GITERR_ODB, "Failed to inflate loose object.");
+ return -1;
+ }
/*
* allocate a buffer and inflate the data into it
*/
buf = git__malloc(hdr.size + 1);
- if (!buf)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(buf);
in = ((unsigned char *)obj->ptr) + used;
len = obj->size - used;
- if (inflate_buffer(in, len, buf, hdr.size)) {
+ if (inflate_buffer(in, len, buf, hdr.size) < 0) {
git__free(buf);
- return git__throw(GIT_ERROR, "Failed to inflate loose object. Could not inflate buffer");
+ return -1;
}
buf[hdr.size] = '\0';
@@ -322,7 +326,7 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
out->len = hdr.size;
out->type = hdr.type;
- return GIT_SUCCESS;
+ return 0;
}
static int inflate_disk_obj(git_rawobj *out, git_buf *obj)
@@ -342,28 +346,27 @@ static int inflate_disk_obj(git_rawobj *out, git_buf *obj)
* inflate the initial part of the io buffer in order
* to parse the object header (type and size).
*/
- if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK)
- return git__throw(GIT_ERROR, "Failed to inflate disk object. Could not inflate buffer");
-
- if ((used = get_object_header(&hdr, head)) == 0)
- return git__throw(GIT_ERROR, "Failed to inflate disk object. Object has no header");
-
- if (!git_object_typeisloose(hdr.type))
- return git__throw(GIT_ERROR, "Failed to inflate disk object. Wrong object type");
+ if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK ||
+ (used = get_object_header(&hdr, head)) == 0 ||
+ !git_object_typeisloose(hdr.type))
+ {
+ giterr_set(GITERR_ODB, "Failed to inflate disk object.");
+ return -1;
+ }
/*
* allocate a buffer and inflate the object data into it
* (including the initial sequence in the head buffer).
*/
if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL)
- return GIT_ENOMEM;
+ return -1;
buf[hdr.size] = '\0';
out->data = buf;
out->len = hdr.size;
out->type = hdr.type;
- return GIT_SUCCESS;
+ return 0;
}
@@ -387,25 +390,24 @@ static int read_loose(git_rawobj *out, git_buf *loc)
assert(out && loc);
- if ((error = git_buf_lasterror(loc)) < GIT_SUCCESS)
- return error;
+ if (git_buf_oom(loc))
+ return -1;
out->data = NULL;
out->len = 0;
out->type = GIT_OBJ_BAD;
- if (git_futils_readbuffer(&obj, loc->ptr) < 0)
- return git__throw(GIT_ENOTFOUND, "Failed to read loose object. File not found");
+ if (!(error = git_futils_readbuffer(&obj, loc->ptr)))
+ error = inflate_disk_obj(out, &obj);
- error = inflate_disk_obj(out, &obj);
git_buf_free(&obj);
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read loose object");
+ return error;
}
static int read_header_loose(git_rawobj *out, git_buf *loc)
{
- int error = GIT_SUCCESS, z_return = Z_ERRNO, read_bytes;
+ int error = 0, z_return = Z_ERRNO, read_bytes;
git_file fd;
z_stream zs;
obj_hdr header_obj;
@@ -413,49 +415,41 @@ static int read_header_loose(git_rawobj *out, git_buf *loc)
assert(out && loc);
- if ((error = git_buf_lasterror(loc)) < GIT_SUCCESS)
- return error;
+ if (git_buf_oom(loc))
+ return -1;
out->data = NULL;
- if ((fd = p_open(loc->ptr, O_RDONLY)) < 0)
- return git__throw(GIT_ENOTFOUND, "Failed to read loose object header. File not found");
+ if ((fd = git_futils_open_ro(loc->ptr)) < 0)
+ return fd;
init_stream(&zs, inflated_buffer, sizeof(inflated_buffer));
- if (inflateInit(&zs) < Z_OK) {
- error = GIT_EZLIB;
- goto cleanup;
- }
+ z_return = inflateInit(&zs);
- do {
- if ((read_bytes = read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
+ while (z_return == Z_OK) {
+ if ((read_bytes = p_read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
set_stream_input(&zs, raw_buffer, read_bytes);
z_return = inflate(&zs, 0);
- } else {
+ } else
z_return = Z_STREAM_END;
- break;
- }
- } while (z_return == Z_OK);
+ }
if ((z_return != Z_STREAM_END && z_return != Z_BUF_ERROR)
|| get_object_header(&header_obj, inflated_buffer) == 0
- || git_object_typeisloose(header_obj.type) == 0) {
- error = GIT_EOBJCORRUPTED;
- goto cleanup;
+ || git_object_typeisloose(header_obj.type) == 0)
+ {
+ giterr_set(GITERR_ZLIB, "Failed to read loose object header");
+ error = -1;
+ } else {
+ out->len = header_obj.size;
+ out->type = header_obj.type;
}
- out->len = header_obj.size;
- out->type = header_obj.type;
-
-cleanup:
finish_inflate(&zs);
p_close(fd);
- if (error < GIT_SUCCESS)
- return git__throw(error, "Failed to read loose object header. Header is corrupted");
-
- return GIT_SUCCESS;
+ return error;
}
static int locate_object(
@@ -465,8 +459,8 @@ static int locate_object(
{
int error = object_file_name(object_location, backend->objects_dir, oid);
- if (error == GIT_SUCCESS)
- error = git_path_exists(git_buf_cstr(object_location));
+ if (!error && !git_path_exists(object_location->ptr))
+ return GIT_ENOTFOUND;
return error;
}
@@ -475,12 +469,12 @@ static int locate_object(
static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {
loose_locate_object_state *sstate = (loose_locate_object_state *)state;
- if (pathbuf->size - sstate->dir_len != GIT_OID_HEXSZ - 2) {
+ if (git_buf_len(pathbuf) - sstate->dir_len != GIT_OID_HEXSZ - 2) {
/* Entry cannot be an object. Continue to next entry */
- return GIT_SUCCESS;
+ return 0;
}
- if (!git_path_exists(pathbuf->ptr) && git_path_isdir(pathbuf->ptr)) {
+ if (git_path_isdir(pathbuf->ptr) == false) {
/* We are already in the directory matching the 2 first hex characters,
* compare the first ncmp characters of the oids */
if (!memcmp(sstate->short_oid + 2,
@@ -495,10 +489,11 @@ static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {
sstate->found++;
}
}
+
if (sstate->found > 1)
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Ambiguous sha1 prefix within loose objects");
+ return git_odb__error_ambiguous("multiple matches in loose objects");
- return GIT_SUCCESS;
+ return 0;
}
/* Locate an object matching a given short oid */
@@ -515,60 +510,56 @@ static int locate_object_short_oid(
int error;
/* prealloc memory for OBJ_DIR/xx/ */
- if ((error = git_buf_grow(object_location, dir_len + 5)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to locate object from short oid");
+ if (git_buf_grow(object_location, dir_len + 5) < 0)
+ return -1;
git_buf_sets(object_location, objects_dir);
git_path_to_dir(object_location);
/* save adjusted position at end of dir so it can be restored later */
- dir_len = object_location->size;
+ dir_len = git_buf_len(object_location);
/* Convert raw oid to hex formatted oid */
git_oid_fmt((char *)state.short_oid, short_oid);
/* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
- error = git_buf_printf(object_location, "%.2s/", state.short_oid);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to locate object from short oid");
+ if (git_buf_printf(object_location, "%.2s/", state.short_oid) < 0)
+ return -1;
/* Check that directory exists */
- if (git_path_exists(object_location->ptr) ||
- git_path_isdir(object_location->ptr))
- return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found");
+ if (git_path_isdir(object_location->ptr) == false)
+ return git_odb__error_notfound("failed to locate from short oid");
- state.dir_len = object_location->size;
+ state.dir_len = git_buf_len(object_location);
state.short_oid_len = len;
state.found = 0;
/* Explore directory to find a unique object matching short_oid */
- error = git_path_direach(object_location, fn_locate_object_short_oid, &state);
+ error = git_path_direach(
+ object_location, fn_locate_object_short_oid, &state);
if (error)
- return git__rethrow(error, "Failed to locate object from short oid");
+ return error;
- if (!state.found) {
- return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found");
- }
+ if (!state.found)
+ return git_odb__error_notfound("failed to locate from short oid");
/* Convert obtained hex formatted oid to raw */
error = git_oid_fromstr(res_oid, (char *)state.res_oid);
- if (error) {
- return git__rethrow(error, "Failed to locate object from short oid");
- }
+ if (error)
+ return error;
/* Update the location according to the oid obtained */
git_buf_truncate(object_location, dir_len);
- error = git_buf_grow(object_location, dir_len + GIT_OID_HEXSZ + 2);
- if (error)
- return git__rethrow(error, "Failed to locate object from short oid");
+ if (git_buf_grow(object_location, dir_len + GIT_OID_HEXSZ + 2) < 0)
+ return -1;
git_oid_pathfmt(object_location->ptr + dir_len, res_oid);
object_location->size += GIT_OID_HEXSZ + 1;
object_location->ptr[object_location->size] = '\0';
- return GIT_SUCCESS;
+ return 0;
}
@@ -591,7 +582,7 @@ static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_
{
git_buf object_path = GIT_BUF_INIT;
git_rawobj raw;
- int error = GIT_SUCCESS;
+ int error;
assert(backend && oid);
@@ -599,8 +590,8 @@ static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_
raw.type = GIT_OBJ_BAD;
if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
- error = git__throw(GIT_ENOTFOUND, "Failed to read loose backend header. Object not found");
- else if ((error = read_header_loose(&raw, &object_path)) == GIT_SUCCESS) {
+ error = git_odb__error_notfound("in loose backend");
+ else if ((error = read_header_loose(&raw, &object_path)) == 0) {
*len_p = raw.len;
*type_p = raw.type;
}
@@ -614,20 +605,17 @@ static int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p
{
git_buf object_path = GIT_BUF_INIT;
git_rawobj raw;
- int error = GIT_SUCCESS;
+ int error = 0;
assert(backend && oid);
if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
- error = git__throw(GIT_ENOTFOUND, "Failed to read loose backend. Object not found");
- else if ((error = read_loose(&raw, &object_path)) == GIT_SUCCESS) {
+ error = git_odb__error_notfound("in loose backend");
+ else if ((error = read_loose(&raw, &object_path)) == 0) {
*buffer_p = raw.data;
*len_p = raw.len;
*type_p = raw.type;
}
- else {
- git__rethrow(error, "Failed to read loose backend");
- }
git_buf_free(&object_path);
@@ -643,16 +631,15 @@ static int loose_backend__read_prefix(
const git_oid *short_oid,
unsigned int len)
{
- int error = GIT_SUCCESS;
+ int error = 0;
if (len < GIT_OID_MINPREFIXLEN)
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read loose "
- "backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+ error = git_odb__error_ambiguous("prefix length too short");
- if (len >= GIT_OID_HEXSZ) {
+ else if (len >= GIT_OID_HEXSZ) {
/* We can fall back to regular read method */
error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
- if (error == GIT_SUCCESS)
+ if (!error)
git_oid_cpy(out_oid, short_oid);
} else {
git_buf object_path = GIT_BUF_INIT;
@@ -661,11 +648,9 @@ static int loose_backend__read_prefix(
assert(backend && short_oid);
if ((error = locate_object_short_oid(&object_path, out_oid,
- (loose_backend *)backend, short_oid, len)) < 0)
- git__rethrow(error, "Failed to read loose backend");
- else if ((error = read_loose(&raw, &object_path)) < GIT_SUCCESS)
- git__rethrow(error, "Failed to read loose backend");
- else {
+ (loose_backend *)backend, short_oid, len)) == 0 &&
+ (error = read_loose(&raw, &object_path)) == 0)
+ {
*buffer_p = raw.data;
*len_p = raw.len;
*type_p = raw.type;
@@ -688,47 +673,33 @@ static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
git_buf_free(&object_path);
- return (error == GIT_SUCCESS);
+ return !error;
}
static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream)
{
loose_writestream *stream = (loose_writestream *)_stream;
loose_backend *backend = (loose_backend *)_stream->backend;
-
- int error;
git_buf final_path = GIT_BUF_INIT;
+ int error = 0;
- if ((error = git_filebuf_hash(oid, &stream->fbuf)) < GIT_SUCCESS)
- goto cleanup;
-
- if ((error = object_file_name(&final_path, backend->objects_dir, oid)) < GIT_SUCCESS)
- goto cleanup;
-
- if ((error = git_buf_lasterror(&final_path)) < GIT_SUCCESS)
- goto cleanup;
-
- if ((error = git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE)) < GIT_SUCCESS)
- goto cleanup;
-
+ if (git_filebuf_hash(oid, &stream->fbuf) < 0 ||
+ object_file_name(&final_path, backend->objects_dir, oid) < 0 ||
+ git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE) < 0)
+ error = -1;
/*
* Don't try to add an existing object to the repository. This
* is what git does and allows us to sidestep the fact that
* we're not allowed to overwrite a read-only file on Windows.
*/
- if (git_path_exists(final_path.ptr) == GIT_SUCCESS) {
+ else if (git_path_exists(final_path.ptr) == true)
git_filebuf_cleanup(&stream->fbuf);
- goto cleanup;
- }
+ else
+ error = git_filebuf_commit_at(
+ &stream->fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE);
- error = git_filebuf_commit_at(&stream->fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE);
-
-cleanup:
git_buf_free(&final_path);
- if (error < GIT_SUCCESS)
- git__rethrow(error, "Failed to write loose backend");
-
return error;
}
@@ -752,22 +723,18 @@ static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype o
int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);
assert(len > 0); /* otherwise snprintf() is broken */
- assert(((size_t) len) < n); /* otherwise the caller is broken! */
+ assert(((size_t)len) < n); /* otherwise the caller is broken! */
- if (len < 0 || ((size_t) len) >= n)
- return git__throw(GIT_ERROR, "Failed to format object header. Length is out of bounds");
return len+1;
}
static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, size_t length, git_otype type)
{
loose_backend *backend;
- loose_writestream *stream;
-
+ loose_writestream *stream = NULL;
char hdr[64];
git_buf tmp_path = GIT_BUF_INIT;
int hdrlen;
- int error;
assert(_backend);
@@ -775,12 +742,9 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_
*stream_out = NULL;
hdrlen = format_object_header(hdr, sizeof(hdr), length, type);
- if (hdrlen < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to create loose backend stream. Object is corrupted");
stream = git__calloc(1, sizeof(loose_writestream));
- if (stream == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(stream);
stream->stream.backend = _backend;
stream->stream.read = NULL; /* read only */
@@ -789,36 +753,26 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_
stream->stream.free = &loose_backend__stream_free;
stream->stream.mode = GIT_STREAM_WRONLY;
- error = git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object");
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = git_filebuf_open(&stream->fbuf, tmp_path.ptr,
- GIT_FILEBUF_HASH_CONTENTS |
- GIT_FILEBUF_TEMPORARY |
- (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT));
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = stream->stream.write((git_odb_stream *)stream, hdr, hdrlen);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
+ if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
+ git_filebuf_open(&stream->fbuf, tmp_path.ptr,
+ GIT_FILEBUF_HASH_CONTENTS |
+ GIT_FILEBUF_TEMPORARY |
+ (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0 ||
+ stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
+ {
+ git_filebuf_cleanup(&stream->fbuf);
+ git__free(stream);
+ stream = NULL;
+ }
git_buf_free(&tmp_path);
-
*stream_out = (git_odb_stream *)stream;
- return GIT_SUCCESS;
-cleanup:
- git_buf_free(&tmp_path);
- git_filebuf_cleanup(&stream->fbuf);
- git__free(stream);
- return git__rethrow(error, "Failed to create loose backend stream");
+ return !stream ? -1 : 0;
}
static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type)
{
- int error, header_len;
+ int error = 0, header_len;
git_buf final_path = GIT_BUF_INIT;
char header[64];
git_filebuf fbuf = GIT_FILEBUF_INIT;
@@ -827,39 +781,29 @@ static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const v
backend = (loose_backend *)_backend;
/* prepare the header for the file */
- {
- header_len = format_object_header(header, sizeof(header), len, type);
- if (header_len < GIT_SUCCESS)
- return GIT_EOBJCORRUPTED;
- }
-
- error = git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object");
- if (error < GIT_SUCCESS)
- goto cleanup;
+ header_len = format_object_header(header, sizeof(header), len, type);
- error = git_filebuf_open(&fbuf, final_path.ptr,
- GIT_FILEBUF_HASH_CONTENTS |
- GIT_FILEBUF_TEMPORARY |
- (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT));
- if (error < GIT_SUCCESS)
+ if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
+ git_filebuf_open(&fbuf, final_path.ptr,
+ GIT_FILEBUF_HASH_CONTENTS |
+ GIT_FILEBUF_TEMPORARY |
+ (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0)
+ {
+ error = -1;
goto cleanup;
+ }
git_filebuf_write(&fbuf, header, header_len);
git_filebuf_write(&fbuf, data, len);
git_filebuf_hash(oid, &fbuf);
- error = object_file_name(&final_path, backend->objects_dir, oid);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = git_filebuf_commit_at(&fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE);
+ if (object_file_name(&final_path, backend->objects_dir, oid) < 0 ||
+ git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE) < 0 ||
+ git_filebuf_commit_at(&fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE) < 0)
+ error = -1;
cleanup:
- if (error < GIT_SUCCESS)
+ if (error < 0)
git_filebuf_cleanup(&fbuf);
git_buf_free(&final_path);
return error;
@@ -884,14 +828,10 @@ int git_odb_backend_loose(
loose_backend *backend;
backend = git__calloc(1, sizeof(loose_backend));
- if (backend == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(backend);
backend->objects_dir = git__strdup(objects_dir);
- if (backend->objects_dir == NULL) {
- git__free(backend);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(backend->objects_dir);
if (compression_level < 0)
compression_level = Z_BEST_SPEED;
@@ -908,5 +848,5 @@ int git_odb_backend_loose(
backend->parent.free = &loose_backend__free;
*backend_out = (git_odb_backend *)backend;
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 249144a3a..242200b4a 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -137,19 +137,19 @@ static int packfile_load__cb(void *_data, git_buf *path);
static int packfile_refresh_all(struct pack_backend *backend);
static int pack_entry_find(struct git_pack_entry *e,
- struct pack_backend *backend, const git_oid *oid);
+ struct pack_backend *backend, const git_oid *oid);
/* Can find the offset of an object given
* a prefix of an identifier.
- * Throws GIT_EAMBIGUOUSOIDPREFIX if short oid
- * is ambiguous.
+ * Sets GIT_EAMBIGUOUS if short oid is ambiguous.
* This method assumes that len is between
* GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
*/
-static int pack_entry_find_prefix(struct git_pack_entry *e,
- struct pack_backend *backend,
- const git_oid *short_oid,
- unsigned int len);
+static int pack_entry_find_prefix(
+ struct git_pack_entry *e,
+ struct pack_backend *backend,
+ const git_oid *short_oid,
+ unsigned int len);
@@ -212,30 +212,25 @@ static int packfile_load__cb(void *_data, git_buf *path)
struct pack_backend *backend = (struct pack_backend *)_data;
struct git_pack_file *pack;
int error;
- size_t i;
+ unsigned int i;
if (git__suffixcmp(path->ptr, ".idx") != 0)
- return GIT_SUCCESS; /* not an index */
+ return 0; /* not an index */
for (i = 0; i < backend->packs.length; ++i) {
struct git_pack_file *p = git_vector_get(&backend->packs, i);
- if (memcmp(p->pack_name, path->ptr, path->size - strlen(".idx")) == 0)
- return GIT_SUCCESS;
+ if (memcmp(p->pack_name, git_buf_cstr(path), git_buf_len(path) - strlen(".idx")) == 0)
+ return 0;
}
error = git_packfile_check(&pack, path->ptr);
- if (error == GIT_ENOTFOUND) {
+ if (error == GIT_ENOTFOUND)
/* ignore missing .pack file as git does */
- return GIT_SUCCESS;
- } else if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to load packfile");
-
- if (git_vector_insert(&backend->packs, pack) < GIT_SUCCESS) {
- git__free(pack);
- return GIT_ENOMEM;
- }
+ return 0;
+ else if (error < 0)
+ return error;
- return GIT_SUCCESS;
+ return git_vector_insert(&backend->packs, pack);
}
static int packfile_refresh_all(struct pack_backend *backend)
@@ -244,10 +239,10 @@ static int packfile_refresh_all(struct pack_backend *backend)
struct stat st;
if (backend->pack_folder == NULL)
- return GIT_SUCCESS;
+ return 0;
if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
- return git__throw(GIT_ENOTFOUND, "Failed to refresh packfiles. Backend not found");
+ return git_odb__error_notfound("failed to refresh packfiles");
if (st.st_mtime != backend->pack_folder_mtime) {
git_buf path = GIT_BUF_INIT;
@@ -257,27 +252,28 @@ static int packfile_refresh_all(struct pack_backend *backend)
error = git_path_direach(&path, packfile_load__cb, (void *)backend);
git_buf_free(&path);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to refresh packfiles");
+
+ if (error < 0)
+ return error;
git_vector_sort(&backend->packs);
backend->pack_folder_mtime = st.st_mtime;
}
- return GIT_SUCCESS;
+ return 0;
}
static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
{
int error;
- size_t i;
+ unsigned int i;
- if ((error = packfile_refresh_all(backend)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to find pack entry");
+ if ((error = packfile_refresh_all(backend)) < 0)
+ return error;
if (backend->last_found &&
- git_pack_entry_find(e, backend->last_found, oid, GIT_OID_HEXSZ) == GIT_SUCCESS)
- return GIT_SUCCESS;
+ git_pack_entry_find(e, backend->last_found, oid, GIT_OID_HEXSZ) == 0)
+ return 0;
for (i = 0; i < backend->packs.length; ++i) {
struct git_pack_file *p;
@@ -286,13 +282,13 @@ static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backen
if (p == backend->last_found)
continue;
- if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == GIT_SUCCESS) {
+ if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == 0) {
backend->last_found = p;
- return GIT_SUCCESS;
+ return 0;
}
}
- return git__throw(GIT_ENOTFOUND, "Failed to find pack entry");
+ return git_odb__error_notfound("failed to find pack entry");
}
static int pack_entry_find_prefix(
@@ -302,19 +298,18 @@ static int pack_entry_find_prefix(
unsigned int len)
{
int error;
- size_t i;
+ unsigned int i;
unsigned found = 0;
- if ((error = packfile_refresh_all(backend)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to find pack entry");
+ if ((error = packfile_refresh_all(backend)) < 0)
+ return error;
if (backend->last_found) {
error = git_pack_entry_find(e, backend->last_found, short_oid, len);
- if (error == GIT_EAMBIGUOUSOIDPREFIX) {
- return git__rethrow(error, "Failed to find pack entry. Ambiguous sha1 prefix");
- } else if (error == GIT_SUCCESS) {
+ if (error == GIT_EAMBIGUOUS)
+ return error;
+ if (!error)
found = 1;
- }
}
for (i = 0; i < backend->packs.length; ++i) {
@@ -325,24 +320,21 @@ static int pack_entry_find_prefix(
continue;
error = git_pack_entry_find(e, p, short_oid, len);
- if (error == GIT_EAMBIGUOUSOIDPREFIX) {
- return git__rethrow(error, "Failed to find pack entry. Ambiguous sha1 prefix");
- } else if (error == GIT_SUCCESS) {
- found++;
- if (found > 1)
+ if (error == GIT_EAMBIGUOUS)
+ return error;
+ if (!error) {
+ if (++found > 1)
break;
backend->last_found = p;
}
}
- if (!found) {
- return git__rethrow(GIT_ENOTFOUND, "Failed to find pack entry");
- } else if (found > 1) {
- return git__rethrow(GIT_EAMBIGUOUSOIDPREFIX, "Failed to find pack entry. Ambiguous sha1 prefix");
- } else {
- return GIT_SUCCESS;
- }
-
+ if (!found)
+ return git_odb__error_notfound("failed to find pack entry");
+ else if (found > 1)
+ return git_odb__error_ambiguous("found multiple pack entries");
+ else
+ return 0;
}
@@ -374,17 +366,15 @@ static int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p,
git_rawobj raw;
int error;
- if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read pack backend");
-
- if ((error = git_packfile_unpack(&raw, e.p, &e.offset)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read pack backend");
+ if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0 ||
+ (error = git_packfile_unpack(&raw, e.p, &e.offset)) < 0)
+ return error;
*buffer_p = raw.data;
*len_p = raw.len;
*type_p = raw.type;
- return GIT_SUCCESS;
+ return 0;
}
static int pack_backend__read_prefix(
@@ -396,46 +386,44 @@ static int pack_backend__read_prefix(
const git_oid *short_oid,
unsigned int len)
{
+ int error = 0;
+
if (len < GIT_OID_MINPREFIXLEN)
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read pack backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+ error = git_odb__error_ambiguous("prefix length too short");
- if (len >= GIT_OID_HEXSZ) {
+ else if (len >= GIT_OID_HEXSZ) {
/* We can fall back to regular read method */
- int error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
- if (error == GIT_SUCCESS)
+ error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
+ if (!error)
git_oid_cpy(out_oid, short_oid);
-
- return error;
} else {
struct git_pack_entry e;
git_rawobj raw;
- int error;
-
- if ((error = pack_entry_find_prefix(&e, (struct pack_backend *)backend, short_oid, len)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read pack backend");
- if ((error = git_packfile_unpack(&raw, e.p, &e.offset)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read pack backend");
-
- *buffer_p = raw.data;
- *len_p = raw.len;
- *type_p = raw.type;
- git_oid_cpy(out_oid, &e.sha1);
+ if ((error = pack_entry_find_prefix(
+ &e, (struct pack_backend *)backend, short_oid, len)) == 0 &&
+ (error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)
+ {
+ *buffer_p = raw.data;
+ *len_p = raw.len;
+ *type_p = raw.type;
+ git_oid_cpy(out_oid, &e.sha1);
+ }
}
- return GIT_SUCCESS;
+ return error;
}
static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
{
struct git_pack_entry e;
- return pack_entry_find(&e, (struct pack_backend *)backend, oid) == GIT_SUCCESS;
+ return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
}
static void pack_backend__free(git_odb_backend *_backend)
{
struct pack_backend *backend;
- size_t i;
+ unsigned int i;
assert(_backend);
@@ -455,21 +443,18 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
{
struct pack_backend *backend = NULL;
git_buf path = GIT_BUF_INIT;
- int error = GIT_SUCCESS;
backend = git__calloc(1, sizeof(struct pack_backend));
- if (backend == NULL)
- return GIT_ENOMEM;
-
- error = git_vector_init(&backend->packs, 8, packfile_sort__cb);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ GITERR_CHECK_ALLOC(backend);
- error = git_buf_joinpath(&path, objects_dir, "pack");
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_vector_init(&backend->packs, 8, packfile_sort__cb) < 0 ||
+ git_buf_joinpath(&path, objects_dir, "pack") < 0)
+ {
+ git__free(backend);
+ return -1;
+ }
- if (git_path_isdir(git_buf_cstr(&path)) == GIT_SUCCESS) {
+ if (git_path_isdir(git_buf_cstr(&path)) == true) {
backend->pack_folder = git_buf_detach(&path);
backend->pack_folder_mtime = 0;
}
@@ -482,10 +467,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
*backend_out = (git_odb_backend *)backend;
-cleanup:
- if (error < GIT_SUCCESS)
- git__free(backend);
git_buf_free(&path);
- return error;
+ return 0;
}
diff --git a/src/oid.c b/src/oid.c
index 34860138d..87756010b 100644
--- a/src/oid.c
+++ b/src/oid.c
@@ -13,13 +13,19 @@
static char to_hex[] = "0123456789abcdef";
+static int oid_error_invalid(const char *msg)
+{
+ giterr_set(GITERR_INVALID, "Unable to parse OID - %s", msg);
+ return -1;
+}
+
int git_oid_fromstrn(git_oid *out, const char *str, size_t length)
{
size_t p;
int v;
if (length < 4)
- return git__throw(GIT_ENOTOID, "Failed to generate sha1. Given string is too short");
+ return oid_error_invalid("input too short");
if (length > GIT_OID_HEXSZ)
length = GIT_OID_HEXSZ;
@@ -29,7 +35,7 @@ int git_oid_fromstrn(git_oid *out, const char *str, size_t length)
| git__fromhex(str[p + 1]);
if (v < 0)
- return git__throw(GIT_ENOTOID, "Failed to generate sha1. Given string is not a valid sha1 hash");
+ return oid_error_invalid("contains invalid characters");
out->id[p / 2] = (unsigned char)v;
}
@@ -37,7 +43,7 @@ int git_oid_fromstrn(git_oid *out, const char *str, size_t length)
if (length % 2) {
v = (git__fromhex(str[p + 0]) << 4);
if (v < 0)
- return git__throw(GIT_ENOTOID, "Failed to generate sha1. Given string is not a valid sha1 hash");
+ return oid_error_invalid("contains invalid characters");
out->id[p / 2] = (unsigned char)v;
p += 2;
@@ -45,7 +51,7 @@ int git_oid_fromstrn(git_oid *out, const char *str, size_t length)
memset(out->id + p / 2, 0, (GIT_OID_HEXSZ - p) / 2);
- return GIT_SUCCESS;
+ return 0;
}
int git_oid_fromstr(git_oid *out, const char *str)
@@ -109,8 +115,9 @@ char *git_oid_tostr(char *out, size_t n, const git_oid *oid)
return out;
}
-int git_oid__parse(git_oid *oid, const char **buffer_out,
- const char *buffer_end, const char *header)
+int git_oid__parse(
+ git_oid *oid, const char **buffer_out,
+ const char *buffer_end, const char *header)
{
const size_t sha_len = GIT_OID_HEXSZ;
const size_t header_len = strlen(header);
@@ -118,20 +125,20 @@ int git_oid__parse(git_oid *oid, const char **buffer_out,
const char *buffer = *buffer_out;
if (buffer + (header_len + sha_len + 1) > buffer_end)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse OID. Buffer too small");
+ return -1;
if (memcmp(buffer, header, header_len) != 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse OID. Buffer and header do not match");
+ return -1;
if (buffer[header_len + sha_len] != '\n')
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse OID. Buffer not terminated correctly");
+ return -1;
- if (git_oid_fromstr(oid, buffer + header_len) < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse OID. Failed to generate sha1");
+ if (git_oid_fromstr(oid, buffer + header_len) < 0)
+ return -1;
*buffer_out = buffer + (header_len + sha_len + 1);
- return GIT_SUCCESS;
+ return 0;
}
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid)
@@ -182,12 +189,11 @@ int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, unsigned int len)
int git_oid_streq(const git_oid *a, const char *str)
{
git_oid id;
- int error;
- if ((error = git_oid_fromstr(&id, str)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to convert '%s' to oid.", str);
+ if (git_oid_fromstr(&id, str) < 0)
+ return -1;
- return git_oid_cmp(a, &id) == 0 ? GIT_SUCCESS : GIT_ERROR;
+ return git_oid_cmp(a, &id) == 0 ? 0 : -1;
}
int git_oid_iszero(const git_oid *oid_a)
@@ -216,15 +222,14 @@ struct git_oid_shorten {
static int resize_trie(git_oid_shorten *self, size_t new_size)
{
self->nodes = git__realloc(self->nodes, new_size * sizeof(trie_node));
- if (self->nodes == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(self->nodes);
if (new_size > self->size) {
memset(&self->nodes[self->size], 0x0, (new_size - self->size) * sizeof(trie_node));
}
self->size = new_size;
- return GIT_SUCCESS;
+ return 0;
}
static trie_node *push_leaf(git_oid_shorten *os, node_index idx, int push_at, const char *oid)
@@ -233,7 +238,7 @@ static trie_node *push_leaf(git_oid_shorten *os, node_index idx, int push_at, co
node_index idx_leaf;
if (os->node_count >= os->size) {
- if (resize_trie(os, os->size * 2) < GIT_SUCCESS)
+ if (resize_trie(os, os->size * 2) < 0)
return NULL;
}
@@ -255,19 +260,19 @@ git_oid_shorten *git_oid_shorten_new(size_t min_length)
{
git_oid_shorten *os;
- os = git__malloc(sizeof(git_oid_shorten));
+ assert((size_t)((int)min_length) == min_length);
+
+ os = git__calloc(1, sizeof(git_oid_shorten));
if (os == NULL)
return NULL;
- memset(os, 0x0, sizeof(git_oid_shorten));
-
- if (resize_trie(os, 16) < GIT_SUCCESS) {
+ if (resize_trie(os, 16) < 0) {
git__free(os);
return NULL;
}
os->node_count = 1;
- os->min_length = min_length;
+ os->min_length = (int)min_length;
return os;
}
@@ -325,24 +330,27 @@ void git_oid_shorten_free(git_oid_shorten *os)
*/
int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid)
{
- int i, is_leaf;
+ int i;
+ bool is_leaf;
node_index idx;
if (os->full)
- return GIT_ENOMEM;
+ return -1;
if (text_oid == NULL)
return os->min_length;
idx = 0;
- is_leaf = 0;
+ is_leaf = false;
for (i = 0; i < GIT_OID_HEXSZ; ++i) {
int c = git__fromhex(text_oid[i]);
trie_node *node;
- if (c == -1)
- return git__throw(GIT_ENOTOID, "Failed to shorten OID. Invalid hex value");
+ if (c == -1) {
+ giterr_set(GITERR_INVALID, "Unable to shorten OID - invalid hex value");
+ return -1;
+ }
node = &os->nodes[idx];
@@ -353,22 +361,21 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid)
node->tail = NULL;
node = push_leaf(os, idx, git__fromhex(tail[0]), &tail[1]);
- if (node == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(node);
}
if (node->children[c] == 0) {
if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL)
- return GIT_ENOMEM;
+ return -1;
break;
}
idx = node->children[c];
- is_leaf = 0;
+ is_leaf = false;
if (idx < 0) {
node->children[c] = idx = -idx;
- is_leaf = 1;
+ is_leaf = true;
}
}
diff --git a/src/oidmap.h b/src/oidmap.h
new file mode 100644
index 000000000..858268c92
--- /dev/null
+++ b/src/oidmap.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 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.
+ */
+#ifndef INCLUDE_oidmap_h__
+#define INCLUDE_oidmap_h__
+
+#include "common.h"
+#include "git2/oid.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kfree git__free
+#include "khash.h"
+
+__KHASH_TYPE(oid, const git_oid *, void *);
+typedef khash_t(oid) git_oidmap;
+
+GIT_INLINE(khint_t) hash_git_oid(const git_oid *oid)
+{
+ int i;
+ khint_t h = 0;
+ for (i = 0; i < 20; ++i)
+ h = (h << 5) - h + oid->id[i];
+ return h;
+}
+
+GIT_INLINE(int) hash_git_oid_equal(const git_oid *a, const git_oid *b)
+{
+ return (memcmp(a->id, b->id, sizeof(a->id)) == 0);
+}
+
+#define GIT__USE_OIDMAP \
+ __KHASH_IMPL(oid, static 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
+
+#endif
diff --git a/src/pack.c b/src/pack.c
index 0d618324b..8d71138a2 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -17,12 +17,12 @@
#include <zlib.h>
static int packfile_open(struct git_pack_file *p);
-static off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n);
+static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n);
int packfile_unpack_compressed(
git_rawobj *obj,
struct git_pack_file *p,
git_mwindow **w_curs,
- off_t *curpos,
+ git_off_t *curpos,
size_t size,
git_otype type);
@@ -34,12 +34,18 @@ int packfile_unpack_compressed(
* GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
*/
static int pack_entry_find_offset(
- off_t *offset_out,
+ git_off_t *offset_out,
git_oid *found_oid,
struct git_pack_file *p,
const git_oid *short_oid,
unsigned int len);
+static int packfile_error(const char *message)
+{
+ giterr_set(GITERR_ODB, "Invalid pack file - %s", message);
+ return -1;
+}
+
/***********************************************************
*
* PACK INDEX METHODS
@@ -58,40 +64,31 @@ static int pack_index_check(const char *path, struct git_pack_file *p)
{
struct git_pack_idx_header *hdr;
uint32_t version, nr, i, *index;
-
void *idx_map;
size_t idx_size;
-
struct stat st;
-
- /* TODO: properly open the file without access time */
- git_file fd = p_open(path, O_RDONLY /*| O_NOATIME */);
-
int error;
-
+ /* TODO: properly open the file without access time using O_NOATIME */
+ git_file fd = git_futils_open_ro(path);
if (fd < 0)
- return git__throw(GIT_EOSERR, "Failed to check index. File missing or corrupted");
+ return fd;
- if (p_fstat(fd, &st) < GIT_SUCCESS) {
+ if (p_fstat(fd, &st) < 0 ||
+ !S_ISREG(st.st_mode) ||
+ !git__is_sizet(st.st_size) ||
+ (idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20)
+ {
p_close(fd);
- return git__throw(GIT_EOSERR, "Failed to check index. File appears to be corrupted");
- }
-
- if (!git__is_sizet(st.st_size))
- return GIT_ENOMEM;
-
- idx_size = (size_t)st.st_size;
-
- if (idx_size < 4 * 256 + 20 + 20) {
- p_close(fd);
- return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Object is corrupted");
+ giterr_set(GITERR_OS, "Failed to check pack index.");
+ return -1;
}
error = git_futils_mmap_ro(&p->index_map, fd, 0, idx_size);
+
p_close(fd);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to check index");
+ if (error < 0)
+ return error;
hdr = idx_map = p->index_map.data;
@@ -100,7 +97,7 @@ static int pack_index_check(const char *path, struct git_pack_file *p)
if (version < 2 || version > 2) {
git_futils_mmap_free(&p->index_map);
- return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Unsupported index version");
+ return packfile_error("unsupported index version");
}
} else
@@ -116,7 +113,7 @@ static int pack_index_check(const char *path, struct git_pack_file *p)
uint32_t n = ntohl(index[i]);
if (n < nr) {
git_futils_mmap_free(&p->index_map);
- return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Index is non-monotonic");
+ return packfile_error("index is non-monotonic");
}
nr = n;
}
@@ -131,7 +128,7 @@ static int pack_index_check(const char *path, struct git_pack_file *p)
*/
if (idx_size != 4*256 + nr * 24 + 20 + 20) {
git_futils_mmap_free(&p->index_map);
- return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Object is corrupted");
+ return packfile_error("index is corrupted");
}
} else if (version == 2) {
/*
@@ -155,39 +152,46 @@ static int pack_index_check(const char *path, struct git_pack_file *p)
if (idx_size < min_size || idx_size > max_size) {
git_futils_mmap_free(&p->index_map);
- return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Wrong index size");
+ return packfile_error("wrong index size");
}
}
p->index_version = version;
p->num_objects = nr;
- return GIT_SUCCESS;
+ return 0;
}
static int pack_index_open(struct git_pack_file *p)
{
char *idx_name;
int error;
+ size_t name_len, offset;
if (p->index_map.data)
- return GIT_SUCCESS;
+ return 0;
idx_name = git__strdup(p->pack_name);
- strcpy(idx_name + strlen(idx_name) - strlen(".pack"), ".idx");
+ GITERR_CHECK_ALLOC(idx_name);
+
+ name_len = strlen(idx_name);
+ offset = name_len - strlen(".pack");
+ assert(offset < name_len); /* make sure no underflow */
+
+ strncpy(idx_name + offset, ".idx", name_len - offset);
error = pack_index_check(idx_name, p);
git__free(idx_name);
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to open index");
+ return error;
}
static unsigned char *pack_window_open(
struct git_pack_file *p,
git_mwindow **w_cursor,
- off_t offset,
+ git_off_t offset,
unsigned int *left)
{
- if (p->mwf.fd == -1 && packfile_open(p) < GIT_SUCCESS)
+ if (p->mwf.fd == -1 && packfile_open(p) < 0)
return NULL;
/* Since packfiles end in a hash of their content and it's
@@ -201,7 +205,8 @@ static unsigned char *pack_window_open(
return git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);
}
-static unsigned long packfile_unpack_header1(
+static int packfile_unpack_header1(
+ unsigned long *usedp,
size_t *sizep,
git_otype *type,
const unsigned char *buf,
@@ -216,8 +221,13 @@ static unsigned long packfile_unpack_header1(
size = c & 15;
shift = 4;
while (c & 0x80) {
- if (len <= used || bitsizeof(long) <= shift)
- return 0;
+ if (len <= used)
+ return GIT_ESHORTBUFFER;
+
+ if (bitsizeof(long) <= shift) {
+ *usedp = 0;
+ return -1;
+ }
c = buf[used++];
size += (c & 0x7f) << shift;
@@ -225,7 +235,8 @@ static unsigned long packfile_unpack_header1(
}
*sizep = (size_t)size;
- return used;
+ *usedp = used;
+ return 0;
}
int git_packfile_unpack_header(
@@ -233,11 +244,12 @@ int git_packfile_unpack_header(
git_otype *type_p,
git_mwindow_file *mwf,
git_mwindow **w_curs,
- off_t *curpos)
+ git_off_t *curpos)
{
unsigned char *base;
unsigned int left;
unsigned long used;
+ int ret;
/* pack_window_open() assures us we have [base, base + 20) available
* as a range that we can look at at. (Its actually the hash
@@ -248,37 +260,39 @@ int git_packfile_unpack_header(
// base = pack_window_open(p, w_curs, *curpos, &left);
base = git_mwindow_open(mwf, w_curs, *curpos, 20, &left);
if (base == NULL)
- return GIT_ENOMEM;
-
- used = packfile_unpack_header1(size_p, type_p, base, left);
+ return GIT_ESHORTBUFFER;
- if (used == 0)
- return git__throw(GIT_EOBJCORRUPTED, "Header length is zero");
+ ret = packfile_unpack_header1(&used, size_p, type_p, base, left);
+ git_mwindow_close(w_curs);
+ if (ret == GIT_ESHORTBUFFER)
+ return ret;
+ else if (ret < 0)
+ return packfile_error("header length is zero");
*curpos += used;
- return GIT_SUCCESS;
+ return 0;
}
static int packfile_unpack_delta(
git_rawobj *obj,
struct git_pack_file *p,
git_mwindow **w_curs,
- off_t *curpos,
+ git_off_t *curpos,
size_t delta_size,
git_otype delta_type,
- off_t obj_offset)
+ git_off_t obj_offset)
{
- off_t base_offset;
+ git_off_t base_offset;
git_rawobj base, delta;
int error;
base_offset = get_delta_base(p, w_curs, curpos, delta_type, obj_offset);
+ git_mwindow_close(w_curs);
if (base_offset == 0)
- return git__throw(GIT_EOBJCORRUPTED, "Delta offset is zero");
- if (base_offset < 0)
- return git__rethrow(base_offset, "Failed to get delta base");
+ return packfile_error("delta offset is zero");
+ if (base_offset < 0) /* must actually be an error code */
+ return (int)base_offset;
- git_mwindow_close(w_curs);
error = git_packfile_unpack(&base, p, &base_offset);
/*
@@ -287,35 +301,35 @@ static int packfile_unpack_delta(
*
* We'll need to do this in order to support thin packs.
*/
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Corrupted delta");
+ if (error < 0)
+ return error;
error = packfile_unpack_compressed(&delta, p, w_curs, curpos, delta_size, delta_type);
- if (error < GIT_SUCCESS) {
+ git_mwindow_close(w_curs);
+ if (error < 0) {
git__free(base.data);
- return git__rethrow(error, "Corrupted delta");
+ return error;
}
obj->type = base.type;
- error = git__delta_apply(obj,
- base.data, base.len,
- delta.data, delta.len);
+ error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);
git__free(base.data);
git__free(delta.data);
/* TODO: we might want to cache this shit. eventually */
//add_delta_base_cache(p, base_offset, base, base_size, *type);
+
return error; /* error set by git__delta_apply */
}
int git_packfile_unpack(
- git_rawobj *obj,
- struct git_pack_file *p,
- off_t *obj_offset)
+ git_rawobj *obj,
+ struct git_pack_file *p,
+ git_off_t *obj_offset)
{
git_mwindow *w_curs = NULL;
- off_t curpos = *obj_offset;
+ git_off_t curpos = *obj_offset;
int error;
size_t size = 0;
@@ -330,8 +344,10 @@ int git_packfile_unpack(
obj->type = GIT_OBJ_BAD;
error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to unpack packfile");
+ git_mwindow_close(&w_curs);
+
+ if (error < 0)
+ return error;
switch (type) {
case GIT_OBJ_OFS_DELTA:
@@ -351,33 +367,28 @@ int git_packfile_unpack(
break;
default:
- error = GIT_EOBJCORRUPTED;
+ error = packfile_error("invalid packfile type in header");;
break;
}
- git_mwindow_close(&w_curs);
-
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to unpack object");
-
*obj_offset = curpos;
- return GIT_SUCCESS;
+ return error;
}
int packfile_unpack_compressed(
- git_rawobj *obj,
- struct git_pack_file *p,
- git_mwindow **w_curs,
- off_t *curpos,
- size_t size,
- git_otype type)
+ git_rawobj *obj,
+ struct git_pack_file *p,
+ git_mwindow **w_curs,
+ git_off_t *curpos,
+ size_t size,
+ git_otype type)
{
int st;
z_stream stream;
unsigned char *buffer, *in;
- buffer = git__malloc(size + 1);
- memset(buffer, 0x0, size + 1);
+ buffer = git__calloc(1, size + 1);
+ GITERR_CHECK_ALLOC(buffer);
memset(&stream, 0, sizeof(stream));
stream.next_out = buffer;
@@ -386,17 +397,26 @@ int packfile_unpack_compressed(
st = inflateInit(&stream);
if (st != Z_OK) {
git__free(buffer);
- return git__throw(GIT_EZLIB, "Error in zlib");
+ giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
+
+ return -1;
}
do {
in = pack_window_open(p, w_curs, *curpos, &stream.avail_in);
stream.next_in = in;
st = inflate(&stream, Z_FINISH);
+ git_mwindow_close(w_curs);
if (!stream.avail_out)
break; /* the payload is larger than it should be */
+ if (st == Z_BUF_ERROR && in == NULL) {
+ inflateEnd(&stream);
+ git__free(buffer);
+ return GIT_ESHORTBUFFER;
+ }
+
*curpos += stream.next_in - in;
} while (st == Z_OK || st == Z_BUF_ERROR);
@@ -404,30 +424,36 @@ int packfile_unpack_compressed(
if ((st != Z_STREAM_END) || stream.total_out != size) {
git__free(buffer);
- return git__throw(GIT_EZLIB, "Error in zlib");
+ giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
+ return -1;
}
obj->type = type;
obj->len = size;
obj->data = buffer;
- return GIT_SUCCESS;
+ return 0;
}
/*
* curpos is where the data starts, delta_obj_offset is the where the
* header starts
*/
-off_t get_delta_base(
- struct git_pack_file *p,
- git_mwindow **w_curs,
- off_t *curpos,
- git_otype type,
- off_t delta_obj_offset)
+git_off_t get_delta_base(
+ struct git_pack_file *p,
+ git_mwindow **w_curs,
+ git_off_t *curpos,
+ git_otype type,
+ git_off_t delta_obj_offset)
{
- unsigned char *base_info = pack_window_open(p, w_curs, *curpos, NULL);
- off_t base_offset;
+ unsigned int left = 0;
+ unsigned char *base_info;
+ git_off_t base_offset;
git_oid unused;
+ base_info = pack_window_open(p, w_curs, *curpos, &left);
+ /* Assumption: the only reason this would fail is because the file is too small */
+ if (base_info == NULL)
+ return GIT_ESHORTBUFFER;
/* pack_window_open() assured us we have [base_info, base_info + 20)
* as a range that we can look at without walking off the
* end of the mapped window. Its actually the hash size
@@ -439,6 +465,8 @@ off_t get_delta_base(
unsigned char c = base_info[used++];
base_offset = c & 127;
while (c & 128) {
+ if (left <= used)
+ return GIT_ESHORTBUFFER;
base_offset += 1;
if (!base_offset || MSB(base_offset, 7))
return 0; /* overflow */
@@ -463,8 +491,8 @@ off_t get_delta_base(
}
}
/* The base entry _must_ be in the same pack */
- if (pack_entry_find_offset(&base_offset, &unused, p, (git_oid *)base_info, GIT_OID_HEXSZ) < GIT_SUCCESS)
- return git__rethrow(GIT_EPACKCORRUPTED, "Base entry delta is not in the same pack");
+ if (pack_entry_find_offset(&base_offset, &unused, p, (git_oid *)base_info, GIT_OID_HEXSZ) < 0)
+ return packfile_error("base entry delta is not in the same pack");
*curpos += 20;
} else
return 0;
@@ -478,11 +506,11 @@ off_t get_delta_base(
*
***********************************************************/
-static struct git_pack_file *packfile_alloc(int extra)
+static struct git_pack_file *packfile_alloc(size_t extra)
{
- struct git_pack_file *p = git__malloc(sizeof(*p) + extra);
- memset(p, 0, sizeof(*p));
- p->mwf.fd = -1;
+ struct git_pack_file *p = git__calloc(1, sizeof(*p) + extra);
+ if (p != NULL)
+ p->mwf.fd = -1;
return p;
}
@@ -510,24 +538,25 @@ static int packfile_open(struct git_pack_file *p)
git_oid sha1;
unsigned char *idx_sha1;
- if (!p->index_map.data && pack_index_open(p) < GIT_SUCCESS)
- return git__throw(GIT_ENOTFOUND, "Failed to open packfile. File not found");
+ assert(p->index_map.data);
+
+ if (!p->index_map.data && pack_index_open(p) < 0)
+ return git_odb__error_notfound("failed to open packfile");
/* TODO: open with noatime */
- p->mwf.fd = p_open(p->pack_name, O_RDONLY);
- if (p->mwf.fd < 0 || p_fstat(p->mwf.fd, &st) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to open packfile. File appears to be corrupted");
+ p->mwf.fd = git_futils_open_ro(p->pack_name);
+ if (p->mwf.fd < 0)
+ return p->mwf.fd;
- if (git_mwindow_file_register(&p->mwf) < GIT_SUCCESS) {
- p_close(p->mwf.fd);
- return git__throw(GIT_ERROR, "Failed to register packfile windows");
- }
+ if (p_fstat(p->mwf.fd, &st) < 0 ||
+ git_mwindow_file_register(&p->mwf) < 0)
+ goto cleanup;
/* If we created the struct before we had the pack we lack size. */
if (!p->mwf.size) {
if (!S_ISREG(st.st_mode))
goto cleanup;
- p->mwf.size = (off_t)st.st_size;
+ p->mwf.size = (git_off_t)st.st_size;
} else if (p->mwf.size != st.st_size)
goto cleanup;
@@ -537,44 +566,35 @@ static int packfile_open(struct git_pack_file *p)
*/
fd_flag = fcntl(p->mwf.fd, F_GETFD, 0);
if (fd_flag < 0)
- return error("cannot determine file descriptor flags");
+ goto cleanup;
fd_flag |= FD_CLOEXEC;
if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1)
- return GIT_EOSERR;
+ goto cleanup;
#endif
/* Verify we recognize this pack file format. */
- if (p_read(p->mwf.fd, &hdr, sizeof(hdr)) < GIT_SUCCESS)
- goto cleanup;
-
- if (hdr.hdr_signature != htonl(PACK_SIGNATURE))
- goto cleanup;
-
- if (!pack_version_ok(hdr.hdr_version))
+ if (p_read(p->mwf.fd, &hdr, sizeof(hdr)) < 0 ||
+ hdr.hdr_signature != htonl(PACK_SIGNATURE) ||
+ !pack_version_ok(hdr.hdr_version))
goto cleanup;
/* Verify the pack matches its index. */
- if (p->num_objects != ntohl(hdr.hdr_entries))
- goto cleanup;
-
- if (p_lseek(p->mwf.fd, p->mwf.size - GIT_OID_RAWSZ, SEEK_SET) == -1)
- goto cleanup;
-
- if (p_read(p->mwf.fd, sha1.id, GIT_OID_RAWSZ) < GIT_SUCCESS)
+ if (p->num_objects != ntohl(hdr.hdr_entries) ||
+ p_lseek(p->mwf.fd, p->mwf.size - GIT_OID_RAWSZ, SEEK_SET) == -1 ||
+ p_read(p->mwf.fd, sha1.id, GIT_OID_RAWSZ) < 0)
goto cleanup;
idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40;
- if (git_oid_cmp(&sha1, (git_oid *)idx_sha1) != 0)
- goto cleanup;
-
- return GIT_SUCCESS;
+ if (git_oid_cmp(&sha1, (git_oid *)idx_sha1) == 0)
+ return 0;
cleanup:
+ giterr_set(GITERR_OS, "Invalid packfile '%s'", p->pack_name);
p_close(p->mwf.fd);
p->mwf.fd = -1;
- return git__throw(GIT_EPACKCORRUPTED, "Failed to open packfile. Pack is corrupted");
+ return -1;
}
int git_packfile_check(struct git_pack_file **pack_out, const char *path)
@@ -586,6 +606,7 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path)
*pack_out = NULL;
path_len = strlen(path);
p = packfile_alloc(path_len + 2);
+ GITERR_CHECK_ALLOC(p);
/*
* Make sure a corresponding .pack file exists and that
@@ -594,19 +615,19 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path)
path_len -= strlen(".idx");
if (path_len < 1) {
git__free(p);
- return git__throw(GIT_ENOTFOUND, "Failed to check packfile. Wrong path name");
+ return git_odb__error_notfound("invalid packfile path");
}
memcpy(p->pack_name, path, path_len);
strcpy(p->pack_name + path_len, ".keep");
- if (git_path_exists(p->pack_name) == GIT_SUCCESS)
+ if (git_path_exists(p->pack_name) == true)
p->pack_keep = 1;
strcpy(p->pack_name + path_len, ".pack");
- if (p_stat(p->pack_name, &st) < GIT_SUCCESS || !S_ISREG(st.st_mode)) {
+ if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) {
git__free(p);
- return git__throw(GIT_ENOTFOUND, "Failed to check packfile. File not found");
+ return git_odb__error_notfound("packfile not found");
}
/* ok, it looks sane as far as we can check without
@@ -618,11 +639,12 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path)
/* see if we can parse the sha1 oid in the packfile name */
if (path_len < 40 ||
- git_oid_fromstr(&p->sha1, path + path_len - GIT_OID_HEXSZ) < GIT_SUCCESS)
+ git_oid_fromstr(&p->sha1, path + path_len - GIT_OID_HEXSZ) < 0)
memset(&p->sha1, 0x0, GIT_OID_RAWSZ);
*pack_out = p;
- return GIT_SUCCESS;
+
+ return 0;
}
/***********************************************************
@@ -631,7 +653,7 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path)
*
***********************************************************/
-static off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n)
+static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n)
{
const unsigned char *index = p->index_map.data;
index += 4 * 256;
@@ -650,11 +672,11 @@ static off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n)
}
static int pack_entry_find_offset(
- off_t *offset_out,
- git_oid *found_oid,
- struct git_pack_file *p,
- const git_oid *short_oid,
- unsigned int len)
+ git_off_t *offset_out,
+ git_oid *found_oid,
+ struct git_pack_file *p,
+ const git_oid *short_oid,
+ unsigned int len)
{
const uint32_t *level1_ofs = p->index_map.data;
const unsigned char *index = p->index_map.data;
@@ -667,8 +689,8 @@ static int pack_entry_find_offset(
if (index == NULL) {
int error;
- if ((error = pack_index_open(p)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to find offset for pack entry");
+ if ((error = pack_index_open(p)) < 0)
+ return error;
assert(p->index_map.data);
@@ -726,22 +748,22 @@ static int pack_entry_find_offset(
}
}
- if (!found) {
- return git__throw(GIT_ENOTFOUND, "Failed to find offset for pack entry. Entry not found");
- } else if (found > 1) {
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to find offset for pack entry. Ambiguous sha1 prefix within pack");
- } else {
- *offset_out = nth_packed_object_offset(p, pos);
- git_oid_fromraw(found_oid, current);
+ if (!found)
+ return git_odb__error_notfound("failed to find offset for pack entry");
+ if (found > 1)
+ return git_odb__error_ambiguous("found multiple offsets for pack entry");
+ *offset_out = nth_packed_object_offset(p, pos);
+ git_oid_fromraw(found_oid, current);
#ifdef INDEX_DEBUG_LOOKUP
+ {
unsigned char hex_sha1[GIT_OID_HEXSZ + 1];
git_oid_fmt(hex_sha1, found_oid);
hex_sha1[GIT_OID_HEXSZ] = '\0';
printf("found lo=%d %s\n", lo, hex_sha1);
-#endif
- return GIT_SUCCESS;
}
+#endif
+ return 0;
}
int git_pack_entry_find(
@@ -750,7 +772,7 @@ int git_pack_entry_find(
const git_oid *short_oid,
unsigned int len)
{
- off_t offset;
+ git_off_t offset;
git_oid found_oid;
int error;
@@ -760,22 +782,22 @@ int git_pack_entry_find(
unsigned i;
for (i = 0; i < p->num_bad_objects; i++)
if (git_oid_cmp(short_oid, &p->bad_object_sha1[i]) == 0)
- return git__throw(GIT_ERROR, "Failed to find pack entry. Bad object found");
+ return packfile_error("bad object found in packfile");
}
error = pack_entry_find_offset(&offset, &found_oid, p, short_oid, len);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to find pack entry. Couldn't find offset");
+ if (error < 0)
+ return error;
/* we found a unique entry in the index;
* make sure the packfile backing the index
* still exists on disk */
- if (p->mwf.fd == -1 && packfile_open(p) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to find pack entry. Packfile doesn't exist on disk");
+ if (p->mwf.fd == -1 && (error = packfile_open(p)) < 0)
+ return error;
e->offset = offset;
e->p = p;
git_oid_cpy(&e->sha1, &found_oid);
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/pack.h b/src/pack.h
index 590297847..cd7a4d2e1 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -70,7 +70,7 @@ struct git_pack_file {
};
struct git_pack_entry {
- off_t offset;
+ git_off_t offset;
git_oid sha1;
struct git_pack_file *p;
};
@@ -80,13 +80,20 @@ int git_packfile_unpack_header(
git_otype *type_p,
git_mwindow_file *mwf,
git_mwindow **w_curs,
- off_t *curpos);
-
-int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, off_t *obj_offset);
-
-off_t get_delta_base(struct git_pack_file *p, git_mwindow **w_curs,
- off_t *curpos, git_otype type,
- off_t delta_obj_offset);
+ git_off_t *curpos);
+
+int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, git_off_t *obj_offset);
+int packfile_unpack_compressed(
+ git_rawobj *obj,
+ struct git_pack_file *p,
+ git_mwindow **w_curs,
+ git_off_t *curpos,
+ size_t size,
+ git_otype type);
+
+git_off_t get_delta_base(struct git_pack_file *p, git_mwindow **w_curs,
+ git_off_t *curpos, git_otype type,
+ git_off_t delta_obj_offset);
void packfile_free(struct git_pack_file *p);
int git_packfile_check(struct git_pack_file **pack_out, const char *path);
diff --git a/src/path.c b/src/path.c
index 2e6a1eb40..703f43af1 100644
--- a/src/path.c
+++ b/src/path.c
@@ -50,16 +50,14 @@ int git_path_basename_r(git_buf *buffer, const char *path)
while (startp > path && *(startp - 1) != '/')
startp--;
- len = endp - startp +1;
+ /* Cast is safe because max path < max int */
+ len = (int)(endp - startp + 1);
Exit:
result = len;
- if (buffer != NULL) {
- if (git_buf_set(buffer, startp, len) < GIT_SUCCESS)
- return git__rethrow(git_buf_lasterror(buffer),
- "Could not get basename of '%s'", path);
- }
+ if (buffer != NULL && git_buf_set(buffer, startp, len) < 0)
+ return -1;
return result;
}
@@ -100,7 +98,8 @@ int git_path_dirname_r(git_buf *buffer, const char *path)
endp--;
} while (endp > path && *endp == '/');
- len = endp - path +1;
+ /* Cast is safe because max path < max int */
+ len = (int)(endp - path + 1);
#ifdef GIT_WIN32
/* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return
@@ -115,11 +114,8 @@ int git_path_dirname_r(git_buf *buffer, const char *path)
Exit:
result = len;
- if (buffer != NULL) {
- if (git_buf_set(buffer, path, len) < GIT_SUCCESS)
- return git__rethrow(git_buf_lasterror(buffer),
- "Could not get dirname of '%s'", path);
- }
+ if (buffer != NULL && git_buf_set(buffer, path, len) < 0)
+ return -1;
return result;
}
@@ -153,7 +149,7 @@ char *git_path_basename(const char *path)
const char *git_path_topdir(const char *path)
{
size_t len;
- int i;
+ ssize_t i;
assert(path);
len = strlen(path);
@@ -161,7 +157,7 @@ const char *git_path_topdir(const char *path)
if (!len || path[len - 1] != '/')
return NULL;
- for (i = len - 2; i >= 0; --i)
+ for (i = (ssize_t)len - 2; i >= 0; --i)
if (path[i] == '/')
break;
@@ -176,54 +172,61 @@ int git_path_root(const char *path)
/* Does the root of the path look like a windows drive ? */
if (isalpha(path[0]) && (path[1] == ':'))
offset += 2;
+
+ /* Are we dealing with a windows network path? */
+ else if ((path[0] == '/' && path[1] == '/') ||
+ (path[0] == '\\' && path[1] == '\\'))
+ {
+ offset += 2;
+
+ /* Skip the computer name segment */
+ while (path[offset] && path[offset] != '/' && path[offset] != '\\')
+ offset++;
+ }
#endif
- if (*(path + offset) == '/')
+ if (path[offset] == '/' || path[offset] == '\\')
return offset;
- return -1; /* Not a real error. Rather a signal than the path is not rooted */
+ return -1; /* Not a real error - signals that path is not rooted */
}
int git_path_prettify(git_buf *path_out, const char *path, const char *base)
{
- int error = GIT_SUCCESS;
char buf[GIT_PATH_MAX];
- git_buf_clear(path_out);
+ assert(path && path_out);
/* construct path if needed */
if (base != NULL && git_path_root(path) < 0) {
- if ((error = git_buf_joinpath(path_out, base, path)) < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(path_out, base, path) < 0)
+ return -1;
path = path_out->ptr;
}
- if (path == NULL || p_realpath(path, buf) == NULL)
- error = GIT_EOSERR;
- else
- error = git_buf_sets(path_out, buf);
+ if (p_realpath(path, buf) == NULL) {
+ giterr_set(GITERR_OS, "Failed to resolve path '%s'", path);
+ git_buf_clear(path_out);
+ return (errno == ENOENT) ? GIT_ENOTFOUND : -1;
+ }
- return error;
+ return git_buf_sets(path_out, buf);
}
int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base)
{
int error = git_path_prettify(path_out, path, base);
-
- if (error == GIT_SUCCESS)
- error = git_path_to_dir(path_out);
-
- return error;
+ return (error < 0) ? error : git_path_to_dir(path_out);
}
int git_path_to_dir(git_buf *path)
{
if (path->asize > 0 &&
- path->size > 0 &&
- path->ptr[path->size - 1] != '/')
+ git_buf_len(path) > 0 &&
+ path->ptr[git_buf_len(path) - 1] != '/')
git_buf_putc(path, '/');
- return git_buf_lasterror(path);
+ return git_buf_oom(path) ? -1 : 0;
}
void git_path_string_to_dir(char* path, size_t size)
@@ -238,10 +241,10 @@ void git_path_string_to_dir(char* path, size_t size)
int git__percent_decode(git_buf *decoded_out, const char *input)
{
- int len, hi, lo, i, error = GIT_SUCCESS;
+ int len, hi, lo, i;
assert(decoded_out && input);
- len = strlen(input);
+ len = (int)strlen(input);
git_buf_clear(decoded_out);
for(i = 0; i < len; i++)
@@ -264,39 +267,40 @@ int git__percent_decode(git_buf *decoded_out, const char *input)
i += 2;
append:
- error = git_buf_putc(decoded_out, c);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to percent decode '%s'.", input);
+ if (git_buf_putc(decoded_out, c) < 0)
+ return -1;
}
- return error;
+ return 0;
+}
+
+static int error_invalid_local_file_uri(const char *uri)
+{
+ giterr_set(GITERR_CONFIG, "'%s' is not a valid local file URI", uri);
+ return -1;
}
int git_path_fromurl(git_buf *local_path_out, const char *file_url)
{
- int error = GIT_SUCCESS, offset = 0, len;
+ int offset = 0, len;
assert(local_path_out && file_url);
if (git__prefixcmp(file_url, "file://") != 0)
- return git__throw(GIT_EINVALIDPATH,
- "Parsing of '%s' failed. A file Uri is expected (ie. with 'file://' scheme).",
- file_url);
+ return error_invalid_local_file_uri(file_url);
offset += 7;
- len = strlen(file_url);
+ len = (int)strlen(file_url);
if (offset < len && file_url[offset] == '/')
offset++;
else if (offset < len && git__prefixcmp(file_url + offset, "localhost/") == 0)
offset += 10;
else
- return git__throw(GIT_EINVALIDPATH,
- "Parsing of '%s' failed. A local file Uri is expected.", file_url);
+ return error_invalid_local_file_uri(file_url);
if (offset >= len || file_url[offset] == '/')
- return git__throw(GIT_EINVALIDPATH,
- "Parsing of '%s' failed. Invalid file Uri format.", file_url);
+ return error_invalid_local_file_uri(file_url);
#ifndef _MSC_VER
offset--; /* A *nix absolute path starts with a forward slash */
@@ -304,11 +308,7 @@ int git_path_fromurl(git_buf *local_path_out, const char *file_url)
git_buf_clear(local_path_out);
- error = git__percent_decode(local_path_out, file_url + offset);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Parsing of '%s' failed.", file_url);
-
- return error;
+ return git__percent_decode(local_path_out, file_url + offset);
}
int git_path_walk_up(
@@ -317,7 +317,7 @@ int git_path_walk_up(
int (*cb)(void *data, git_buf *),
void *data)
{
- int error = GIT_SUCCESS;
+ int error = 0;
git_buf iter;
ssize_t stop = 0, scan;
char oldc = '\0';
@@ -325,19 +325,19 @@ int git_path_walk_up(
assert(path && cb);
if (ceiling != NULL) {
- if (git__prefixcmp(path->ptr, ceiling) == GIT_SUCCESS)
+ if (git__prefixcmp(path->ptr, ceiling) == 0)
stop = (ssize_t)strlen(ceiling);
else
- stop = path->size;
+ stop = git_buf_len(path);
}
- scan = path->size;
+ scan = git_buf_len(path);
iter.ptr = path->ptr;
- iter.size = path->size;
+ iter.size = git_buf_len(path);
iter.asize = path->asize;
while (scan >= stop) {
- if ((error = cb(data, &iter)) < GIT_SUCCESS)
+ if ((error = cb(data, &iter)) < 0)
break;
iter.ptr[scan] = oldc;
scan = git_buf_rfind_next(&iter, '/');
@@ -355,116 +355,123 @@ int git_path_walk_up(
return error;
}
-int git_path_exists(const char *path)
+bool git_path_exists(const char *path)
{
assert(path);
- return p_access(path, F_OK);
+ return p_access(path, F_OK) == 0;
}
-int git_path_isdir(const char *path)
+bool git_path_isdir(const char *path)
{
struct stat st;
- if (p_stat(path, &st) < GIT_SUCCESS)
- return GIT_ERROR;
+ if (p_stat(path, &st) < 0)
+ return false;
- return S_ISDIR(st.st_mode) ? GIT_SUCCESS : GIT_ERROR;
+ return S_ISDIR(st.st_mode) != 0;
}
-int git_path_isfile(const char *path)
+bool git_path_isfile(const char *path)
{
struct stat st;
- int stat_error;
assert(path);
- stat_error = p_stat(path, &st);
+ if (p_stat(path, &st) < 0)
+ return false;
- if (stat_error < GIT_SUCCESS)
- return -1;
+ return S_ISREG(st.st_mode) != 0;
+}
- if (!S_ISREG(st.st_mode))
- return -1;
+int git_path_lstat(const char *path, struct stat *st)
+{
+ int err = 0;
- return 0;
+ if (p_lstat(path, st) < 0) {
+ err = (errno == ENOENT) ? GIT_ENOTFOUND : -1;
+ giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
+ }
+
+ return err;
}
-static int _check_dir_contents(
+static bool _check_dir_contents(
git_buf *dir,
const char *sub,
- int (*predicate)(const char *))
+ bool (*predicate)(const char *))
{
- int error = GIT_SUCCESS;
- size_t dir_size = dir->size;
+ bool result;
+ size_t dir_size = git_buf_len(dir);
size_t sub_size = strlen(sub);
- /* separate allocation and join, so we can always leave git_buf valid */
- if ((error = git_buf_try_grow(dir, dir_size + sub_size + 2)) < GIT_SUCCESS)
- return error;
+ /* leave base valid even if we could not make space for subdir */
+ if (git_buf_try_grow(dir, dir_size + sub_size + 2) < 0)
+ return false;
+
+ /* save excursion */
git_buf_joinpath(dir, dir->ptr, sub);
- error = (*predicate)(dir->ptr);
+ result = predicate(dir->ptr);
/* restore path */
git_buf_truncate(dir, dir_size);
-
- return error;
+ return result;
}
-int git_path_contains(git_buf *dir, const char *item)
+bool git_path_contains(git_buf *dir, const char *item)
{
return _check_dir_contents(dir, item, &git_path_exists);
}
-int git_path_contains_dir(git_buf *base, const char *subdir)
+bool git_path_contains_dir(git_buf *base, const char *subdir)
{
return _check_dir_contents(base, subdir, &git_path_isdir);
}
-int git_path_contains_file(git_buf *base, const char *file)
+bool git_path_contains_file(git_buf *base, const char *file)
{
return _check_dir_contents(base, file, &git_path_isfile);
}
int git_path_find_dir(git_buf *dir, const char *path, const char *base)
{
- int error = GIT_SUCCESS;
+ int error;
if (base != NULL && git_path_root(path) < 0)
error = git_buf_joinpath(dir, base, path);
else
error = git_buf_sets(dir, path);
- if (error == GIT_SUCCESS) {
+ if (!error) {
char buf[GIT_PATH_MAX];
if (p_realpath(dir->ptr, buf) != NULL)
error = git_buf_sets(dir, buf);
}
/* call dirname if this is not a directory */
- if (error == GIT_SUCCESS && git_path_isdir(dir->ptr) != GIT_SUCCESS)
- if (git_path_dirname_r(dir, dir->ptr) < GIT_SUCCESS)
- error = git_buf_lasterror(dir);
+ if (!error && git_path_isdir(dir->ptr) == false)
+ error = git_path_dirname_r(dir, dir->ptr);
- if (error == GIT_SUCCESS)
+ if (!error)
error = git_path_to_dir(dir);
return error;
}
-int git_path_cmp(const char *name1, int len1, int isdir1,
- const char *name2, int len2, int isdir2)
+int git_path_cmp(
+ const char *name1, size_t len1, int isdir1,
+ const char *name2, size_t len2, int isdir2)
{
- int len = len1 < len2 ? len1 : len2;
+ size_t len = len1 < len2 ? len1 : len2;
int cmp;
cmp = memcmp(name1, name2, len);
if (cmp)
return cmp;
if (len1 < len2)
- return ((!isdir1 && !isdir2) ? -1 :
- (isdir1 ? '/' - name2[len1] : name2[len1] - '/'));
+ return (!isdir1 && !isdir2) ? -1 :
+ (isdir1 ? '/' - name2[len1] : name2[len1] - '/');
if (len1 > len2)
- return ((!isdir1 && !isdir2) ? 1 :
- (isdir2 ? name1[len2] - '/' : '/' - name1[len2]));
+ return (!isdir1 && !isdir2) ? 1 :
+ (isdir2 ? name1[len2] - '/' : '/' - name1[len2]);
return 0;
}
@@ -485,13 +492,15 @@ int git_path_direach(
DIR *dir;
struct dirent de_buf, *de;
- if (git_path_to_dir(path) < GIT_SUCCESS)
- return git_buf_lasterror(path);
+ if (git_path_to_dir(path) < 0)
+ return -1;
+
+ wd_len = git_buf_len(path);
- wd_len = path->size;
- dir = opendir(path->ptr);
- if (!dir)
- return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path->ptr);
+ if ((dir = opendir(path->ptr)) == NULL) {
+ giterr_set(GITERR_OS, "Failed to open directory '%s'", path->ptr);
+ return -1;
+ }
while (p_readdir_r(dir, &de_buf, &de) == 0 && de != NULL) {
int result;
@@ -499,21 +508,21 @@ int git_path_direach(
if (is_dot_or_dotdot(de->d_name))
continue;
- if (git_buf_puts(path, de->d_name) < GIT_SUCCESS)
- return git_buf_lasterror(path);
+ if (git_buf_puts(path, de->d_name) < 0)
+ return -1;
result = fn(arg, path);
git_buf_truncate(path, wd_len); /* restore path */
- if (result != GIT_SUCCESS) {
+ if (result < 0) {
closedir(dir);
- return result; /* The callee is reponsible for setting the correct error message */
+ return -1;
}
}
closedir(dir);
- return GIT_SUCCESS;
+ return 0;
}
int git_path_dirload(
@@ -531,9 +540,10 @@ int git_path_dirload(
path_len = strlen(path);
assert(path_len > 0 && path_len >= prefix_len);
- if ((dir = opendir(path)) == NULL)
- return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure."
- " An error occured while opening the directory", path);
+ if ((dir = opendir(path)) == NULL) {
+ giterr_set(GITERR_OS, "Failed to open directory '%s'", path);
+ return -1;
+ }
path += prefix_len;
path_len -= prefix_len;
@@ -550,8 +560,7 @@ int git_path_dirload(
entry_path = git__malloc(
path_len + need_slash + entry_len + 1 + alloc_extra);
- if (entry_path == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(entry_path);
if (path_len)
memcpy(entry_path, path, path_len);
@@ -560,19 +569,16 @@ int git_path_dirload(
memcpy(&entry_path[path_len + need_slash], de->d_name, entry_len);
entry_path[path_len + need_slash + entry_len] = '\0';
- if ((error = git_vector_insert(contents, entry_path)) < GIT_SUCCESS) {
- git__free(entry_path);
- return error;
- }
+ if (git_vector_insert(contents, entry_path) < 0)
+ return -1;
}
closedir(dir);
- if (error != GIT_SUCCESS)
- return git__throw(
- GIT_EOSERR, "Failed to process directory entry in `%s`", path);
+ if (error != 0)
+ giterr_set(GITERR_OS, "Failed to process directory entry in '%s'", path);
- return GIT_SUCCESS;
+ return error;
}
int git_path_with_stat_cmp(const void *a, const void *b)
@@ -591,11 +597,12 @@ int git_path_dirload_with_stat(
git_path_with_stat *ps;
git_buf full = GIT_BUF_INIT;
- if ((error = git_buf_set(&full, path, prefix_len)) != GIT_SUCCESS)
- return error;
+ if (git_buf_set(&full, path, prefix_len) < 0)
+ return -1;
- if ((error = git_path_dirload(path, prefix_len,
- sizeof(git_path_with_stat) + 1, contents)) != GIT_SUCCESS) {
+ error = git_path_dirload(
+ path, prefix_len, sizeof(git_path_with_stat) + 1, contents);
+ if (error < 0) {
git_buf_free(&full);
return error;
}
@@ -606,8 +613,10 @@ int git_path_dirload_with_stat(
memmove(ps->path, ps, path_len + 1);
ps->path_len = path_len;
- git_buf_joinpath(&full, full.ptr, ps->path);
- p_lstat(full.ptr, &ps->st);
+ if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 ||
+ (error = git_path_lstat(full.ptr, &ps->st)) < 0)
+ break;
+
git_buf_truncate(&full, prefix_len);
if (S_ISDIR(ps->st.st_mode)) {
diff --git a/src/path.h b/src/path.h
index 981fdd6a4..fd76805e5 100644
--- a/src/path.h
+++ b/src/path.h
@@ -113,48 +113,55 @@ extern int git_path_fromurl(git_buf *local_path_out, const char *file_url);
/**
* Check if a file exists and can be accessed.
- * @return GIT_SUCCESS if file exists, < 0 otherwise.
+ * @return true or false
*/
-extern int git_path_exists(const char *path);
+extern bool git_path_exists(const char *path);
/**
* Check if the given path points to a directory.
- * @return GIT_SUCCESS if it is a directory, < 0 otherwise.
+ * @return true or false
*/
-extern int git_path_isdir(const char *path);
+extern bool git_path_isdir(const char *path);
/**
* Check if the given path points to a regular file.
- * @return GIT_SUCCESS if it is a regular file, < 0 otherwise.
+ * @return true or false
*/
-extern int git_path_isfile(const char *path);
+extern bool git_path_isfile(const char *path);
+
+/**
+ * Stat a file and/or link and set error if needed.
+ */
+extern int git_path_lstat(const char *path, struct stat *st);
/**
* Check if the parent directory contains the item.
*
* @param dir Directory to check.
* @param item Item that might be in the directory.
- * @return GIT_SUCCESS if item exists in directory, <0 otherwise.
+ * @return 0 if item exists in directory, <0 otherwise.
*/
-extern int git_path_contains(git_buf *dir, const char *item);
+extern bool git_path_contains(git_buf *dir, const char *item);
/**
* Check if the given path contains the given subdirectory.
*
* @param parent Directory path that might contain subdir
* @param subdir Subdirectory name to look for in parent
- * @return GIT_SUCCESS if subdirectory exists, < 0 otherwise.
+ * @param append_if_exists If true, then subdir will be appended to the parent path if it does exist
+ * @return true if subdirectory exists, false otherwise.
*/
-extern int git_path_contains_dir(git_buf *parent, const char *subdir);
+extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
/**
* Check if the given path contains the given file.
*
* @param dir Directory path that might contain file
* @param file File name to look for in parent
- * @return GIT_SUCCESS if file exists, < 0 otherwise.
+ * @param append_if_exists If true, then file will be appended to the path if it does exist
+ * @return true if file exists, false otherwise.
*/
-extern int git_path_contains_file(git_buf *dir, const char *file);
+extern bool git_path_contains_file(git_buf *dir, const char *file);
/**
* Clean up path, prepending base if it is not already rooted.
@@ -197,14 +204,14 @@ extern int git_path_direach(
* Sort function to order two paths.
*/
extern int git_path_cmp(
- const char *name1, int len1, int isdir1,
- const char *name2, int len2, int isdir2);
+ const char *name1, size_t len1, int isdir1,
+ const char *name2, size_t len2, int isdir2);
/**
* Invoke callback up path directory by directory until the ceiling is
* reached (inclusive of a final call at the root_path).
*
- * Returning anything other than GIT_SUCCESS from the callback function
+ * Returning anything other than 0 from the callback function
* will stop the iteration and propogate the error to the caller.
*
* @param pathbuf Buffer the function reads the directory from and
diff --git a/src/pkt.c b/src/pkt.c
index 51da55de1..00836bc34 100644
--- a/src/pkt.c
+++ b/src/pkt.c
@@ -31,13 +31,12 @@ static int flush_pkt(git_pkt **out)
git_pkt *pkt;
pkt = git__malloc(sizeof(git_pkt));
- if (pkt == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_FLUSH;
*out = pkt;
- return GIT_SUCCESS;
+ return 0;
}
/* the rest of the line will be useful for multi_ack */
@@ -48,13 +47,12 @@ static int ack_pkt(git_pkt **out, const char *line, size_t len)
GIT_UNUSED(len);
pkt = git__malloc(sizeof(git_pkt));
- if (pkt == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_ACK;
*out = pkt;
- return GIT_SUCCESS;
+ return 0;
}
static int nak_pkt(git_pkt **out)
@@ -62,13 +60,12 @@ static int nak_pkt(git_pkt **out)
git_pkt *pkt;
pkt = git__malloc(sizeof(git_pkt));
- if (pkt == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_NAK;
*out = pkt;
- return GIT_SUCCESS;
+ return 0;
}
static int pack_pkt(git_pkt **out)
@@ -76,13 +73,12 @@ static int pack_pkt(git_pkt **out)
git_pkt *pkt;
pkt = git__malloc(sizeof(git_pkt));
- if (pkt == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_PACK;
*out = pkt;
- return GIT_SUCCESS;
+ return 0;
}
static int comment_pkt(git_pkt **out, const char *line, size_t len)
@@ -90,8 +86,7 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len)
git_pkt_comment *pkt;
pkt = git__malloc(sizeof(git_pkt_comment) + len + 1);
- if (pkt == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_COMMENT;
memcpy(pkt->comment, line, len);
@@ -99,7 +94,26 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len)
*out = (git_pkt *) pkt;
- return GIT_SUCCESS;
+ return 0;
+}
+
+static int err_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_err *pkt;
+
+ /* Remove "ERR " from the line */
+ line += 4;
+ len -= 4;
+ pkt = git__malloc(sizeof(git_pkt_err) + len + 1);
+ GITERR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_ERR;
+ memcpy(pkt->error, line, len);
+ pkt->error[len] = '\0';
+
+ *out = (git_pkt *) pkt;
+
+ return 0;
}
/*
@@ -107,25 +121,22 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len)
*/
static int ref_pkt(git_pkt **out, const char *line, size_t len)
{
- git_pkt_ref *pkt;
int error;
+ git_pkt_ref *pkt;
pkt = git__malloc(sizeof(git_pkt_ref));
- if (pkt == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(pkt);
memset(pkt, 0x0, sizeof(git_pkt_ref));
pkt->type = GIT_PKT_REF;
- error = git_oid_fromstr(&pkt->head.oid, line);
- if (error < GIT_SUCCESS) {
- error = git__throw(error, "Failed to parse reference ID");
- goto out;
- }
+ if ((error = git_oid_fromstr(&pkt->head.oid, line)) < 0)
+ goto error_out;
/* Check for a bit of consistency */
if (line[GIT_OID_HEXSZ] != ' ') {
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ref. No SP");
- goto out;
+ giterr_set(GITERR_NET, "Error parsing pkt-line");
+ error = -1;
+ goto error_out;
}
/* Jump from the name */
@@ -136,10 +147,8 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len)
--len;
pkt->head.name = git__malloc(len + 1);
- if (pkt->head.name == NULL) {
- error = GIT_ENOMEM;
- goto out;
- }
+ GITERR_CHECK_ALLOC(pkt->head.name);
+
memcpy(pkt->head.name, line, len);
pkt->head.name[len] = '\0';
@@ -147,36 +156,35 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len)
pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
}
-out:
- if (error < GIT_SUCCESS)
- git__free(pkt);
- else
- *out = (git_pkt *)pkt;
+ *out = (git_pkt *)pkt;
+ return 0;
+error_out:
+ git__free(pkt);
return error;
}
-static ssize_t parse_len(const char *line)
+static int32_t parse_len(const char *line)
{
char num[PKT_LEN_SIZE + 1];
int i, error;
- int len;
+ int32_t len;
const char *num_end;
memcpy(num, line, PKT_LEN_SIZE);
num[PKT_LEN_SIZE] = '\0';
for (i = 0; i < PKT_LEN_SIZE; ++i) {
- if (!isxdigit(num[i]))
- return GIT_ENOTNUM;
+ if (!isxdigit(num[i])) {
+ giterr_set(GITERR_NET, "Found invalid hex digit in length");
+ return -1;
+ }
}
- error = git__strtol32(&len, num, &num_end, 16);
- if (error < GIT_SUCCESS) {
+ if ((error = git__strtol32(&len, num, &num_end, 16)) < 0)
return error;
- }
- return (unsigned int) len;
+ return len;
}
/*
@@ -192,17 +200,18 @@ static ssize_t parse_len(const char *line)
* in ASCII hexadecimal (including itself)
*/
-int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t bufflen)
+int git_pkt_parse_line(
+ git_pkt **head, const char *line, const char **out, size_t bufflen)
{
- int error = GIT_SUCCESS;
- size_t len;
+ int ret;
+ int32_t len;
/* Not even enough for the length */
if (bufflen > 0 && bufflen < PKT_LEN_SIZE)
return GIT_ESHORTBUFFER;
- error = parse_len(line);
- if (error < GIT_SUCCESS) {
+ len = parse_len(line);
+ if (len < 0) {
/*
* If we fail to parse the length, it might be because the
* server is trying to send us the packfile already.
@@ -212,16 +221,14 @@ int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_
return pack_pkt(head);
}
- return git__throw(error, "Failed to parse pkt length");
+ return (int)len;
}
- len = error;
-
/*
* If we were given a buffer length, then make sure there is
* enough in the buffer to satisfy this line
*/
- if (bufflen > 0 && bufflen < len)
+ if (bufflen > 0 && bufflen < (size_t)len)
return GIT_ESHORTBUFFER;
line += PKT_LEN_SIZE;
@@ -231,7 +238,7 @@ int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_
*/
if (len == PKT_LEN_SIZE) {
*out = line;
- return GIT_SUCCESS;
+ return 0;
}
if (len == 0) { /* Flush pkt */
@@ -243,22 +250,24 @@ int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_
/* Assming the minimal size is actually 4 */
if (!git__prefixcmp(line, "ACK"))
- error = ack_pkt(head, line, len);
+ ret = ack_pkt(head, line, len);
else if (!git__prefixcmp(line, "NAK"))
- error = nak_pkt(head);
+ ret = nak_pkt(head);
+ else if (!git__prefixcmp(line, "ERR "))
+ ret = err_pkt(head, line, len);
else if (*line == '#')
- error = comment_pkt(head, line, len);
+ ret = comment_pkt(head, line, len);
else
- error = ref_pkt(head, line, len);
+ ret = ref_pkt(head, line, len);
*out = line + len;
- return error;
+ return ret;
}
void git_pkt_free(git_pkt *pkt)
{
- if(pkt->type == GIT_PKT_REF) {
+ if (pkt->type == GIT_PKT_REF) {
git_pkt_ref *p = (git_pkt_ref *) pkt;
git__free(p->head.name);
}
@@ -271,7 +280,7 @@ int git_pkt_buffer_flush(git_buf *buf)
return git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str));
}
-int git_pkt_send_flush(int s)
+int git_pkt_send_flush(GIT_SOCKET s)
{
return gitno_send(s, pkt_flush_str, strlen(pkt_flush_str), 0);
@@ -281,33 +290,20 @@ static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps
{
char capstr[20];
char oid[GIT_OID_HEXSZ +1] = {0};
- int len;
+ unsigned int len;
if (caps->ofs_delta)
- strcpy(capstr, GIT_CAP_OFS_DELTA);
+ strncpy(capstr, GIT_CAP_OFS_DELTA, sizeof(capstr));
- len = strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ + strlen(capstr) + 1 /* LF */;
- git_buf_grow(buf, buf->size + len);
+ len = (unsigned int)
+ (strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ +
+ strlen(capstr) + 1 /* LF */);
+ 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);
}
-static int send_want_with_caps(git_remote_head *head, git_transport_caps *caps, GIT_SOCKET fd)
-{
- git_buf buf = GIT_BUF_INIT;
- int error;
-
- error = buffer_want_with_caps(head, caps, &buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to buffer want with caps");
-
- error = gitno_send(fd, buf.ptr, buf.size, 0);
- git_buf_free(&buf);
-
- return error;
-}
-
/*
* All "want" packets have the same length and format, so what we do
* is overwrite the OID each time.
@@ -316,7 +312,6 @@ static int send_want_with_caps(git_remote_head *head, git_transport_caps *caps,
int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf)
{
unsigned int i = 0;
- int error;
git_remote_head *head;
if (caps->common) {
@@ -326,9 +321,8 @@ int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_b
break;
}
- error = buffer_want_with_caps(refs->contents[i], caps, buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to buffer want with caps");
+ if (buffer_want_with_caps(refs->contents[i], caps, buf) < 0)
+ return -1;
i++;
}
@@ -344,54 +338,13 @@ int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_b
git_buf_put(buf, pkt_want_prefix, strlen(pkt_want_prefix));
git_buf_put(buf, oid, GIT_OID_HEXSZ);
git_buf_putc(buf, '\n');
+ if (git_buf_oom(buf))
+ return -1;
}
return git_pkt_buffer_flush(buf);
}
-int git_pkt_send_wants(const git_vector *refs, git_transport_caps *caps, int fd)
-{
- unsigned int i = 0;
- int error = GIT_SUCCESS;
- char buf[sizeof(pkt_want_prefix) + GIT_OID_HEXSZ + 1];
- git_remote_head *head;
-
- memcpy(buf, pkt_want_prefix, strlen(pkt_want_prefix));
- buf[sizeof(buf) - 2] = '\n';
- buf[sizeof(buf) - 1] = '\0';
-
- /* If there are common caps, find the first one */
- if (caps->common) {
- for (; i < refs->length; ++i) {
- head = refs->contents[i];
- if (head->local)
- continue;
- else
- break;
- }
-
- error = send_want_with_caps(refs->contents[i], caps, fd);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to send want pkt with caps");
- /* Increase it here so it's correct whether we run this or not */
- i++;
- }
-
- /* Continue from where we left off */
- for (; i < refs->length; ++i) {
- head = refs->contents[i];
- if (head->local)
- continue;
-
- git_oid_fmt(buf + strlen(pkt_want_prefix), &head->oid);
- error = gitno_send(fd, buf, strlen(buf), 0);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to send want pkt");
- }
-
- return git_pkt_send_flush(fd);
-}
-
int git_pkt_buffer_have(git_oid *oid, git_buf *buf)
{
char oidhex[GIT_OID_HEXSZ + 1];
@@ -401,21 +354,7 @@ int git_pkt_buffer_have(git_oid *oid, git_buf *buf)
return git_buf_printf(buf, "%s%s\n", pkt_have_prefix, oidhex);
}
-int git_pkt_send_have(git_oid *oid, int fd)
-{
- char buf[] = "0032have 0000000000000000000000000000000000000000\n";
-
- git_oid_fmt(buf + strlen(pkt_have_prefix), oid);
- return gitno_send(fd, buf, strlen(buf), 0);
-}
-
-
int git_pkt_buffer_done(git_buf *buf)
{
return git_buf_puts(buf, pkt_done_str);
}
-
-int git_pkt_send_done(int fd)
-{
- return gitno_send(fd, pkt_done_str, strlen(pkt_done_str), 0);
-}
diff --git a/src/pkt.h b/src/pkt.h
index b0bc0892e..75442c833 100644
--- a/src/pkt.h
+++ b/src/pkt.h
@@ -11,6 +11,7 @@
#include "common.h"
#include "transport.h"
#include "buffer.h"
+#include "posix.h"
#include "git2/net.h"
enum git_pkt_type {
@@ -22,6 +23,7 @@ enum git_pkt_type {
GIT_PKT_NAK,
GIT_PKT_PACK,
GIT_PKT_COMMENT,
+ GIT_PKT_ERR,
};
/* Used for multi-ack */
@@ -63,15 +65,17 @@ typedef struct {
char comment[GIT_FLEX_ARRAY];
} git_pkt_comment;
+typedef struct {
+ enum git_pkt_type type;
+ char error[GIT_FLEX_ARRAY];
+} git_pkt_err;
+
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
int git_pkt_buffer_flush(git_buf *buf);
-int git_pkt_send_flush(int s);
+int git_pkt_send_flush(GIT_SOCKET s);
int git_pkt_buffer_done(git_buf *buf);
-int git_pkt_send_done(int s);
int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf);
-int git_pkt_send_wants(const git_vector *refs, git_transport_caps *caps, int fd);
int git_pkt_buffer_have(git_oid *oid, git_buf *buf);
-int git_pkt_send_have(git_oid *oid, int fd);
void git_pkt_free(git_pkt *pkt);
#endif
diff --git a/src/pool.c b/src/pool.c
new file mode 100644
index 000000000..641292d06
--- /dev/null
+++ b/src/pool.c
@@ -0,0 +1,294 @@
+#include "pool.h"
+#ifndef GIT_WIN32
+#include <unistd.h>
+#endif
+
+struct git_pool_page {
+ git_pool_page *next;
+ uint32_t size;
+ uint32_t avail;
+ char data[GIT_FLEX_ARRAY];
+};
+
+#define GIT_POOL_MIN_USABLE 4
+#define GIT_POOL_MIN_PAGESZ 2 * sizeof(void*)
+
+static void *pool_alloc_page(git_pool *pool, uint32_t size);
+static void pool_insert_page(git_pool *pool, git_pool_page *page);
+
+int git_pool_init(
+ git_pool *pool, uint32_t item_size, uint32_t items_per_page)
+{
+ assert(pool);
+
+ if (!item_size)
+ item_size = 1;
+ /* round up item_size for decent object alignment */
+ if (item_size > 4)
+ item_size = (item_size + 7) & ~7;
+ else if (item_size == 3)
+ item_size = 4;
+
+ if (!items_per_page)
+ items_per_page = git_pool__suggest_items_per_page(item_size);
+ if (item_size * items_per_page < GIT_POOL_MIN_PAGESZ)
+ items_per_page = (GIT_POOL_MIN_PAGESZ + item_size - 1) / item_size;
+
+ memset(pool, 0, sizeof(git_pool));
+ pool->item_size = item_size;
+ pool->page_size = item_size * items_per_page;
+
+ return 0;
+}
+
+void git_pool_clear(git_pool *pool)
+{
+ git_pool_page *scan, *next;
+
+ for (scan = pool->open; scan != NULL; scan = next) {
+ next = scan->next;
+ git__free(scan);
+ }
+ pool->open = NULL;
+
+ for (scan = pool->full; scan != NULL; scan = next) {
+ next = scan->next;
+ git__free(scan);
+ }
+ pool->full = NULL;
+
+ pool->free_list = NULL;
+
+ pool->items = 0;
+
+ pool->has_string_alloc = 0;
+ pool->has_multi_item_alloc = 0;
+ pool->has_large_page_alloc = 0;
+}
+
+void git_pool_swap(git_pool *a, git_pool *b)
+{
+ git_pool temp;
+
+ if (a == b)
+ return;
+
+ memcpy(&temp, a, sizeof(temp));
+ memcpy(a, b, sizeof(temp));
+ memcpy(b, &temp, sizeof(temp));
+}
+
+static void pool_insert_page(git_pool *pool, git_pool_page *page)
+{
+ git_pool_page *scan;
+
+ /* If there are no open pages or this page has the most open space,
+ * insert it at the beginning of the list. This is the common case.
+ */
+ if (pool->open == NULL || pool->open->avail < page->avail) {
+ page->next = pool->open;
+ pool->open = page;
+ return;
+ }
+
+ /* Otherwise insert into sorted position. */
+ for (scan = pool->open;
+ scan->next && scan->next->avail > page->avail;
+ scan = scan->next);
+ page->next = scan->next;
+ scan->next = page;
+}
+
+static void *pool_alloc_page(git_pool *pool, uint32_t size)
+{
+ git_pool_page *page;
+ uint32_t alloc_size;
+
+ if (size <= pool->page_size)
+ alloc_size = pool->page_size;
+ else {
+ alloc_size = size;
+ pool->has_large_page_alloc = 1;
+ }
+
+ page = git__calloc(1, alloc_size + sizeof(git_pool_page));
+ if (!page)
+ return NULL;
+
+ page->size = alloc_size;
+ page->avail = alloc_size - size;
+
+ if (page->avail > 0)
+ pool_insert_page(pool, page);
+ else {
+ page->next = pool->full;
+ pool->full = page;
+ }
+
+ pool->items++;
+
+ return page->data;
+}
+
+GIT_INLINE(void) pool_remove_page(
+ git_pool *pool, git_pool_page *page, git_pool_page *prev)
+{
+ if (prev == NULL)
+ pool->open = page->next;
+ else
+ prev->next = page->next;
+}
+
+void *git_pool_malloc(git_pool *pool, uint32_t items)
+{
+ git_pool_page *scan = pool->open, *prev;
+ uint32_t size = items * pool->item_size;
+ void *ptr = NULL;
+
+ pool->has_string_alloc = 0;
+ if (items > 1)
+ pool->has_multi_item_alloc = 1;
+ else if (pool->free_list != NULL) {
+ ptr = pool->free_list;
+ pool->free_list = *((void **)pool->free_list);
+ return ptr;
+ }
+
+ /* just add a block if there is no open one to accomodate this */
+ if (size >= pool->page_size || !scan || scan->avail < size)
+ return pool_alloc_page(pool, size);
+
+ pool->items++;
+
+ /* find smallest block in free list with space */
+ for (scan = pool->open, prev = NULL;
+ scan->next && scan->next->avail >= size;
+ prev = scan, scan = scan->next);
+
+ /* allocate space from the block */
+ ptr = &scan->data[scan->size - scan->avail];
+ scan->avail -= size;
+
+ /* move to full list if there is almost no space left */
+ if (scan->avail < pool->item_size || scan->avail < GIT_POOL_MIN_USABLE) {
+ pool_remove_page(pool, scan, prev);
+ scan->next = pool->full;
+ pool->full = scan;
+ }
+ /* reorder list if block is now smaller than the one after it */
+ else if (scan->next != NULL && scan->next->avail > scan->avail) {
+ pool_remove_page(pool, scan, prev);
+ pool_insert_page(pool, scan);
+ }
+
+ return ptr;
+}
+
+char *git_pool_strndup(git_pool *pool, const char *str, size_t n)
+{
+ void *ptr = NULL;
+
+ assert(pool && str && pool->item_size == sizeof(char));
+
+ if ((ptr = git_pool_malloc(pool, (uint32_t)(n + 1))) != NULL) {
+ memcpy(ptr, str, n);
+ *(((char *)ptr) + n) = '\0';
+ }
+ pool->has_string_alloc = 1;
+
+ return ptr;
+}
+
+char *git_pool_strdup(git_pool *pool, const char *str)
+{
+ assert(pool && str && pool->item_size == sizeof(char));
+
+ return git_pool_strndup(pool, str, strlen(str));
+}
+
+char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
+{
+ void *ptr;
+ size_t len_a, len_b;
+
+ assert(pool && a && b && pool->item_size == sizeof(char));
+
+ len_a = a ? strlen(a) : 0;
+ len_b = b ? strlen(b) : 0;
+
+ if ((ptr = git_pool_malloc(pool, (uint32_t)(len_a + len_b + 1))) != NULL) {
+ if (len_a)
+ memcpy(ptr, a, len_a);
+ if (len_b)
+ memcpy(((char *)ptr) + len_a, b, len_b);
+ *(((char *)ptr) + len_a + len_b) = '\0';
+ }
+ pool->has_string_alloc = 1;
+
+ return ptr;
+}
+
+void git_pool_free(git_pool *pool, void *ptr)
+{
+ assert(pool && ptr && pool->item_size >= sizeof(void*));
+
+ *((void **)ptr) = pool->free_list;
+ pool->free_list = ptr;
+}
+
+uint32_t git_pool__open_pages(git_pool *pool)
+{
+ uint32_t ct = 0;
+ git_pool_page *scan;
+ for (scan = pool->open; scan != NULL; scan = scan->next) ct++;
+ return ct;
+}
+
+uint32_t git_pool__full_pages(git_pool *pool)
+{
+ uint32_t ct = 0;
+ git_pool_page *scan;
+ for (scan = pool->full; scan != NULL; scan = scan->next) ct++;
+ return ct;
+}
+
+bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
+{
+ git_pool_page *scan;
+ for (scan = pool->open; scan != NULL; scan = scan->next)
+ if ((void *)scan->data <= ptr &&
+ (void *)(((char *)scan->data) + scan->size) > ptr)
+ return true;
+ for (scan = pool->full; scan != NULL; scan = scan->next)
+ if ((void *)scan->data <= ptr &&
+ (void *)(((char *)scan->data) + scan->size) > ptr)
+ return true;
+ return false;
+}
+
+uint32_t git_pool__system_page_size(void)
+{
+ static uint32_t size = 0;
+
+ if (!size) {
+#ifdef GIT_WIN32
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+ size = (uint32_t)info.dwPageSize;
+#else
+ size = (uint32_t)sysconf(_SC_PAGE_SIZE);
+#endif
+
+ size -= 2 * sizeof(void *); /* allow space for malloc overhead */
+ }
+
+ return size;
+}
+
+uint32_t git_pool__suggest_items_per_page(uint32_t item_size)
+{
+ uint32_t page_bytes =
+ git_pool__system_page_size() - sizeof(git_pool_page);
+ return page_bytes / item_size;
+}
+
diff --git a/src/pool.h b/src/pool.h
new file mode 100644
index 000000000..54a2861ed
--- /dev/null
+++ b/src/pool.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 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.
+ */
+#ifndef INCLUDE_pool_h__
+#define INCLUDE_pool_h__
+
+#include "common.h"
+
+typedef struct git_pool_page git_pool_page;
+
+/**
+ * Chunked allocator.
+ *
+ * A `git_pool` can be used when you want to cheaply allocate
+ * multiple items of the same type and are willing to free them
+ * all together with a single call. The two most common cases
+ * are a set of fixed size items (such as lots of OIDs) or a
+ * bunch of strings.
+ *
+ * Internally, a `git_pool` allocates pages of memory and then
+ * deals out blocks from the trailing unused portion of each page.
+ * The pages guarantee that the number of actual allocations done
+ * will be much smaller than the number of items needed.
+ *
+ * For examples of how to set up a `git_pool` see `git_pool_init`.
+ */
+typedef struct {
+ git_pool_page *open; /* pages with space left */
+ git_pool_page *full; /* pages with no space left */
+ void *free_list; /* optional: list of freed blocks */
+ uint32_t item_size; /* size of single alloc unit in bytes */
+ uint32_t page_size; /* size of page in bytes */
+ uint32_t items;
+ unsigned has_string_alloc : 1; /* was the strdup function used */
+ unsigned has_multi_item_alloc : 1; /* was items ever > 1 in malloc */
+ unsigned has_large_page_alloc : 1; /* are any pages > page_size */
+} git_pool;
+
+#define GIT_POOL_INIT_STRINGPOOL { 0, 0, 0, 1, 4000, 0, 0, 0, 0 }
+
+/**
+ * Initialize a pool.
+ *
+ * To allocation strings, use like this:
+ *
+ * git_pool_init(&string_pool, 1, 0);
+ * my_string = git_pool_strdup(&string_pool, your_string);
+ *
+ * To allocate items of fixed size, use like this:
+ *
+ * git_pool_init(&pool, sizeof(item), 0);
+ * my_item = git_pool_malloc(&pool, 1);
+ *
+ * Of course, you can use this in other ways, but those are the
+ * two most common patterns.
+ */
+extern int git_pool_init(
+ git_pool *pool, uint32_t item_size, uint32_t items_per_page);
+
+/**
+ * Free all items in pool
+ */
+extern void git_pool_clear(git_pool *pool);
+
+/**
+ * Swap two pools with one another
+ */
+extern void git_pool_swap(git_pool *a, git_pool *b);
+
+/**
+ * Allocate space for one or more items from a pool.
+ */
+extern void *git_pool_malloc(git_pool *pool, uint32_t items);
+
+/**
+ * Allocate space and duplicate string data into it.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strndup(git_pool *pool, const char *str, size_t n);
+
+/**
+ * Allocate space and duplicate a string into it.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strdup(git_pool *pool, const char *str);
+
+/**
+ * Allocate space for the concatenation of two strings.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b);
+
+/**
+ * Push a block back onto the free list for the pool.
+ *
+ * This is allowed only if the item_size is >= sizeof(void*).
+ *
+ * In some cases, it is helpful to "release" an allocated block
+ * for reuse. Pools don't support a general purpose free, but
+ * they will keep a simple free blocks linked list provided the
+ * native block size is large enough to hold a void pointer
+ */
+extern void git_pool_free(git_pool *pool, void *ptr);
+
+/*
+ * Misc utilities
+ */
+
+extern uint32_t git_pool__open_pages(git_pool *pool);
+
+extern uint32_t git_pool__full_pages(git_pool *pool);
+
+extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr);
+
+extern uint32_t git_pool__system_page_size(void);
+
+extern uint32_t git_pool__suggest_items_per_page(uint32_t item_size);
+
+#endif
diff --git a/src/posix.c b/src/posix.c
index d2364d9b4..a3f81d767 100644
--- a/src/posix.c
+++ b/src/posix.c
@@ -31,27 +31,25 @@ int p_getcwd(char *buffer_out, size_t size)
cwd_buffer = getcwd(buffer_out, size);
if (cwd_buffer == NULL)
- return git__throw(GIT_EOSERR, "Failed to retrieve current working directory");
+ return -1;
git_path_mkposix(buffer_out);
+ git_path_string_to_dir(buffer_out, size); /* append trailing slash */
- git_path_string_to_dir(buffer_out, size); //Ensure the path ends with a trailing slash
-
- return GIT_SUCCESS;
+ return 0;
}
int p_rename(const char *from, const char *to)
{
if (!link(from, to)) {
p_unlink(from);
- return GIT_SUCCESS;
+ return 0;
}
if (!rename(from, to))
- return GIT_SUCCESS;
-
- return GIT_ERROR;
+ return 0;
+ return -1;
}
#endif
@@ -60,11 +58,17 @@ int p_read(git_file fd, void *buf, size_t cnt)
{
char *b = buf;
while (cnt) {
- ssize_t r = read(fd, b, cnt);
+ ssize_t r;
+#ifdef GIT_WIN32
+ assert((size_t)((unsigned int)cnt) == cnt);
+ r = read(fd, b, (unsigned int)cnt);
+#else
+ r = read(fd, b, cnt);
+#endif
if (r < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
- return GIT_EOSERR;
+ return -1;
}
if (!r)
break;
@@ -78,18 +82,24 @@ int p_write(git_file fd, const void *buf, size_t cnt)
{
const char *b = buf;
while (cnt) {
- ssize_t r = write(fd, b, cnt);
+ ssize_t r;
+#ifdef GIT_WIN32
+ assert((size_t)((unsigned int)cnt) == cnt);
+ r = write(fd, b, (unsigned int)cnt);
+#else
+ r = write(fd, b, cnt);
+#endif
if (r < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
- return GIT_EOSERR;
+ return -1;
}
if (!r) {
errno = EPIPE;
- return GIT_EOSERR;
+ return -1;
}
cnt -= r;
b += r;
}
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/posix.h b/src/posix.h
index fb17cba6c..752d5156f 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -54,6 +54,14 @@ extern int p_rename(const char *from, const char *to);
#define p_rmdir(p) rmdir(p)
#define p_chmod(p,m) chmod(p, m)
#define p_access(p,m) access(p,m)
+#define p_recv(s,b,l,f) recv(s,b,l,f)
+#define p_send(s,b,l,f) send(s,b,l,f)
+typedef int GIT_SOCKET;
+#define INVALID_SOCKET -1
+
+#else
+
+typedef SOCKET GIT_SOCKET;
#endif
diff --git a/src/pqueue.c b/src/pqueue.c
index 3fbf93315..cb59c13ec 100644
--- a/src/pqueue.c
+++ b/src/pqueue.c
@@ -17,14 +17,14 @@ int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri)
assert(q);
/* Need to allocate n+1 elements since element 0 isn't used. */
- if ((q->d = git__malloc((n + 1) * sizeof(void *))) == NULL)
- return GIT_ENOMEM;
+ q->d = git__malloc((n + 1) * sizeof(void *));
+ GITERR_CHECK_ALLOC(q->d);
q->size = 1;
q->avail = q->step = (n + 1); /* see comment above about n+1 */
q->cmppri = cmppri;
- return GIT_SUCCESS;
+ return 0;
}
@@ -102,8 +102,8 @@ int git_pqueue_insert(git_pqueue *q, void *d)
/* allocate more memory if necessary */
if (q->size >= q->avail) {
newsize = q->size + q->step;
- if ((tmp = git__realloc(q->d, sizeof(void *) * newsize)) == NULL)
- return GIT_ENOMEM;
+ tmp = git__realloc(q->d, sizeof(void *) * newsize);
+ GITERR_CHECK_ALLOC(tmp);
q->d = tmp;
q->avail = newsize;
@@ -114,7 +114,7 @@ int git_pqueue_insert(git_pqueue *q, void *d)
q->d[i] = d;
bubble_up(q, i);
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/protocol.c b/src/protocol.c
index dd93623b3..a75354121 100644
--- a/src/protocol.c
+++ b/src/protocol.c
@@ -17,10 +17,12 @@ int git_protocol_store_refs(git_protocol *p, const char *data, size_t len)
const char *line_end, *ptr;
if (len == 0) { /* EOF */
- if (buf->size != 0)
- return p->error = git__throw(GIT_ERROR, "EOF and unprocessed data");
- else
+ if (git_buf_len(buf) != 0) {
+ giterr_set(GITERR_NET, "Unexpected EOF");
+ return p->error = -1;
+ } else {
return 0;
+ }
}
git_buf_put(buf, data, len);
@@ -28,23 +30,29 @@ int git_protocol_store_refs(git_protocol *p, const char *data, size_t len)
while (1) {
git_pkt *pkt;
- if (buf->size == 0)
+ if (git_buf_len(buf) == 0)
return 0;
- error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->size);
+ error = git_pkt_parse_line(&pkt, ptr, &line_end, git_buf_len(buf));
if (error == GIT_ESHORTBUFFER)
return 0; /* Ask for more */
- if (error < GIT_SUCCESS)
- return p->error = git__rethrow(error, "Failed to parse pkt-line");
+ if (error < 0)
+ return p->error = -1;
git_buf_consume(buf, line_end);
- error = git_vector_insert(refs, pkt);
- if (error < GIT_SUCCESS)
- return p->error = git__rethrow(error, "Failed to add pkt to list");
+
+ if (pkt->type == GIT_PKT_ERR) {
+ giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error);
+ git__free(pkt);
+ return -1;
+ }
+
+ if (git_vector_insert(refs, pkt) < 0)
+ return p->error = -1;
if (pkt->type == GIT_PKT_FLUSH)
p->flush = 1;
}
- return error;
+ return 0;
}
diff --git a/src/reflog.c b/src/reflog.c
index e8b0b9811..3ea073e65 100644
--- a/src/reflog.c
+++ b/src/reflog.c
@@ -16,23 +16,21 @@ static int reflog_init(git_reflog **reflog, git_reference *ref)
*reflog = NULL;
- log = git__malloc(sizeof(git_reflog));
- if (log == NULL)
- return GIT_ENOMEM;
-
- memset(log, 0x0, sizeof(git_reflog));
+ log = git__calloc(1, sizeof(git_reflog));
+ GITERR_CHECK_ALLOC(log);
log->ref_name = git__strdup(ref->name);
+ GITERR_CHECK_ALLOC(log->ref_name);
if (git_vector_init(&log->entries, 0, NULL) < 0) {
git__free(log->ref_name);
git__free(log);
- return GIT_ENOMEM;
+ return -1;
}
*reflog = log;
- return GIT_SUCCESS;
+ return 0;
}
static int reflog_write(const char *log_path, const char *oid_old,
@@ -42,9 +40,22 @@ static int reflog_write(const char *log_path, const char *oid_old,
int error;
git_buf log = GIT_BUF_INIT;
git_filebuf fbuf = GIT_FILEBUF_INIT;
+ bool trailing_newline = false;
assert(log_path && oid_old && oid_new && committer);
+ if (msg) {
+ const char *newline = strchr(msg, '\n');
+ if (newline) {
+ if (*(newline + 1) == '\0')
+ trailing_newline = true;
+ else {
+ giterr_set(GITERR_INVALID, "Reflog message cannot contain newline");
+ return -1;
+ }
+ }
+ }
+
git_buf_puts(&log, oid_old);
git_buf_putc(&log, ' ');
@@ -54,68 +65,58 @@ static int reflog_write(const char *log_path, const char *oid_old,
git_buf_truncate(&log, log.size - 1); /* drop LF */
if (msg) {
- if (strchr(msg, '\n')) {
- git_buf_free(&log);
- return git__throw(GIT_ERROR, "Reflog message cannot contain newline");
- }
-
git_buf_putc(&log, '\t');
git_buf_puts(&log, msg);
}
- git_buf_putc(&log, '\n');
+ if (!trailing_newline)
+ git_buf_putc(&log, '\n');
- if ((error = git_buf_lasterror(&log)) < GIT_SUCCESS) {
+ if (git_buf_oom(&log)) {
git_buf_free(&log);
- return git__rethrow(error, "Failed to write reflog. Memory allocation failure");
+ return -1;
}
- if ((error = git_filebuf_open(&fbuf, log_path, GIT_FILEBUF_APPEND)) < GIT_SUCCESS) {
- git_buf_free(&log);
- return git__rethrow(error, "Failed to write reflog. Cannot open reflog `%s`", log_path);
+ error = git_filebuf_open(&fbuf, log_path, GIT_FILEBUF_APPEND);
+ if (!error) {
+ if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
+ git_filebuf_cleanup(&fbuf);
+ else
+ error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
}
- git_filebuf_write(&fbuf, log.ptr, log.size);
- error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
-
git_buf_free(&log);
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write reflog");
+ return error;
}
static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
{
- int error = GIT_SUCCESS;
const char *ptr;
git_reflog_entry *entry;
-#define seek_forward(_increase) { \
+#define seek_forward(_increase) do { \
if (_increase >= buf_size) { \
- if (entry->committer) \
- git__free(entry->committer); \
- git__free(entry); \
- return git__throw(GIT_ERROR, "Failed to seek forward. Buffer size exceeded"); \
+ giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \
+ goto fail; \
} \
buf += _increase; \
buf_size -= _increase; \
-}
+ } while (0)
while (buf_size > GIT_REFLOG_SIZE_MIN) {
entry = git__malloc(sizeof(git_reflog_entry));
- if (entry == NULL)
- return GIT_ENOMEM;
- entry->committer = NULL;
+ GITERR_CHECK_ALLOC(entry);
- if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < GIT_SUCCESS) {
- git__free(entry);
- return GIT_ERROR;
- }
+ entry->committer = git__malloc(sizeof(git_signature));
+ GITERR_CHECK_ALLOC(entry->committer);
+
+ if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
+ goto fail;
seek_forward(GIT_OID_HEXSZ + 1);
- if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < GIT_SUCCESS) {
- git__free(entry);
- return GIT_ERROR;
- }
+ if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
+ goto fail;
seek_forward(GIT_OID_HEXSZ + 1);
ptr = buf;
@@ -124,17 +125,8 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
while (*buf && *buf != '\t' && *buf != '\n')
seek_forward(1);
- entry->committer = git__malloc(sizeof(git_signature));
- if (entry->committer == NULL) {
- git__free(entry);
- return GIT_ENOMEM;
- }
-
- if ((error = git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf)) < GIT_SUCCESS) {
- git__free(entry->committer);
- git__free(entry);
- return git__rethrow(error, "Failed to parse reflog. Could not parse signature");
- }
+ if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
+ goto fail;
if (*buf == '\t') {
/* We got a message. Read everything till we reach LF. */
@@ -145,19 +137,27 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
seek_forward(1);
entry->msg = git__strndup(ptr, buf - ptr);
+ GITERR_CHECK_ALLOC(entry->msg);
} else
entry->msg = NULL;
while (*buf && *buf == '\n' && buf_size > 1)
seek_forward(1);
- if ((error = git_vector_insert(&log->entries, entry)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to parse reflog. Could not add new entry");
+ if (git_vector_insert(&log->entries, entry) < 0)
+ goto fail;
}
+ return 0;
+
#undef seek_forward
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse reflog");
+fail:
+ if (entry) {
+ git__free(entry->committer);
+ git__free(entry);
+ }
+ return -1;
}
void git_reflog_free(git_reflog *reflog)
@@ -188,27 +188,23 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref)
*reflog = NULL;
- if ((error = reflog_init(&log, ref)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read reflog. Cannot init reflog");
+ if (reflog_init(&log, ref) < 0)
+ return -1;
error = git_buf_join_n(&log_path, '/', 3,
ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
- if (error < GIT_SUCCESS)
- goto cleanup;
- if ((error = git_futils_readbuffer(&log_file, log_path.ptr)) < GIT_SUCCESS) {
- git__rethrow(error, "Failed to read reflog. Cannot read file `%s`", log_path.ptr);
- goto cleanup;
- }
+ if (!error)
+ error = git_futils_readbuffer(&log_file, log_path.ptr);
- if ((error = reflog_parse(log, log_file.ptr, log_file.size)) < GIT_SUCCESS)
- git__rethrow(error, "Failed to read reflog");
- else
- *reflog = log;
+ if (!error)
+ error = reflog_parse(log, log_file.ptr, log_file.size);
-cleanup:
- if (error != GIT_SUCCESS && log != NULL)
+ if (!error)
+ *reflog = log;
+ else
git_reflog_free(log);
+
git_buf_free(&log_file);
git_buf_free(&log_path);
@@ -225,16 +221,15 @@ int git_reflog_write(git_reference *ref, const git_oid *oid_old,
git_reference *r;
const git_oid *oid;
- if ((error = git_reference_resolve(&r, ref)) < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to write reflog. Cannot resolve reference `%s`", ref->name);
+ if ((error = git_reference_resolve(&r, ref)) < 0)
+ return error;
oid = git_reference_oid(r);
if (oid == NULL) {
- error = git__throw(GIT_ERROR,
+ giterr_set(GITERR_REFERENCE,
"Failed to write reflog. Cannot resolve reference `%s`", r->name);
git_reference_free(r);
- return error;
+ return -1;
}
git_oid_tostr(new, GIT_OID_HEXSZ+1, oid);
@@ -243,23 +238,21 @@ int git_reflog_write(git_reference *ref, const git_oid *oid_old,
error = git_buf_join_n(&log_path, '/', 3,
ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
- if (git_path_exists(log_path.ptr)) {
+ if (git_path_exists(log_path.ptr) == false) {
error = git_futils_mkpath2file(log_path.ptr, GIT_REFLOG_DIR_MODE);
- if (error < GIT_SUCCESS)
- git__rethrow(error,
- "Failed to write reflog. Cannot create reflog directory");
- } else if (git_path_isfile(log_path.ptr)) {
- error = git__throw(GIT_ERROR,
+ } else if (git_path_isfile(log_path.ptr) == false) {
+ giterr_set(GITERR_REFERENCE,
"Failed to write reflog. `%s` is directory", log_path.ptr);
+ error = -1;
} else if (oid_old == NULL) {
- error = git__throw(GIT_ERROR,
+ giterr_set(GITERR_REFERENCE,
"Failed to write reflog. Old OID cannot be NULL for existing reference");
+ error = -1;
}
-
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
if (oid_old)
@@ -280,13 +273,13 @@ int git_reflog_rename(git_reference *ref, const char *new_name)
git_buf old_path = GIT_BUF_INIT;
git_buf new_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))
+ 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 = GIT_ENOMEM;
+ error = -1;
git_buf_free(&old_path);
git_buf_free(&new_path);
@@ -296,13 +289,13 @@ int git_reflog_rename(git_reference *ref, const char *new_name)
int git_reflog_delete(git_reference *ref)
{
- int error = GIT_SUCCESS;
+ int error;
git_buf path = GIT_BUF_INIT;
- error = git_buf_join_n(&path, '/', 3,
- ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
+ error = git_buf_join_n(
+ &path, '/', 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
- if (error == GIT_SUCCESS && git_path_exists(path.ptr) == 0)
+ if (!error && git_path_exists(path.ptr))
error = p_unlink(path.ptr);
git_buf_free(&path);
diff --git a/src/refs.c b/src/refs.c
index f3388bf53..28e8f786b 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -15,7 +15,10 @@
#include <git2/tag.h>
#include <git2/object.h>
-#define MAX_NESTING_LEVEL 5
+GIT__USE_STRMAP;
+
+#define DEFAULT_NESTING_LEVEL 5
+#define MAX_NESTING_LEVEL 10
enum {
GIT_PACKREF_HAS_PEEL = 1,
@@ -29,8 +32,6 @@ struct packref {
char name[GIT_FLEX_ARRAY];
};
-static const int default_table_size = 32;
-
static int reference_read(
git_buf *file_content,
time_t *mtime,
@@ -61,7 +62,7 @@ static int packed_lookup(git_reference *ref);
static int packed_write(git_repository *repo);
/* internal helpers */
-static int reference_available(git_repository *repo,
+static int reference_path_available(git_repository *repo,
const char *ref, const char *old_ref);
static int reference_delete(git_reference *ref);
static int reference_lookup(git_reference *ref);
@@ -97,98 +98,100 @@ static int reference_alloc(
assert(ref_out && repo && name);
reference = git__malloc(sizeof(git_reference));
- if (reference == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(reference);
memset(reference, 0x0, sizeof(git_reference));
reference->owner = repo;
reference->name = git__strdup(name);
- if (reference->name == NULL) {
- git__free(reference);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(reference->name);
*ref_out = reference;
- return GIT_SUCCESS;
+ return 0;
}
-static int reference_read(git_buf *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated)
+static int reference_read(
+ git_buf *file_content,
+ time_t *mtime,
+ const char *repo_path,
+ const char *ref_name,
+ int *updated)
{
git_buf path = GIT_BUF_INIT;
- int error = GIT_SUCCESS;
+ int result;
assert(file_content && repo_path && ref_name);
/* Determine the full path of the file */
- if ((error = git_buf_joinpath(&path, repo_path, ref_name)) == GIT_SUCCESS)
- error = git_futils_readbuffer_updated(file_content, path.ptr, mtime, updated);
+ if (git_buf_joinpath(&path, repo_path, ref_name) < 0)
+ return -1;
+ result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, updated);
git_buf_free(&path);
-
- return error;
+ return result;
}
static int loose_parse_symbolic(git_reference *ref, git_buf *file_content)
{
- const unsigned int header_len = strlen(GIT_SYMREF);
+ const unsigned int header_len = (unsigned int)strlen(GIT_SYMREF);
const char *refname_start;
char *eol;
refname_start = (const char *)file_content->ptr;
- if (file_content->size < (header_len + 1))
- return git__throw(GIT_EOBJCORRUPTED,
- "Failed to parse loose reference. Object too short");
+ if (git_buf_len(file_content) < header_len + 1)
+ goto corrupt;
/*
* Assume we have already checked for the header
* before calling this function
*/
-
refname_start += header_len;
ref->target.symbolic = git__strdup(refname_start);
- if (ref->target.symbolic == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(ref->target.symbolic);
/* remove newline at the end of file */
eol = strchr(ref->target.symbolic, '\n');
if (eol == NULL)
- return git__throw(GIT_EOBJCORRUPTED,
- "Failed to parse loose reference. Missing EOL");
+ goto corrupt;
*eol = '\0';
if (eol[-1] == '\r')
eol[-1] = '\0';
- return GIT_SUCCESS;
+ return 0;
+
+corrupt:
+ giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
+ return -1;
}
static int loose_parse_oid(git_oid *oid, git_buf *file_content)
{
- int error;
char *buffer;
buffer = (char *)file_content->ptr;
/* File format: 40 chars (OID) + newline */
- if (file_content->size < GIT_OID_HEXSZ + 1)
- return git__throw(GIT_EOBJCORRUPTED,
- "Failed to parse loose reference. Reference too short");
+ if (git_buf_len(file_content) < GIT_OID_HEXSZ + 1)
+ goto corrupt;
- if ((error = git_oid_fromstr(oid, buffer)) < GIT_SUCCESS)
- return git__rethrow(GIT_EOBJCORRUPTED, "Failed to parse loose reference.");
+ if (git_oid_fromstr(oid, buffer) < 0)
+ goto corrupt;
buffer = buffer + GIT_OID_HEXSZ;
if (*buffer == '\r')
buffer++;
if (*buffer != '\n')
- return git__throw(GIT_EOBJCORRUPTED,
- "Failed to parse loose reference. Missing EOL");
+ goto corrupt;
- return GIT_SUCCESS;
+ return 0;
+
+corrupt:
+ giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
+ return -1;
}
static git_rtype loose_guess_rtype(const git_buf *full_path)
@@ -198,7 +201,7 @@ static git_rtype loose_guess_rtype(const git_buf *full_path)
type = GIT_REF_INVALID;
- if (git_futils_readbuffer(&ref_file, full_path->ptr) == GIT_SUCCESS) {
+ if (git_futils_readbuffer(&ref_file, full_path->ptr) == 0) {
if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0)
type = GIT_REF_SYMBOLIC;
else
@@ -211,15 +214,17 @@ static git_rtype loose_guess_rtype(const git_buf *full_path)
static int loose_lookup(git_reference *ref)
{
- int error = GIT_SUCCESS, updated;
+ int result, updated;
git_buf ref_file = GIT_BUF_INIT;
- if (reference_read(&ref_file, &ref->mtime,
- ref->owner->path_repository, ref->name, &updated) < GIT_SUCCESS)
- return git__throw(GIT_ENOTFOUND, "Failed to lookup loose reference");
+ result = reference_read(&ref_file, &ref->mtime,
+ ref->owner->path_repository, ref->name, &updated);
+
+ if (result < 0)
+ return result;
if (!updated)
- return GIT_SUCCESS;
+ return 0;
if (ref->flags & GIT_REF_SYMBOLIC) {
git__free(ref->target.symbolic);
@@ -230,18 +235,14 @@ static int loose_lookup(git_reference *ref)
if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) {
ref->flags |= GIT_REF_SYMBOLIC;
- error = loose_parse_symbolic(ref, &ref_file);
+ result = loose_parse_symbolic(ref, &ref_file);
} else {
ref->flags |= GIT_REF_OID;
- error = loose_parse_oid(&ref->target.oid, &ref_file);
+ result = loose_parse_oid(&ref->target.oid, &ref_file);
}
git_buf_free(&ref_file);
-
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup loose reference");
-
- return GIT_SUCCESS;
+ return result;
}
static int loose_lookup_to_packfile(
@@ -249,54 +250,59 @@ static int loose_lookup_to_packfile(
git_repository *repo,
const char *name)
{
- int error = GIT_SUCCESS;
git_buf ref_file = GIT_BUF_INIT;
struct packref *ref = NULL;
size_t name_len;
*ref_out = NULL;
- error = reference_read(&ref_file, NULL, repo->path_repository, name, NULL);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (reference_read(&ref_file, NULL, repo->path_repository, name, NULL) < 0)
+ return -1;
name_len = strlen(name);
ref = git__malloc(sizeof(struct packref) + name_len + 1);
+ GITERR_CHECK_ALLOC(ref);
memcpy(ref->name, name, name_len);
ref->name[name_len] = 0;
- error = loose_parse_oid(&ref->oid, &ref_file);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (loose_parse_oid(&ref->oid, &ref_file) < 0) {
+ git_buf_free(&ref_file);
+ git__free(ref);
+ return -1;
+ }
ref->flags = GIT_PACKREF_WAS_LOOSE;
*ref_out = ref;
git_buf_free(&ref_file);
- return GIT_SUCCESS;
-
-cleanup:
- git_buf_free(&ref_file);
- git__free(ref);
-
- return git__rethrow(error, "Failed to lookup loose reference");
+ return 0;
}
static int loose_write(git_reference *ref)
{
git_filebuf file = GIT_FILEBUF_INIT;
git_buf ref_path = GIT_BUF_INIT;
- int error;
struct stat st;
- error = git_buf_joinpath(&ref_path, ref->owner->path_repository, ref->name);
- if (error < GIT_SUCCESS)
- goto unlock;
+ if (git_buf_joinpath(&ref_path, ref->owner->path_repository, ref->name) < 0)
+ return -1;
+
+ /* Remove a possibly existing empty directory hierarchy
+ * which name would collide with the reference name
+ */
+ if (git_path_isdir(git_buf_cstr(&ref_path)) &&
+ (git_futils_rmdir_r(git_buf_cstr(&ref_path), GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0)) {
+ git_buf_free(&ref_path);
+ return -1;
+ }
+
+ if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE) < 0) {
+ git_buf_free(&ref_path);
+ return -1;
+ }
- error = git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE);
- if (error < GIT_SUCCESS)
- goto unlock;
+ git_buf_free(&ref_path);
if (ref->flags & GIT_REF_OID) {
char oid[GIT_OID_HEXSZ + 1];
@@ -304,29 +310,18 @@ static int loose_write(git_reference *ref)
git_oid_fmt(oid, &ref->target.oid);
oid[GIT_OID_HEXSZ] = '\0';
- error = git_filebuf_printf(&file, "%s\n", oid);
- if (error < GIT_SUCCESS)
- goto unlock;
+ git_filebuf_printf(&file, "%s\n", oid);
- } else if (ref->flags & GIT_REF_SYMBOLIC) { /* GIT_REF_SYMBOLIC */
- error = git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic);
+ } else if (ref->flags & GIT_REF_SYMBOLIC) {
+ git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic);
} else {
- error = git__throw(GIT_EOBJCORRUPTED,
- "Failed to write reference. Invalid reference type");
- goto unlock;
+ assert(0); /* don't let this happen */
}
- if (p_stat(ref_path.ptr, &st) == GIT_SUCCESS)
+ if (p_stat(ref_path.ptr, &st) == 0)
ref->mtime = st.st_mtime;
- git_buf_free(&ref_path);
-
return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);
-
-unlock:
- git_buf_free(&ref_path);
- git_filebuf_cleanup(&file);
- return git__rethrow(error, "Failed to write loose reference");
}
static int packed_parse_peel(
@@ -340,34 +335,32 @@ static int packed_parse_peel(
/* Ensure it's not the first entry of the file */
if (tag_ref == NULL)
- return git__throw(GIT_EPACKEDREFSCORRUPTED,
- "Failed to parse packed reference. "
- "Reference is the first entry of the file");
+ goto corrupt;
/* Ensure reference is a tag */
if (git__prefixcmp(tag_ref->name, GIT_REFS_TAGS_DIR) != 0)
- return git__throw(GIT_EPACKEDREFSCORRUPTED,
- "Failed to parse packed reference. Reference is not a tag");
+ goto corrupt;
if (buffer + GIT_OID_HEXSZ >= buffer_end)
- return git__throw(GIT_EPACKEDREFSCORRUPTED,
- "Failed to parse packed reference. Buffer too small");
+ goto corrupt;
/* Is this a valid object id? */
- if (git_oid_fromstr(&tag_ref->peel, buffer) < GIT_SUCCESS)
- return git__throw(GIT_EPACKEDREFSCORRUPTED,
- "Failed to parse packed reference. Not a valid object ID");
+ if (git_oid_fromstr(&tag_ref->peel, buffer) < 0)
+ goto corrupt;
buffer = buffer + GIT_OID_HEXSZ;
if (*buffer == '\r')
buffer++;
if (*buffer != '\n')
- return git__throw(GIT_EPACKEDREFSCORRUPTED,
- "Failed to parse packed reference. Buffer not terminated correctly");
+ goto corrupt;
*buffer_out = buffer + 1;
- return GIT_SUCCESS;
+ return 0;
+
+corrupt:
+ giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
+ return -1;
}
static int packed_parse_oid(
@@ -380,26 +373,20 @@ static int packed_parse_oid(
const char *buffer = *buffer_out;
const char *refname_begin, *refname_end;
- int error = GIT_SUCCESS;
size_t refname_len;
git_oid id;
refname_begin = (buffer + GIT_OID_HEXSZ + 1);
- if (refname_begin >= buffer_end ||
- refname_begin[-1] != ' ') {
- error = GIT_EPACKEDREFSCORRUPTED;
- goto cleanup;
- }
+ if (refname_begin >= buffer_end || refname_begin[-1] != ' ')
+ goto corrupt;
/* Is this a valid object id? */
- if ((error = git_oid_fromstr(&id, buffer)) < GIT_SUCCESS)
- goto cleanup;
+ if (git_oid_fromstr(&id, buffer) < 0)
+ goto corrupt;
refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin);
- if (refname_end == NULL) {
- error = GIT_EPACKEDREFSCORRUPTED;
- goto cleanup;
- }
+ if (refname_end == NULL)
+ goto corrupt;
if (refname_end[-1] == '\r')
refname_end--;
@@ -407,6 +394,7 @@ static int packed_parse_oid(
refname_len = refname_end - refname_begin;
ref = git__malloc(sizeof(struct packref) + refname_len + 1);
+ GITERR_CHECK_ALLOC(ref);
memcpy(ref->name, refname_begin, refname_len);
ref->name[refname_len] = 0;
@@ -418,32 +406,28 @@ static int packed_parse_oid(
*ref_out = ref;
*buffer_out = refname_end + 1;
- return GIT_SUCCESS;
+ return 0;
-cleanup:
+corrupt:
git__free(ref);
- return git__rethrow(error, "Failed to parse OID of packed reference");
+ giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
+ return -1;
}
static int packed_load(git_repository *repo)
{
- int error = GIT_SUCCESS, updated;
+ int result, updated;
git_buf packfile = GIT_BUF_INIT;
const char *buffer_start, *buffer_end;
git_refcache *ref_cache = &repo->references;
/* First we make sure we have allocated the hash table */
if (ref_cache->packfile == NULL) {
- ref_cache->packfile = git_hashtable_alloc(
- default_table_size, git_hash__strhash_cb, git_hash__strcmp_cb);
-
- if (ref_cache->packfile == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ ref_cache->packfile = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(ref_cache->packfile);
}
- error = reference_read(&packfile, &ref_cache->packfile_time,
+ result = reference_read(&packfile, &ref_cache->packfile_time,
repo->path_repository, GIT_PACKEDREFS_FILE, &updated);
/*
@@ -453,62 +437,59 @@ static int packed_load(git_repository *repo)
* for us here, so just return. Anything else means we need to
* refresh the packed refs.
*/
- if (error == GIT_ENOTFOUND) {
- git_hashtable_clear(ref_cache->packfile);
- return GIT_SUCCESS;
- } else if (error < GIT_SUCCESS) {
- return git__rethrow(error, "Failed to read packed refs");
- } else if (!updated) {
- return GIT_SUCCESS;
+ if (result == GIT_ENOTFOUND) {
+ git_strmap_clear(ref_cache->packfile);
+ return 0;
}
+ if (result < 0)
+ return -1;
+
+ if (!updated)
+ return 0;
+
/*
* At this point, we want to refresh the packed refs. We already
* have the contents in our buffer.
*/
-
- git_hashtable_clear(ref_cache->packfile);
+ git_strmap_clear(ref_cache->packfile);
buffer_start = (const char *)packfile.ptr;
buffer_end = (const char *)(buffer_start) + packfile.size;
while (buffer_start < buffer_end && buffer_start[0] == '#') {
buffer_start = strchr(buffer_start, '\n');
- if (buffer_start == NULL) {
- error = GIT_EPACKEDREFSCORRUPTED;
- goto cleanup;
- }
+ if (buffer_start == NULL)
+ goto parse_failed;
+
buffer_start++;
}
while (buffer_start < buffer_end) {
+ int err;
struct packref *ref = NULL;
- error = packed_parse_oid(&ref, &buffer_start, buffer_end);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (packed_parse_oid(&ref, &buffer_start, buffer_end) < 0)
+ goto parse_failed;
if (buffer_start[0] == '^') {
- error = packed_parse_peel(ref, &buffer_start, buffer_end);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (packed_parse_peel(ref, &buffer_start, buffer_end) < 0)
+ goto parse_failed;
}
- error = git_hashtable_insert(ref_cache->packfile, ref->name, ref);
- if (error < GIT_SUCCESS) {
- git__free(ref);
- goto cleanup;
- }
+ git_strmap_insert(ref_cache->packfile, ref->name, ref, err);
+ if (err < 0)
+ goto parse_failed;
}
git_buf_free(&packfile);
- return GIT_SUCCESS;
+ return 0;
-cleanup:
- git_hashtable_free(ref_cache->packfile);
+parse_failed:
+ git_strmap_free(ref_cache->packfile);
ref_cache->packfile = NULL;
git_buf_free(&packfile);
- return git__rethrow(error, "Failed to load packed references");
+ return -1;
}
@@ -526,17 +507,17 @@ static int _dirent_loose_listall(void *_data, git_buf *full_path)
struct dirent_list_data *data = (struct dirent_list_data *)_data;
const char *file_path = full_path->ptr + data->repo_path_len;
- if (git_path_isdir(full_path->ptr) == GIT_SUCCESS)
+ if (git_path_isdir(full_path->ptr) == true)
return git_path_direach(full_path, _dirent_loose_listall, _data);
/* do not add twice a reference that exists already in the packfile */
if ((data->list_flags & GIT_REF_PACKED) != 0 &&
- git_hashtable_lookup(data->repo->references.packfile, file_path) != NULL)
- return GIT_SUCCESS;
+ git_strmap_exists(data->repo->references.packfile, file_path))
+ return 0;
if (data->list_flags != GIT_REF_LISTALL) {
if ((data->list_flags & loose_guess_rtype(full_path)) == 0)
- return GIT_SUCCESS; /* we are filtering out this reference */
+ return 0; /* we are filtering out this reference */
}
return data->callback(file_path, data->callback_payload);
@@ -548,30 +529,25 @@ static int _dirent_loose_load(void *data, git_buf *full_path)
void *old_ref = NULL;
struct packref *ref;
const char *file_path;
- int error;
+ int err;
- if (git_path_isdir(full_path->ptr) == GIT_SUCCESS)
+ if (git_path_isdir(full_path->ptr) == true)
return git_path_direach(full_path, _dirent_loose_load, repository);
file_path = full_path->ptr + strlen(repository->path_repository);
- error = loose_lookup_to_packfile(&ref, repository, file_path);
- if (error == GIT_SUCCESS) {
+ if (loose_lookup_to_packfile(&ref, repository, file_path) < 0)
+ return -1;
- if (git_hashtable_insert2(
- repository->references.packfile,
- ref->name, ref, &old_ref) < GIT_SUCCESS) {
- git__free(ref);
- return GIT_ENOMEM;
- }
-
- if (old_ref != NULL)
- git__free(old_ref);
+ git_strmap_insert2(
+ repository->references.packfile, ref->name, ref, old_ref, err);
+ if (err < 0) {
+ git__free(ref);
+ return -1;
}
- return error == GIT_SUCCESS ?
- GIT_SUCCESS :
- git__rethrow(error, "Failed to load loose references into packfile");
+ git__free(old_ref);
+ return 0;
}
/*
@@ -582,24 +558,24 @@ static int _dirent_loose_load(void *data, git_buf *full_path)
*/
static int packed_loadloose(git_repository *repository)
{
- int error = GIT_SUCCESS;
git_buf refs_path = GIT_BUF_INIT;
+ int result;
/* the packfile must have been previously loaded! */
assert(repository->references.packfile);
- if ((error = git_buf_joinpath(&refs_path,
- repository->path_repository, GIT_REFS_DIR)) < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&refs_path, repository->path_repository, GIT_REFS_DIR) < 0)
+ return -1;
/*
* Load all the loose files from disk into the Packfile table.
* This will overwrite any old packed entries with their
* updated loose versions
*/
- error = git_path_direach(&refs_path, _dirent_loose_load, repository);
+ result = git_path_direach(&refs_path, _dirent_loose_load, repository);
git_buf_free(&refs_path);
- return error;
+
+ return result;
}
/*
@@ -607,7 +583,6 @@ static int packed_loadloose(git_repository *repository)
*/
static int packed_write_ref(struct packref *ref, git_filebuf *file)
{
- int error;
char oid[GIT_OID_HEXSZ + 1];
git_oid_fmt(oid, &ref->oid);
@@ -628,14 +603,14 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file)
git_oid_fmt(peel, &ref->peel);
peel[GIT_OID_HEXSZ] = 0;
- error = git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel);
+ if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
+ return -1;
} else {
- error = git_filebuf_printf(file, "%s %s\n", oid, ref->name);
+ if (git_filebuf_printf(file, "%s %s\n", oid, ref->name) < 0)
+ return -1;
}
- return error == GIT_SUCCESS ?
- GIT_SUCCESS :
- git__rethrow(error, "Failed to write packed reference");
+ return 0;
}
/*
@@ -649,24 +624,22 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file)
static int packed_find_peel(git_repository *repo, struct packref *ref)
{
git_object *object;
- int error;
if (ref->flags & GIT_PACKREF_HAS_PEEL)
- return GIT_SUCCESS;
+ return 0;
/*
* Only applies to tags, i.e. references
* in the /refs/tags folder
*/
if (git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) != 0)
- return GIT_SUCCESS;
+ return 0;
/*
* Find the tagged object in the repository
*/
- error = git_object_lookup(&object, repo, &ref->oid, GIT_OBJ_ANY);
- if (error < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to find packed reference");
+ if (git_object_lookup(&object, repo, &ref->oid, GIT_OBJ_ANY) < 0)
+ return -1;
/*
* If the tagged object is a Tag object, we need to resolve it;
@@ -690,7 +663,7 @@ static int packed_find_peel(git_repository *repo, struct packref *ref)
}
git_object_free(object);
- return GIT_SUCCESS;
+ return 0;
}
/*
@@ -708,25 +681,27 @@ static int packed_remove_loose(git_repository *repo, git_vector *packing_list)
{
unsigned int i;
git_buf full_path = GIT_BUF_INIT;
- int error = GIT_SUCCESS;
+ int failed = 0;
for (i = 0; i < packing_list->length; ++i) {
struct packref *ref = git_vector_get(packing_list, i);
- int an_error;
if ((ref->flags & GIT_PACKREF_WAS_LOOSE) == 0)
continue;
- an_error = git_buf_joinpath(&full_path, repo->path_repository, ref->name);
+ if (git_buf_joinpath(&full_path, repo->path_repository, ref->name) < 0)
+ return -1; /* critical; do not try to recover on oom */
- if (an_error == GIT_SUCCESS &&
- git_path_exists(full_path.ptr) == GIT_SUCCESS &&
- p_unlink(full_path.ptr) < GIT_SUCCESS)
- an_error = GIT_EOSERR;
+ if (git_path_exists(full_path.ptr) == true && p_unlink(full_path.ptr) < 0) {
+ if (failed)
+ continue;
- /* keep the error if we haven't seen one yet */
- if (error > an_error)
- error = an_error;
+ giterr_set(GITERR_REFERENCE,
+ "Failed to remove loose reference '%s' after packing: %s",
+ full_path.ptr, strerror(errno));
+
+ failed = 1;
+ }
/*
* if we fail to remove a single file, this is *not* good,
@@ -737,10 +712,7 @@ static int packed_remove_loose(git_repository *repo, git_vector *packing_list)
}
git_buf_free(&full_path);
-
- return error == GIT_SUCCESS ?
- GIT_SUCCESS :
- git__rethrow(error, "Failed to remove loose packed reference");
+ return failed ? -1 : 0;
}
static int packed_sort(const void *a, const void *b)
@@ -757,176 +729,228 @@ static int packed_sort(const void *a, const void *b)
static int packed_write(git_repository *repo)
{
git_filebuf pack_file = GIT_FILEBUF_INIT;
- int error;
- const char *errmsg = "Failed to write packed references file";
unsigned int i;
git_buf pack_file_path = GIT_BUF_INIT;
git_vector packing_list;
- size_t total_refs;
+ unsigned int total_refs;
assert(repo && repo->references.packfile);
- total_refs = repo->references.packfile->key_count;
- if ((error =
- git_vector_init(&packing_list, total_refs, packed_sort)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to init packed references list");
+ total_refs =
+ (unsigned int)git_strmap_num_entries(repo->references.packfile);
+
+ if (git_vector_init(&packing_list, total_refs, packed_sort) < 0)
+ return -1;
/* Load all the packfile into a vector */
{
struct packref *reference;
- GIT_HASHTABLE_FOREACH_VALUE(repo->references.packfile, reference,
- /* cannot fail: vector already has the right size */
+ /* cannot fail: vector already has the right size */
+ git_strmap_foreach_value(repo->references.packfile, reference, {
git_vector_insert(&packing_list, reference);
- );
+ });
}
/* sort the vector so the entries appear sorted on the packfile */
git_vector_sort(&packing_list);
/* Now we can open the file! */
- error = git_buf_joinpath(&pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_buf_joinpath(&pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE) < 0)
+ goto cleanup_memory;
- if ((error = git_filebuf_open(&pack_file, pack_file_path.ptr, 0)) < GIT_SUCCESS) {
- errmsg = "Failed to open packed references file";
- goto cleanup;
- }
+ if (git_filebuf_open(&pack_file, pack_file_path.ptr, 0) < 0)
+ goto cleanup_packfile;
/* Packfiles have a header... apparently
* This is in fact not required, but we might as well print it
* just for kicks */
- if ((error =
- git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < GIT_SUCCESS) {
- errmsg = "Failed to write packed references file header";
- goto cleanup;
- }
+ if (git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER) < 0)
+ goto cleanup_packfile;
for (i = 0; i < packing_list.length; ++i) {
struct packref *ref = (struct packref *)git_vector_get(&packing_list, i);
- if ((error = packed_find_peel(repo, ref)) < GIT_SUCCESS) {
- error = git__throw(GIT_EOBJCORRUPTED,
- "A reference cannot be peeled");
- goto cleanup;
- }
+ if (packed_find_peel(repo, ref) < 0)
+ goto cleanup_packfile;
- if ((error = packed_write_ref(ref, &pack_file)) < GIT_SUCCESS)
- goto cleanup;
+ if (packed_write_ref(ref, &pack_file) < 0)
+ goto cleanup_packfile;
}
-cleanup:
/* if we've written all the references properly, we can commit
* the packfile to make the changes effective */
- if (error == GIT_SUCCESS) {
- error = git_filebuf_commit(&pack_file, GIT_PACKEDREFS_FILE_MODE);
+ if (git_filebuf_commit(&pack_file, GIT_PACKEDREFS_FILE_MODE) < 0)
+ goto cleanup_memory;
- /* when and only when the packfile has been properly written,
- * we can go ahead and remove the loose refs */
- if (error == GIT_SUCCESS) {
- struct stat st;
+ /* when and only when the packfile has been properly written,
+ * we can go ahead and remove the loose refs */
+ if (packed_remove_loose(repo, &packing_list) < 0)
+ goto cleanup_memory;
- error = packed_remove_loose(repo, &packing_list);
-
- if (p_stat(pack_file_path.ptr, &st) == GIT_SUCCESS)
- repo->references.packfile_time = st.st_mtime;
- }
- }
- else git_filebuf_cleanup(&pack_file);
+ {
+ struct stat st;
+ if (p_stat(pack_file_path.ptr, &st) == 0)
+ repo->references.packfile_time = st.st_mtime;
+ }
git_vector_free(&packing_list);
git_buf_free(&pack_file_path);
- if (error < GIT_SUCCESS)
- git__rethrow(error, "%s", errmsg);
+ /* we're good now */
+ return 0;
+
+cleanup_packfile:
+ git_filebuf_cleanup(&pack_file);
+
+cleanup_memory:
+ git_vector_free(&packing_list);
+ git_buf_free(&pack_file_path);
- return error;
+ return -1;
}
+struct reference_available_t {
+ const char *new_ref;
+ const char *old_ref;
+ int available;
+};
+
static int _reference_available_cb(const char *ref, void *data)
{
- const char *new, *old;
- const char **refs;
+ struct reference_available_t *d;
assert(ref && data);
+ d = (struct reference_available_t *)data;
- refs = (const char **)data;
-
- new = (const char *)refs[0];
- old = (const char *)refs[1];
+ if (!d->old_ref || strcmp(d->old_ref, ref)) {
+ size_t reflen = strlen(ref);
+ size_t newlen = strlen(d->new_ref);
+ size_t cmplen = reflen < newlen ? reflen : newlen;
+ const char *lead = reflen < newlen ? d->new_ref : ref;
- if (!old || strcmp(old, ref)) {
- int reflen = strlen(ref);
- int newlen = strlen(new);
- int cmplen = reflen < newlen ? reflen : newlen;
- const char *lead = reflen < newlen ? new : ref;
-
- if (!strncmp(new, ref, cmplen) &&
- lead[cmplen] == '/')
- return GIT_EEXISTS;
+ if (!strncmp(d->new_ref, ref, cmplen) && lead[cmplen] == '/') {
+ d->available = 0;
+ return -1;
+ }
}
- return GIT_SUCCESS;
+ return 0;
}
-static int reference_available(
+static int reference_path_available(
git_repository *repo,
const char *ref,
const char* old_ref)
{
- const char *refs[2];
+ struct reference_available_t data;
- refs[0] = ref;
- refs[1] = old_ref;
+ data.new_ref = ref;
+ data.old_ref = old_ref;
+ data.available = 1;
if (git_reference_foreach(repo, GIT_REF_LISTALL,
- _reference_available_cb, (void *)refs) < 0) {
- return git__throw(GIT_EEXISTS,
- "Reference name `%s` conflicts with existing reference", ref);
+ _reference_available_cb, (void *)&data) < 0)
+ return -1;
+
+ if (!data.available) {
+ giterr_set(GITERR_REFERENCE,
+ "The path to reference '%s' collides with an existing one", ref);
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
static int reference_exists(int *exists, git_repository *repo, const char *ref_name)
{
- int error;
git_buf ref_path = GIT_BUF_INIT;
- error = packed_load(repo);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Cannot resolve if a reference exists");
+ if (packed_load(repo) < 0)
+ return -1;
- error = git_buf_joinpath(&ref_path, repo->path_repository, ref_name);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Cannot resolve if a reference exists");
+ if (git_buf_joinpath(&ref_path, repo->path_repository, ref_name) < 0)
+ return -1;
- if (git_path_isfile(ref_path.ptr) == GIT_SUCCESS ||
- git_hashtable_lookup(repo->references.packfile, ref_path.ptr) != NULL) {
+ if (git_path_isfile(ref_path.ptr) == true ||
+ git_strmap_exists(repo->references.packfile, ref_path.ptr))
+ {
*exists = 1;
} else {
*exists = 0;
}
git_buf_free(&ref_path);
+ return 0;
+}
+
+/*
+ * Check if a reference could be written to disk, based on:
+ *
+ * - Whether a reference with the same name already exists,
+ * and we are allowing or disallowing overwrites
+ *
+ * - Whether the name of the reference would collide with
+ * an existing path
+ */
+static int reference_can_write(
+ git_repository *repo,
+ const char *refname,
+ const char *previous_name,
+ int force)
+{
+ /* see if the reference shares a path with an existing reference;
+ * if a path is shared, we cannot create the reference, even when forcing */
+ if (reference_path_available(repo, refname, previous_name) < 0)
+ return -1;
+
+ /* check if the reference actually exists, but only if we are not forcing
+ * the rename. If we are forcing, it's OK to overwrite */
+ if (!force) {
+ int exists;
+
+ if (reference_exists(&exists, repo, refname) < 0)
+ return -1;
+
+ /* We cannot proceed if the reference already exists and we're not forcing
+ * the rename; the existing one would be overwritten */
+ if (exists) {
+ giterr_set(GITERR_REFERENCE,
+ "A reference with that name (%s) already exists", refname);
+ return GIT_EEXISTS;
+ }
+ }
- return GIT_SUCCESS;
+ /* FIXME: if the reference exists and we are forcing, do we really need to
+ * remove the reference first?
+ *
+ * Two cases:
+ *
+ * - the reference already exists and is loose: not a problem, the file
+ * gets overwritten on disk
+ *
+ * - the reference already exists and is packed: we write a new one as
+ * loose, which by all means renders the packed one useless
+ */
+
+ return 0;
}
+
static int packed_lookup(git_reference *ref)
{
- int error;
struct packref *pack_ref = NULL;
+ git_strmap *packfile_refs;
+ khiter_t pos;
- error = packed_load(ref->owner);
- if (error < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to lookup reference from packfile");
+ if (packed_load(ref->owner) < 0)
+ return -1;
- if (ref->flags & GIT_REF_PACKED &&
+ /* maybe the packfile hasn't changed at all, so we don't
+ * have to re-lookup the reference */
+ if ((ref->flags & GIT_REF_PACKED) &&
ref->mtime == ref->owner->references.packfile_time)
- return GIT_SUCCESS;
+ return 0;
if (ref->flags & GIT_REF_SYMBOLIC) {
git__free(ref->target.symbolic);
@@ -934,39 +958,42 @@ static int packed_lookup(git_reference *ref)
}
/* Look up on the packfile */
- pack_ref = git_hashtable_lookup(ref->owner->references.packfile, ref->name);
- if (pack_ref == NULL)
- return git__throw(GIT_ENOTFOUND,
- "Failed to lookup reference from packfile");
+ packfile_refs = ref->owner->references.packfile;
+ pos = git_strmap_lookup_index(packfile_refs, ref->name);
+ if (!git_strmap_valid_index(packfile_refs, pos)) {
+ giterr_set(GITERR_REFERENCE, "Reference '%s' not found", ref->name);
+ return GIT_ENOTFOUND;
+ }
+
+ pack_ref = git_strmap_value_at(packfile_refs, pos);
ref->flags = GIT_REF_OID | GIT_REF_PACKED;
ref->mtime = ref->owner->references.packfile_time;
git_oid_cpy(&ref->target.oid, &pack_ref->oid);
- return GIT_SUCCESS;
+ return 0;
}
static int reference_lookup(git_reference *ref)
{
- int error_loose, error_packed;
+ int result;
- error_loose = loose_lookup(ref);
- if (error_loose == GIT_SUCCESS)
- return GIT_SUCCESS;
+ result = loose_lookup(ref);
+ if (result == 0)
+ return 0;
- error_packed = packed_lookup(ref);
- if (error_packed == GIT_SUCCESS)
- return GIT_SUCCESS;
+ /* only try to lookup this reference on the packfile if it
+ * wasn't found on the loose refs; not if there was a critical error */
+ if (result == GIT_ENOTFOUND) {
+ giterr_clear();
+ result = packed_lookup(ref);
+ if (result == 0)
+ return 0;
+ }
+ /* unexpected error; free the reference */
git_reference_free(ref);
-
- if (error_loose != GIT_ENOTFOUND)
- return git__rethrow(error_loose, "Failed to lookup reference");
-
- if (error_packed != GIT_ENOTFOUND)
- return git__rethrow(error_packed, "Failed to lookup reference");
-
- return git__throw(GIT_ENOTFOUND, "Reference not found");
+ return result;
}
/*
@@ -976,7 +1003,7 @@ static int reference_lookup(git_reference *ref)
*/
static int reference_delete(git_reference *ref)
{
- int error;
+ int result;
assert(ref);
@@ -984,17 +1011,28 @@ static int reference_delete(git_reference *ref)
* We need to reload the packfile, remove the reference from the
* packing list, and repack */
if (ref->flags & GIT_REF_PACKED) {
+ git_strmap *packfile_refs;
struct packref *packref;
+ khiter_t pos;
+
/* load the existing packfile */
- if ((error = packed_load(ref->owner)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to delete reference");
+ if (packed_load(ref->owner) < 0)
+ return -1;
+
+ packfile_refs = ref->owner->references.packfile;
+ pos = git_strmap_lookup_index(packfile_refs, ref->name);
+ if (!git_strmap_valid_index(packfile_refs, pos)) {
+ giterr_set(GITERR_REFERENCE,
+ "Reference %s stopped existing in the packfile", ref->name);
+ return -1;
+ }
- if (git_hashtable_remove2(ref->owner->references.packfile,
- ref->name, (void **) &packref) < GIT_SUCCESS)
- return git__throw(GIT_ENOTFOUND, "Reference not found");
+ packref = git_strmap_value_at(packfile_refs, pos);
+ git_strmap_delete_at(packfile_refs, pos);
- git__free (packref);
- error = packed_write(ref->owner);
+ git__free(packref);
+ if (packed_write(ref->owner) < 0)
+ return -1;
/* If the reference is loose, we can just remove the reference
* from the filesystem */
@@ -1002,66 +1040,114 @@ static int reference_delete(git_reference *ref)
git_reference *ref_in_pack;
git_buf full_path = GIT_BUF_INIT;
- error = git_buf_joinpath(&full_path, ref->owner->path_repository, ref->name);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_buf_joinpath(&full_path, ref->owner->path_repository, ref->name) < 0)
+ return -1;
- error = p_unlink(full_path.ptr);
+ result = p_unlink(full_path.ptr);
git_buf_free(&full_path); /* done with path at this point */
- if (error < GIT_SUCCESS)
- goto cleanup;
+
+ if (result < 0) {
+ giterr_set(GITERR_OS, "Failed to unlink '%s'", full_path.ptr);
+ return -1;
+ }
/* When deleting a loose reference, we have to ensure that an older
* packed version of it doesn't exist */
- if (git_reference_lookup(&ref_in_pack, ref->owner,
- ref->name) == GIT_SUCCESS) {
+ if (git_reference_lookup(&ref_in_pack, ref->owner, ref->name) == 0) {
assert((ref_in_pack->flags & GIT_REF_PACKED) != 0);
- error = git_reference_delete(ref_in_pack);
+ return git_reference_delete(ref_in_pack);
}
+
+ giterr_clear();
}
-cleanup:
- return error == GIT_SUCCESS ?
- GIT_SUCCESS :
- git__rethrow(error, "Failed to delete reference");
+ return 0;
}
int git_reference_delete(git_reference *ref)
{
- int error = reference_delete(ref);
- if (error < GIT_SUCCESS)
- return error;
-
+ int result = reference_delete(ref);
git_reference_free(ref);
- return GIT_SUCCESS;
+ return result;
}
-
int git_reference_lookup(git_reference **ref_out,
git_repository *repo, const char *name)
{
+ return git_reference_lookup_resolved(ref_out, repo, name, 0);
+}
+
+int git_reference_name_to_oid(
+ git_oid *out, git_repository *repo, const char *name)
+{
int error;
- char normalized_name[GIT_REFNAME_MAX];
- git_reference *ref = NULL;
+ git_reference *ref;
+
+ if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0)
+ return error;
+
+ git_oid_cpy(out, git_reference_oid(ref));
+ git_reference_free(ref);
+ return 0;
+}
+
+int git_reference_lookup_resolved(
+ git_reference **ref_out,
+ git_repository *repo,
+ const char *name,
+ int max_nesting)
+{
+ git_reference *scan;
+ int result, nesting;
assert(ref_out && repo && name);
*ref_out = NULL;
- error = normalize_name(normalized_name, sizeof(normalized_name), name, 0);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup reference");
+ if (max_nesting > MAX_NESTING_LEVEL)
+ max_nesting = MAX_NESTING_LEVEL;
+ else if (max_nesting < 0)
+ max_nesting = DEFAULT_NESTING_LEVEL;
- error = reference_alloc(&ref, repo, normalized_name);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup reference");
+ scan = git__calloc(1, sizeof(git_reference));
+ GITERR_CHECK_ALLOC(scan);
- error = reference_lookup(ref);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup reference");
+ scan->name = git__calloc(GIT_REFNAME_MAX + 1, sizeof(char));
+ GITERR_CHECK_ALLOC(scan->name);
- *ref_out = ref;
- return GIT_SUCCESS;
+ if ((result = normalize_name(scan->name, GIT_REFNAME_MAX, name, 0)) < 0) {
+ git_reference_free(scan);
+ return result;
+ }
+
+ scan->target.symbolic = git__strdup(scan->name);
+ GITERR_CHECK_ALLOC(scan->target.symbolic);
+
+ scan->owner = repo;
+ scan->flags = GIT_REF_SYMBOLIC;
+
+ for (nesting = max_nesting;
+ nesting >= 0 && (scan->flags & GIT_REF_SYMBOLIC) != 0;
+ nesting--)
+ {
+ if (nesting != max_nesting)
+ strncpy(scan->name, scan->target.symbolic, GIT_REFNAME_MAX);
+
+ scan->mtime = 0;
+
+ if ((result = reference_lookup(scan)) < 0)
+ return result; /* lookup git_reference_free on scan already */
+ }
+
+ if ((scan->flags & GIT_REF_OID) == 0 && max_nesting != 0) {
+ giterr_set(GITERR_REFERENCE,
+ "Cannot resolve reference (>%u levels deep)", max_nesting);
+ git_reference_free(scan);
+ return -1;
+ }
+
+ *ref_out = scan;
+ return 0;
}
/**
@@ -1126,43 +1212,32 @@ int git_reference_create_symbolic(
int force)
{
char normalized[GIT_REFNAME_MAX];
- int ref_exists, error = GIT_SUCCESS;
git_reference *ref = NULL;
- error = normalize_name(normalized, sizeof(normalized), name, 0);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (normalize_name(normalized, sizeof(normalized), name, 0) < 0)
+ return -1;
- if ((error = reference_exists(&ref_exists, repo, normalized) < GIT_SUCCESS))
- return git__rethrow(error, "Failed to create symbolic reference");
+ if (reference_can_write(repo, normalized, NULL, force) < 0)
+ return -1;
- if (ref_exists && !force)
- return git__throw(GIT_EEXISTS,
- "Failed to create symbolic reference. Reference already exists");
-
- error = reference_alloc(&ref, repo, normalized);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (reference_alloc(&ref, repo, normalized) < 0)
+ return -1;
ref->flags |= GIT_REF_SYMBOLIC;
/* set the target; this will normalize the name automatically
* and write the reference on disk */
- error = git_reference_set_target(ref, target);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
+ if (git_reference_set_target(ref, target) < 0) {
+ git_reference_free(ref);
+ return -1;
+ }
if (ref_out == NULL) {
git_reference_free(ref);
} else {
*ref_out = ref;
}
- return GIT_SUCCESS;
-
-cleanup:
- git_reference_free(ref);
- return git__rethrow(error, "Failed to create symbolic reference");
+ return 0;
}
int git_reference_create_oid(
@@ -1172,34 +1247,25 @@ int git_reference_create_oid(
const git_oid *id,
int force)
{
- int error = GIT_SUCCESS, ref_exists;
git_reference *ref = NULL;
char normalized[GIT_REFNAME_MAX];
- error = normalize_name(normalized, sizeof(normalized), name, 1);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- if ((error = reference_exists(&ref_exists, repo, normalized) < GIT_SUCCESS))
- return git__rethrow(error, "Failed to create OID reference");
+ if (normalize_name(normalized, sizeof(normalized), name, 1) < 0)
+ return -1;
- if (ref_exists && !force)
- return git__throw(GIT_EEXISTS,
- "Failed to create OID reference. Reference already exists");
+ if (reference_can_write(repo, normalized, NULL, force) < 0)
+ return -1;
- if ((error = reference_available(repo, name, NULL)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create reference");
-
- error = reference_alloc(&ref, repo, name);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (reference_alloc(&ref, repo, name) < 0)
+ return -1;
ref->flags |= GIT_REF_OID;
/* set the oid; this will write the reference on disk */
- error = git_reference_set_oid(ref, id);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_reference_set_oid(ref, id) < 0) {
+ git_reference_free(ref);
+ return -1;
+ }
if (ref_out == NULL) {
git_reference_free(ref);
@@ -1207,13 +1273,8 @@ int git_reference_create_oid(
*ref_out = ref;
}
- return GIT_SUCCESS;
-
-cleanup:
- git_reference_free(ref);
- return git__rethrow(error, "Failed to create reference OID");
+ return 0;
}
-
/*
* Change the OID target of a reference.
*
@@ -1225,38 +1286,31 @@ cleanup:
*/
int git_reference_set_oid(git_reference *ref, const git_oid *id)
{
- int error = GIT_SUCCESS, exists;
git_odb *odb = NULL;
- if ((ref->flags & GIT_REF_OID) == 0)
- return git__throw(GIT_EINVALIDREFSTATE,
- "Failed to set OID target of reference. Not an OID reference");
+ if ((ref->flags & GIT_REF_OID) == 0) {
+ giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");
+ return -1;
+ }
assert(ref->owner);
- error = git_repository_odb__weakptr(&odb, ref->owner);
- if (error < GIT_SUCCESS)
- return error;
-
- exists = git_odb_exists(odb, id);
-
- git_odb_free(odb);
+ if (git_repository_odb__weakptr(&odb, ref->owner) < 0)
+ return -1;
/* Don't let the user create references to OIDs that
* don't exist in the ODB */
- if (!exists)
- return git__throw(GIT_ENOTFOUND,
- "Failed to set OID target of reference. OID doesn't exist in ODB");
+ if (!git_odb_exists(odb, id)) {
+ giterr_set(GITERR_REFERENCE,
+ "Target OID for the reference doesn't exist on the repository");
+ return -1;
+ }
/* Update the OID value on `ref` */
git_oid_cpy(&ref->target.oid, id);
/* Write back to disk */
- error = loose_write(ref);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to set OID target of reference");
-
- return GIT_SUCCESS;
+ return loose_write(ref);
}
/*
@@ -1268,82 +1322,44 @@ int git_reference_set_oid(git_reference *ref, const git_oid *id)
*/
int git_reference_set_target(git_reference *ref, const char *target)
{
- int error;
char normalized[GIT_REFNAME_MAX];
- if ((ref->flags & GIT_REF_SYMBOLIC) == 0)
- return git__throw(GIT_EINVALIDREFSTATE,
- "Failed to set reference target. Not a symbolic reference");
+ if ((ref->flags & GIT_REF_SYMBOLIC) == 0) {
+ giterr_set(GITERR_REFERENCE,
+ "Cannot set symbolic target on a direct reference");
+ return -1;
+ }
- error = normalize_name(normalized, sizeof(normalized), target, 0);
- if (error < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to set reference target. Invalid target name");
+ if (normalize_name(normalized, sizeof(normalized), target, 0))
+ return -1;
git__free(ref->target.symbolic);
ref->target.symbolic = git__strdup(normalized);
- if (ref->target.symbolic == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(ref->target.symbolic);
return loose_write(ref);
}
int git_reference_rename(git_reference *ref, const char *new_name, int force)
{
- int error;
+ int result;
git_buf aux_path = GIT_BUF_INIT;
char normalized[GIT_REFNAME_MAX];
const char *head_target = NULL;
- git_reference *existing_ref = NULL, *head = NULL;
-
- error = normalize_name(normalized, sizeof(normalized),
- new_name, ref->flags & GIT_REF_OID);
+ git_reference *head = NULL;
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to rename reference. Invalid name");
+ if (normalize_name(normalized, sizeof(normalized),
+ new_name, ref->flags & GIT_REF_OID) < 0)
+ return -1;
- new_name = normalized;
-
- /* If we are forcing the rename, try to lookup a reference with the
- * new one. If the lookup succeeds, we need to delete that ref
- * before the renaming can proceed */
- if (force) {
- error = git_reference_lookup(&existing_ref, ref->owner, new_name);
-
- if (error == GIT_SUCCESS) {
- error = git_reference_delete(existing_ref);
- if (error < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to rename reference. "
- "The existing reference cannot be deleted");
- } else if (error != GIT_ENOTFOUND)
- goto cleanup;
-
- /* If we're not forcing the rename, check if the reference exists.
- * If it does, renaming cannot continue */
- } else {
- int exists;
-
- error = reference_exists(&exists, ref->owner, normalized);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- if (exists)
- return git__throw(GIT_EEXISTS,
- "Failed to rename reference. Reference already exists");
- }
-
- if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to rename reference. Reference already exists");
+ if (reference_can_write(ref->owner, normalized, ref->name, force) < 0)
+ return -1;
/* Initialize path now so we won't get an allocation failure once
- * we actually start removing things.
- */
- error = git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ * we actually start removing things. */
+ if (git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name) < 0)
+ return -1;
/*
* Now delete the old ref and remove an possibly existing directory
@@ -1351,57 +1367,54 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
* method deletes the ref from disk but doesn't free the pointer, so
* we can still access the ref's attributes for creating the new one
*/
- if ((error = reference_delete(ref)) < GIT_SUCCESS)
+ if (reference_delete(ref) < 0)
goto cleanup;
- if (git_path_exists(aux_path.ptr) == GIT_SUCCESS) {
- if (git_path_isdir(aux_path.ptr) == GIT_SUCCESS) {
- if ((error = git_futils_rmdir_r(aux_path.ptr, 0)) < GIT_SUCCESS)
- goto rollback;
- } else goto rollback;
- }
-
/*
* Finally we can create the new reference.
*/
if (ref->flags & GIT_REF_SYMBOLIC) {
- error = git_reference_create_symbolic(
- NULL, ref->owner, new_name, ref->target.symbolic, 0);
+ result = git_reference_create_symbolic(
+ NULL, ref->owner, new_name, ref->target.symbolic, force);
} else {
- error = git_reference_create_oid(
- NULL, ref->owner, new_name, &ref->target.oid, 0);
+ result = git_reference_create_oid(
+ NULL, ref->owner, new_name, &ref->target.oid, force);
}
- if (error < GIT_SUCCESS)
+ if (result < 0)
goto rollback;
/*
* Check if we have to update HEAD.
*/
- error = git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE);
- if (error < GIT_SUCCESS)
+ if (git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE) < 0) {
+ giterr_set(GITERR_REFERENCE,
+ "Failed to update HEAD after renaming reference");
goto cleanup;
+ }
head_target = git_reference_target(head);
if (head_target && !strcmp(head_target, ref->name)) {
- error = git_reference_create_symbolic(
- &head, ref->owner, "HEAD", new_name, 1);
-
- if (error < GIT_SUCCESS)
+ if (git_reference_create_symbolic(&head, ref->owner, "HEAD", new_name, 1) < 0) {
+ giterr_set(GITERR_REFERENCE,
+ "Failed to update HEAD after renaming reference");
goto cleanup;
+ }
}
/*
* Rename the reflog file.
*/
- error = git_buf_join_n(&aux_path, '/', 3, ref->owner->path_repository,
- GIT_REFLOG_DIR, ref->name);
- if (error < GIT_SUCCESS)
+ if (git_buf_join_n(&aux_path, '/', 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name) < 0)
goto cleanup;
- if (git_path_exists(aux_path.ptr) == GIT_SUCCESS)
- error = git_reflog_rename(ref, new_name);
+ 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.
@@ -1412,92 +1425,49 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
/* The reference is no longer packed */
ref->flags &= ~GIT_REF_PACKED;
+ git_reference_free(head);
+ git_buf_free(&aux_path);
+ return 0;
+
cleanup:
- /* We no longer need the newly created reference nor the head */
git_reference_free(head);
git_buf_free(&aux_path);
- return error == GIT_SUCCESS ?
- GIT_SUCCESS :
- git__rethrow(error, "Failed to rename reference");
+ return -1;
rollback:
/*
- * Try to create the old reference again.
+ * Try to create the old reference again, ignore failures
*/
if (ref->flags & GIT_REF_SYMBOLIC)
- error = git_reference_create_symbolic(
+ git_reference_create_symbolic(
NULL, ref->owner, ref->name, ref->target.symbolic, 0);
else
- error = git_reference_create_oid(
+ git_reference_create_oid(
NULL, ref->owner, ref->name, &ref->target.oid, 0);
/* The reference is no longer packed */
ref->flags &= ~GIT_REF_PACKED;
git_buf_free(&aux_path);
-
- return error == GIT_SUCCESS ?
- git__rethrow(GIT_ERROR, "Failed to rename reference. Did rollback") :
- git__rethrow(error, "Failed to rename reference. Failed to rollback");
+ return -1;
}
int git_reference_resolve(git_reference **ref_out, git_reference *ref)
{
- int error, i = 0;
- git_repository *repo;
-
- assert(ref);
-
- *ref_out = NULL;
- repo = ref->owner;
-
- /* If the reference is already resolved, we need to return a
- * copy. Instead of duplicating `ref`, we look it up again to
- * ensure the copy is out to date */
if (ref->flags & GIT_REF_OID)
return git_reference_lookup(ref_out, ref->owner, ref->name);
-
- /* Otherwise, keep iterating until the reference is resolved */
- for (i = 0; i < MAX_NESTING_LEVEL; ++i) {
- git_reference *new_ref;
-
- error = git_reference_lookup(&new_ref, repo, ref->target.symbolic);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to resolve reference");
-
- /* Free intermediate references, except for the original one
- * we've received */
- if (i > 0)
- git_reference_free(ref);
-
- ref = new_ref;
-
- /* When the reference we've just looked up is an OID, we've
- * successfully resolved the symbolic ref */
- if (ref->flags & GIT_REF_OID) {
- *ref_out = ref;
- return GIT_SUCCESS;
- }
- }
-
- return git__throw(GIT_ENOMEM,
- "Failed to resolve reference. Reference is too nested");
+ else
+ return git_reference_lookup_resolved(ref_out, ref->owner, ref->target.symbolic, -1);
}
int git_reference_packall(git_repository *repo)
{
- int error;
-
- /* load the existing packfile */
- if ((error = packed_load(repo)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to pack references");
+ if (packed_load(repo) < 0 || /* load the existing packfile */
+ packed_loadloose(repo) < 0 || /* add all the loose refs */
+ packed_write(repo) < 0) /* write back to disk */
+ return -1;
- /* update it in-memory with all the loose references */
- if ((error = packed_loadloose(repo)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to pack references");
-
- /* write it back to disk */
- return packed_write(repo);
+ return 0;
}
int git_reference_foreach(
@@ -1506,22 +1476,23 @@ int git_reference_foreach(
int (*callback)(const char *, void *),
void *payload)
{
- int error;
+ int result;
struct dirent_list_data data;
git_buf refs_path = GIT_BUF_INIT;
/* list all the packed references first */
if (list_flags & GIT_REF_PACKED) {
const char *ref_name;
+ void *ref;
+ GIT_UNUSED(ref);
- if ((error = packed_load(repo)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to list references");
+ if (packed_load(repo) < 0)
+ return -1;
- GIT_HASHTABLE_FOREACH_KEY(repo->references.packfile, ref_name,
- if ((error = callback(ref_name, payload)) < GIT_SUCCESS)
- return git__throw(error,
- "Failed to list references. User callback failed");
- );
+ git_strmap_foreach(repo->references.packfile, ref_name, ref, {
+ if (callback(ref_name, payload) < 0)
+ return 0;
+ });
}
/* now list the loose references, trying not to
@@ -1533,15 +1504,13 @@ int git_reference_foreach(
data.callback = callback;
data.callback_payload = payload;
- if ((error = git_buf_joinpath(&refs_path,
- repo->path_repository, GIT_REFS_DIR)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to alloc space for references");
-
- error = git_path_direach(&refs_path, _dirent_loose_listall, &data);
+ if (git_buf_joinpath(&refs_path, repo->path_repository, GIT_REFS_DIR) < 0)
+ return -1;
+ result = git_path_direach(&refs_path, _dirent_loose_listall, &data);
git_buf_free(&refs_path);
- return error;
+ return result;
}
static int cb__reflist_add(const char *ref, void *data)
@@ -1554,7 +1523,6 @@ int git_reference_listall(
git_repository *repo,
unsigned int list_flags)
{
- int error;
git_vector ref_list;
assert(array && repo);
@@ -1562,33 +1530,25 @@ int git_reference_listall(
array->strings = NULL;
array->count = 0;
- if (git_vector_init(&ref_list, 8, NULL) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (git_vector_init(&ref_list, 8, NULL) < 0)
+ return -1;
- error = git_reference_foreach(
- repo, list_flags, &cb__reflist_add, (void *)&ref_list);
-
- if (error < GIT_SUCCESS) {
+ if (git_reference_foreach(
+ repo, list_flags, &cb__reflist_add, (void *)&ref_list) < 0) {
git_vector_free(&ref_list);
- return error;
+ return -1;
}
array->strings = (char **)ref_list.contents;
array->count = ref_list.length;
- return GIT_SUCCESS;
+ return 0;
}
int git_reference_reload(git_reference *ref)
{
- int error = reference_lookup(ref);
-
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to reload reference");
-
- return GIT_SUCCESS;
+ return reference_lookup(ref);
}
-
void git_repository__refcache_free(git_refcache *refs)
{
assert(refs);
@@ -1596,10 +1556,11 @@ void git_repository__refcache_free(git_refcache *refs)
if (refs->packfile) {
struct packref *reference;
- GIT_HASHTABLE_FOREACH_VALUE(
- refs->packfile, reference, git__free(reference));
+ git_strmap_foreach_value(refs->packfile, reference, {
+ git__free(reference);
+ });
- git_hashtable_free(refs->packfile);
+ git_strmap_free(refs->packfile);
}
}
@@ -1643,33 +1604,26 @@ static int normalize_name(
/* A refname can not be empty */
if (name_end == name)
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. Reference name is empty");
+ goto invalid_name;
/* A refname can not end with a dot or a slash */
if (*(name_end - 1) == '.' || *(name_end - 1) == '/')
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. Reference name ends with dot or slash");
+ goto invalid_name;
while (current < name_end && out_size) {
if (!is_valid_ref_char(*current))
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. "
- "Reference name contains invalid characters");
+ goto invalid_name;
if (buffer_out > buffer_out_start) {
char prev = *(buffer_out - 1);
/* A refname can not start with a dot nor contain a double dot */
if (*current == '.' && ((prev == '.') || (prev == '/')))
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. "
- "Reference name starts with a dot or contains a double dot");
+ goto invalid_name;
/* '@{' is forbidden within a refname */
if (*current == '{' && prev == '@')
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. Reference name contains '@{'");
+ goto invalid_name;
/* Prevent multiple slashes from being added to the output */
if (*current == '/' && prev == '/') {
@@ -1686,7 +1640,7 @@ static int normalize_name(
}
if (!out_size)
- return git__throw(GIT_EINVALIDREFNAME, "Reference name is too long");
+ goto invalid_name;
/* Object id refname have to contain at least one slash, except
* for HEAD in a detached state or MERGE_HEAD if we're in the
@@ -1696,13 +1650,11 @@ static int normalize_name(
strcmp(name, GIT_HEAD_FILE) != 0 &&
strcmp(name, GIT_MERGE_HEAD_FILE) != 0 &&
strcmp(name, GIT_FETCH_HEAD_FILE) != 0)
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. Reference name contains no slashes");
+ goto invalid_name;
/* A refname can not end with ".lock" */
if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION))
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. Reference name ends with '.lock'");
+ goto invalid_name;
*buffer_out = '\0';
@@ -1712,11 +1664,13 @@ static int normalize_name(
*/
if (is_oid_ref && !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) ||
strcmp(buffer_out_start, GIT_HEAD_FILE)))
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. "
- "Reference name does not start with 'refs/'");
+ goto invalid_name;
+
+ return 0;
- return GIT_SUCCESS;
+invalid_name:
+ giterr_set(GITERR_REFERENCE, "The given reference name is not valid");
+ return -1;
}
int git_reference__normalize_name(
@@ -1734,3 +1688,20 @@ int git_reference__normalize_name_oid(
{
return normalize_name(buffer_out, out_size, name, 1);
}
+
+#define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)
+
+int git_reference_cmp(git_reference *ref1, git_reference *ref2)
+{
+ assert(ref1 && ref2);
+
+ /* let's put symbolic refs before OIDs */
+ if ((ref1->flags & GIT_REF_TYPEMASK) != (ref2->flags & GIT_REF_TYPEMASK))
+ return (ref1->flags & GIT_REF_SYMBOLIC) ? -1 : 1;
+
+ if (ref1->flags & GIT_REF_SYMBOLIC)
+ return strcmp(ref1->target.symbolic, ref2->target.symbolic);
+
+ return git_oid_cmp(&ref1->target.oid, &ref2->target.oid);
+}
+
diff --git a/src/refs.h b/src/refs.h
index 13b9abf15..369e91e1c 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -10,7 +10,7 @@
#include "common.h"
#include "git2/oid.h"
#include "git2/refs.h"
-#include "hashtable.h"
+#include "strmap.h"
#define GIT_REFS_DIR "refs/"
#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
@@ -46,7 +46,7 @@ struct git_reference {
};
typedef struct {
- git_hashtable *packfile;
+ git_strmap *packfile;
time_t packfile_time;
} git_refcache;
@@ -55,4 +55,27 @@ void git_repository__refcache_free(git_refcache *refs);
int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name);
int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name);
+/**
+ * Lookup a reference by name and try to resolve to an OID.
+ *
+ * You can control how many dereferences this will attempt to resolve the
+ * reference with the `max_deref` parameter, or pass -1 to use a sane
+ * default. If you pass 0 for `max_deref`, this will not attempt to resolve
+ * the reference. For any value of `max_deref` other than 0, not
+ * successfully resolving the reference will be reported as an error.
+
+ * The generated reference must be freed by the user.
+ *
+ * @param reference_out Pointer to the looked-up reference
+ * @param repo The repository to look up the reference
+ * @param name The long name for the reference (e.g. HEAD, ref/heads/master, refs/tags/v0.1.0, ...)
+ * @param max_deref Maximum number of dereferences to make of symbolic refs, 0 means simple lookup, < 0 means use default reasonable value
+ * @return 0 on success or < 0 on error; not being able to resolve the reference is an error unless 0 was passed for max_deref
+ */
+int git_reference_lookup_resolved(
+ git_reference **reference_out,
+ git_repository *repo,
+ const char *name,
+ int max_deref);
+
#endif
diff --git a/src/refspec.c b/src/refspec.c
index a27141431..bec770a30 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -25,24 +25,21 @@ int git_refspec_parse(git_refspec *refspec, const char *str)
delim = strchr(str, ':');
if (delim == NULL) {
refspec->src = git__strdup(str);
- if (refspec->src == NULL)
- return GIT_ENOMEM;
-
- return GIT_SUCCESS;
+ GITERR_CHECK_ALLOC(refspec->src);
+ return 0;
}
refspec->src = git__strndup(str, delim - str);
- if (refspec->src == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(refspec->src);
refspec->dst = git__strdup(delim + 1);
if (refspec->dst == NULL) {
git__free(refspec->src);
refspec->src = NULL;
- return GIT_ENOMEM;
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
const char *git_refspec_src(const git_refspec *refspec)
@@ -65,8 +62,10 @@ int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, con
size_t baselen, namelen;
baselen = strlen(spec->dst);
- if (outlen <= baselen)
- return git__throw(GIT_EINVALIDREFNAME, "Reference name too long");
+ if (outlen <= baselen) {
+ giterr_set(GITERR_INVALID, "Reference name too long");
+ return GIT_ESHORTBUFFER;
+ }
/*
* No '*' at the end means that it's mapped to one specific local
@@ -74,7 +73,7 @@ int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, con
*/
if (spec->dst[baselen - 1] != '*') {
memcpy(out, spec->dst, baselen + 1); /* include '\0' */
- return GIT_SUCCESS;
+ return 0;
}
/* There's a '*' at the end, so remove its length */
@@ -85,29 +84,35 @@ int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, con
namelen = strlen(name);
- if (outlen <= baselen + namelen)
- return git__throw(GIT_EINVALIDREFNAME, "Reference name too long");
+ if (outlen <= baselen + namelen) {
+ giterr_set(GITERR_INVALID, "Reference name too long");
+ return GIT_ESHORTBUFFER;
+ }
memcpy(out, spec->dst, baselen);
memcpy(out + baselen, name, namelen + 1);
- return GIT_SUCCESS;
+ return 0;
}
int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name)
{
- if (git_buf_sets(out, spec->dst) < GIT_SUCCESS)
- return git_buf_lasterror(out);
+ if (git_buf_sets(out, spec->dst) < 0)
+ return -1;
/*
* No '*' at the end means that it's mapped to one specific local
* branch, so no actual transformation is needed.
*/
- if (out->size > 0 && out->ptr[out->size - 1] != '*')
- return GIT_SUCCESS;
+ if (git_buf_len(out) > 0 && out->ptr[git_buf_len(out) - 1] != '*')
+ return 0;
- git_buf_truncate(out, out->size - 1); /* remove trailing '*' */
+ git_buf_truncate(out, git_buf_len(out) - 1); /* remove trailing '*' */
git_buf_puts(out, name + strlen(spec->src) - 1);
- return git_buf_lasterror(out);
+ if (git_buf_oom(out))
+ return -1;
+
+ return 0;
}
+
diff --git a/src/remote.c b/src/remote.c
index 52b6aacc9..98c256929 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -29,30 +29,26 @@ static int refspec_parse(git_refspec *refspec, const char *str)
}
delim = strchr(str, ':');
- if (delim == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse refspec. No ':'");
+ if (delim == NULL) {
+ giterr_set(GITERR_NET, "Invalid refspec, missing ':'");
+ return -1;
+ }
refspec->src = git__strndup(str, delim - str);
- if (refspec->src == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(refspec->src);
refspec->dst = git__strdup(delim + 1);
- if (refspec->dst == NULL) {
- git__free(refspec->src);
- refspec->src = NULL;
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(refspec->dst);
- return GIT_SUCCESS;
+ return 0;
}
static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var)
{
- const char *val;
int error;
+ const char *val;
- error = git_config_get_string(cfg, var, &val);
- if (error < GIT_SUCCESS)
+ if ((error = git_config_get_string(cfg, var, &val)) < 0)
return error;
return refspec_parse(refspec, val);
@@ -66,33 +62,24 @@ int git_remote_new(git_remote **out, git_repository *repo, const char *url, cons
assert(out && repo && url);
remote = git__malloc(sizeof(git_remote));
- if (remote == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(remote);
memset(remote, 0x0, sizeof(git_remote));
remote->repo = repo;
- if (git_vector_init(&remote->refs, 32, NULL) < 0) {
- git_remote_free(remote);
- return GIT_ENOMEM;
- }
+ if (git_vector_init(&remote->refs, 32, NULL) < 0)
+ return -1;
remote->url = git__strdup(url);
- if (remote->url == NULL) {
- git_remote_free(remote);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(remote->url);
if (name != NULL) {
remote->name = git__strdup(name);
- if (remote->name == NULL) {
- git_remote_free(remote);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(remote->name);
}
*out = remote;
- return GIT_SUCCESS;
+ return 0;
}
int git_remote_load(git_remote **out, git_repository *repo, const char *name)
@@ -100,87 +87,68 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
git_remote *remote;
git_buf buf = GIT_BUF_INIT;
const char *val;
- int error;
+ int error = 0;
git_config *config;
assert(out && repo && name);
- error = git_repository_config__weakptr(&config, repo);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_repository_config__weakptr(&config, repo) < 0)
+ return -1;
remote = git__malloc(sizeof(git_remote));
- if (remote == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(remote);
memset(remote, 0x0, sizeof(git_remote));
remote->name = git__strdup(name);
- if (remote->name == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(remote->name);
- if (git_vector_init(&remote->refs, 32, NULL) < 0) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ if (git_vector_init(&remote->refs, 32, NULL) < 0)
+ return -1;
- git_buf_printf(&buf, "remote.%s.url", name);
- if (git_buf_oom(&buf)) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ if (git_buf_printf(&buf, "remote.%s.url", name) < 0)
+ return -1;
- error = git_config_get_string(config, git_buf_cstr(&buf), &val);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Remote's url doesn't exist");
+ if (git_config_get_string(config, git_buf_cstr(&buf), &val) < 0) {
+ error = -1;
goto cleanup;
}
remote->repo = repo;
remote->url = git__strdup(val);
- if (remote->url == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(remote->url);
git_buf_clear(&buf);
- git_buf_printf(&buf, "remote.%s.fetch", name);
- if (git_buf_oom(&buf)) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ if (git_buf_printf(&buf, "remote.%s.fetch", name) < 0)
+ return -1;
error = parse_remote_refspec(config, &remote->fetch, git_buf_cstr(&buf));
if (error == GIT_ENOTFOUND)
- error = GIT_SUCCESS;
+ error = 0;
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to get fetch refspec");
+ if (error < 0) {
+ error = -1;
goto cleanup;
}
git_buf_clear(&buf);
- git_buf_printf(&buf, "remote.%s.push", name);
- if (git_buf_oom(&buf)) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ if (git_buf_printf(&buf, "remote.%s.push", name) < 0)
+ return -1;
error = parse_remote_refspec(config, &remote->push, git_buf_cstr(&buf));
- /* Not finding push is fine */
if (error == GIT_ENOTFOUND)
- error = GIT_SUCCESS;
+ error = 0;
- if (error < GIT_SUCCESS)
+ if (error < 0) {
+ error = -1;
goto cleanup;
+ }
*out = remote;
cleanup:
git_buf_free(&buf);
- if (error < GIT_SUCCESS)
+ if (error < 0)
git_remote_free(remote);
return error;
@@ -188,52 +156,53 @@ cleanup:
int git_remote_save(const git_remote *remote)
{
- int error;
git_config *config;
git_buf buf = GIT_BUF_INIT, value = GIT_BUF_INIT;
- error = git_repository_config__weakptr(&config, remote->repo);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_repository_config__weakptr(&config, remote->repo) < 0)
+ return -1;
- git_buf_printf(&buf, "remote.%s.%s", remote->name, "url");
- if (git_buf_oom(&buf))
- return GIT_ENOMEM;
+ if (git_buf_printf(&buf, "remote.%s.%s", remote->name, "url") < 0)
+ return -1;
- error = git_config_set_string(config, git_buf_cstr(&buf), remote->url);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) {
+ git_buf_free(&buf);
+ return -1;
+ }
if (remote->fetch.src != NULL && remote->fetch.dst != NULL) {
git_buf_clear(&buf);
git_buf_clear(&value);
- git_buf_printf(&buf, "remote.%s.%s", remote->name, "fetch");
+ git_buf_printf(&buf, "remote.%s.fetch", remote->name);
git_buf_printf(&value, "%s:%s", remote->fetch.src, remote->fetch.dst);
if (git_buf_oom(&buf) || git_buf_oom(&value))
- return GIT_ENOMEM;
+ return -1;
- error = git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value));
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value)) < 0)
+ goto on_error;
}
if (remote->push.src != NULL && remote->push.dst != NULL) {
git_buf_clear(&buf);
git_buf_clear(&value);
- git_buf_printf(&buf, "remote.%s.%s", remote->name, "push");
+ git_buf_printf(&buf, "remote.%s.push", remote->name);
git_buf_printf(&value, "%s:%s", remote->push.src, remote->push.dst);
if (git_buf_oom(&buf) || git_buf_oom(&value))
- return GIT_ENOMEM;
+ return -1;
- error = git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value));
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value)) < 0)
+ goto on_error;
}
-cleanup:
git_buf_free(&buf);
git_buf_free(&value);
- return error;
+
+ return 0;
+
+on_error:
+ git_buf_free(&buf);
+ git_buf_free(&value);
+ return -1;
}
const char *git_remote_name(git_remote *remote)
@@ -250,21 +219,19 @@ const char *git_remote_url(git_remote *remote)
int git_remote_set_fetchspec(git_remote *remote, const char *spec)
{
- int error;
git_refspec refspec;
assert(remote && spec);
- error = refspec_parse(&refspec, spec);
- if (error != GIT_SUCCESS)
- return error;
+ if (refspec_parse(&refspec, spec) < 0)
+ return -1;
git__free(remote->fetch.src);
git__free(remote->fetch.dst);
remote->fetch.src = refspec.src;
remote->fetch.dst = refspec.dst;
- return GIT_SUCCESS;
+ return 0;
}
const git_refspec *git_remote_fetchspec(git_remote *remote)
@@ -275,21 +242,19 @@ const git_refspec *git_remote_fetchspec(git_remote *remote)
int git_remote_set_pushspec(git_remote *remote, const char *spec)
{
- int error;
git_refspec refspec;
assert(remote && spec);
- error = refspec_parse(&refspec, spec);
- if (error != GIT_SUCCESS)
- return error;
+ if (refspec_parse(&refspec, spec) < 0)
+ return -1;
git__free(remote->push.src);
git__free(remote->push.dst);
remote->push.src = refspec.src;
remote->push.dst = refspec.dst;
- return GIT_SUCCESS;
+ return 0;
}
const git_refspec *git_remote_pushspec(git_remote *remote)
@@ -300,57 +265,56 @@ const git_refspec *git_remote_pushspec(git_remote *remote)
int git_remote_connect(git_remote *remote, int direction)
{
- int error;
git_transport *t;
assert(remote);
- error = git_transport_new(&t, remote->url);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create transport");
+ if (git_transport_new(&t, remote->url) < 0)
+ return -1;
- error = t->connect(t, direction);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to connect the transport");
- goto cleanup;
+ if (t->connect(t, direction) < 0) {
+ goto on_error;
}
remote->transport = t;
-cleanup:
- if (error < GIT_SUCCESS)
- t->free(t);
+ return 0;
- return error;
+on_error:
+ t->free(t);
+ return -1;
}
int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)
{
assert(remote);
- if (!remote->transport || !remote->transport->connected)
- return git__throw(GIT_ERROR, "The remote is not connected");
+ if (!remote->transport || !remote->transport->connected) {
+ giterr_set(GITERR_NET, "The remote is not connected");
+ return -1;
+ }
return remote->transport->ls(remote->transport, list_cb, payload);
}
-int git_remote_download(char **filename, git_remote *remote)
+int git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
{
int error;
- assert(filename && remote);
+ assert(remote && bytes && stats);
if ((error = git_fetch_negotiate(remote)) < 0)
- return git__rethrow(error, "Error negotiating");
+ return error;
- return git_fetch_download_pack(filename, remote);
+ return git_fetch_download_pack(remote, bytes, stats);
}
-int git_remote_update_tips(git_remote *remote)
+int git_remote_update_tips(git_remote *remote, int (*cb)(const char *refname, const git_oid *a, const git_oid *b))
{
- int error = GIT_SUCCESS;
+ int error = 0;
unsigned int i = 0;
git_buf refname = GIT_BUF_INIT;
+ git_oid old;
git_vector *refs = &remote->refs;
git_remote_head *head;
git_reference *ref;
@@ -364,31 +328,47 @@ int git_remote_update_tips(git_remote *remote)
/* HEAD is only allowed to be the first in the list */
head = refs->contents[0];
if (!strcmp(head->name, GIT_HEAD_FILE)) {
- error = git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1);
- i = 1;
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to update FETCH_HEAD");
+ if (git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0)
+ return -1;
+ i = 1;
git_reference_free(ref);
}
for (; i < refs->length; ++i) {
head = refs->contents[i];
- error = git_refspec_transform_r(&refname, spec, head->name);
- if (error < GIT_SUCCESS)
- break;
+ if (git_refspec_transform_r(&refname, spec, head->name) < 0)
+ goto on_error;
+
+ error = git_reference_name_to_oid(&old, remote->repo, refname.ptr);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto on_error;
+
+ if (error == GIT_ENOTFOUND)
+ memset(&old, 0, GIT_OID_RAWSZ);
- error = git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, 1);
- if (error < GIT_SUCCESS)
+ if (!git_oid_cmp(&old, &head->oid))
+ continue;
+
+ if (git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, 1) < 0)
break;
git_reference_free(ref);
+
+ if (cb != NULL) {
+ if (cb(refname.ptr, &old, &head->oid) < 0)
+ goto on_error;
+ }
}
git_buf_free(&refname);
+ return 0;
+
+on_error:
+ git_buf_free(&refname);
+ return -1;
- return error;
}
int git_remote_connected(git_remote *remote)
@@ -401,13 +381,8 @@ void git_remote_disconnect(git_remote *remote)
{
assert(remote);
- if (remote->transport != NULL) {
- if (remote->transport->connected)
+ if (remote->transport != NULL && remote->transport->connected)
remote->transport->close(remote->transport);
-
- remote->transport->free(remote->transport);
- remote->transport = NULL;
- }
}
void git_remote_free(git_remote *remote)
@@ -415,14 +390,21 @@ void git_remote_free(git_remote *remote)
if (remote == NULL)
return;
+ if (remote->transport != NULL) {
+ git_remote_disconnect(remote);
+
+ remote->transport->free(remote->transport);
+ remote->transport = NULL;
+ }
+
+ git_vector_free(&remote->refs);
+
git__free(remote->fetch.src);
git__free(remote->fetch.dst);
git__free(remote->push.src);
git__free(remote->push.dst);
git__free(remote->url);
git__free(remote->name);
- git_vector_free(&remote->refs);
- git_remote_disconnect(remote);
git__free(remote);
}
@@ -436,20 +418,17 @@ static int remote_list_cb(const char *name, const char *value, void *data_)
struct cb_data *data = (struct cb_data *)data_;
size_t nmatch = 2;
regmatch_t pmatch[2];
- int error;
GIT_UNUSED(value);
if (!regexec(data->preg, name, nmatch, pmatch, 0)) {
char *remote_name = git__strndup(&name[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so);
- if (remote_name == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(remote_name);
- error = git_vector_insert(data->list, remote_name);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_vector_insert(data->list, remote_name) < 0)
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
int git_remote_list(git_strarray *remotes_list, git_repository *repo)
@@ -460,27 +439,26 @@ int git_remote_list(git_strarray *remotes_list, git_repository *repo)
struct cb_data data;
int error;
- error = git_repository_config__weakptr(&cfg, repo);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_repository_config__weakptr(&cfg, repo) < 0)
+ return -1;
- error = git_vector_init(&list, 4, NULL);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_vector_init(&list, 4, NULL) < 0)
+ return -1;
- error = regcomp(&preg, "^remote\\.(.*)\\.url$", REG_EXTENDED);
- if (error < 0)
- return GIT_EOSERR;
+ if (regcomp(&preg, "^remote\\.(.*)\\.url$", REG_EXTENDED) < 0) {
+ giterr_set(GITERR_OS, "Remote catch regex failed to compile");
+ return -1;
+ }
data.list = &list;
data.preg = &preg;
error = git_config_foreach(cfg, remote_list_cb, &data);
regfree(&preg);
- if (error < GIT_SUCCESS) {
+ if (error < 0) {
size_t i;
char *elem;
git_vector_foreach(&list, i, elem) {
- free(elem);
+ git__free(elem);
}
git_vector_free(&list);
@@ -490,5 +468,5 @@ int git_remote_list(git_strarray *remotes_list, git_repository *repo)
remotes_list->strings = (char **)list.contents;
remotes_list->count = list.length;
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/repository.c b/src/repository.c
index 1f8306991..cfabee420 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -5,6 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
#include <stdarg.h>
+#include <ctype.h>
#include "git2/object.h"
@@ -20,7 +21,7 @@
#define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
-#define GIT_FILE_CONTENT_PREFIX "gitdir: "
+#define GIT_FILE_CONTENT_PREFIX "gitdir:"
#define GIT_BRANCH_MASTER "master"
@@ -64,6 +65,7 @@ void git_repository_free(git_repository *repo)
git_cache_free(&repo->objects);
git_repository__refcache_free(&repo->references);
git_attr_cache_flush(repo);
+ git_submodule_config_free(repo);
git__free(repo->path_repository);
git__free(repo->workdir);
@@ -80,35 +82,31 @@ void git_repository_free(git_repository *repo)
*
* Open a repository object from its path
*/
-static int quickcheck_repository_dir(git_buf *repository_path)
+static bool valid_repository_path(git_buf *repository_path)
{
/* Check OBJECTS_DIR first, since it will generate the longest path name */
- if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) < 0)
- return GIT_ERROR;
+ if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false)
+ return false;
/* Ensure HEAD file exists */
- if (git_path_contains_file(repository_path, GIT_HEAD_FILE) < 0)
- return GIT_ERROR;
+ if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
+ return false;
- if (git_path_contains_dir(repository_path, GIT_REFS_DIR) < 0)
- return GIT_ERROR;
+ if (git_path_contains_dir(repository_path, GIT_REFS_DIR) == false)
+ return false;
- return GIT_SUCCESS;
+ return true;
}
-
static git_repository *repository_alloc(void)
{
- int error;
-
git_repository *repo = git__malloc(sizeof(git_repository));
if (!repo)
return NULL;
memset(repo, 0x0, sizeof(git_repository));
- error = git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free);
- if (error < GIT_SUCCESS) {
+ if (git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free) < 0) {
git__free(repo);
return NULL;
}
@@ -121,180 +119,385 @@ static git_repository *repository_alloc(void)
static int load_config_data(git_repository *repo)
{
- int error, is_bare;
+ int is_bare;
git_config *config;
- error = git_repository_config__weakptr(&config, repo);
- if (error < GIT_SUCCESS)
+ if (git_repository_config__weakptr(&config, repo) < 0)
+ return -1;
+
+ if (git_config_get_bool(config, "core.bare", &is_bare) < 0)
+ return -1; /* FIXME: We assume that a missing core.bare
+ variable is an error. Is this right? */
+
+ repo->is_bare = is_bare;
+ return 0;
+}
+
+static int load_workdir(git_repository *repo, git_buf *parent_path)
+{
+ int error;
+ git_config *config;
+ const char *worktree;
+ git_buf worktree_buf = GIT_BUF_INIT;
+
+ if (repo->is_bare)
+ return 0;
+
+ if (git_repository_config__weakptr(&config, repo) < 0)
+ return -1;
+
+ error = git_config_get_string(config, "core.worktree", &worktree);
+ if (!error && worktree != NULL)
+ repo->workdir = git__strdup(worktree);
+ else if (error != GIT_ENOTFOUND)
return error;
+ else {
+ giterr_clear();
+
+ if (parent_path && git_path_isdir(parent_path->ptr))
+ repo->workdir = git_buf_detach(parent_path);
+ else {
+ git_path_dirname_r(&worktree_buf, repo->path_repository);
+ git_path_to_dir(&worktree_buf);
+ repo->workdir = git_buf_detach(&worktree_buf);
+ }
+ }
+
+ GITERR_CHECK_ALLOC(repo->workdir);
+
+ return 0;
+}
+
+/*
+ * This function returns furthest offset into path where a ceiling dir
+ * is found, so we can stop processing the path at that point.
+ *
+ * Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on
+ * the stack could remove directories name limits, but at the cost of doing
+ * repeated malloc/frees inside the loop below, so let's not do it now.
+ */
+static int find_ceiling_dir_offset(
+ const char *path,
+ const char *ceiling_directories)
+{
+ char buf[GIT_PATH_MAX + 1];
+ char buf2[GIT_PATH_MAX + 1];
+ const char *ceil, *sep;
+ size_t len, max_len = 0, min_len;
+
+ assert(path);
+
+ min_len = (size_t)(git_path_root(path) + 1);
+
+ if (ceiling_directories == NULL || min_len == 0)
+ return (int)min_len;
+
+ for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) {
+ for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);
+ len = sep - ceil;
- error = git_config_get_bool(config, "core.bare", &is_bare);
- if (error == GIT_SUCCESS)
- repo->is_bare = is_bare;
+ if (len == 0 || len >= sizeof(buf) || git_path_root(ceil) == -1)
+ continue;
- /* TODO: what else can we load/cache here? */
+ strncpy(buf, ceil, len);
+ buf[len] = '\0';
- return GIT_SUCCESS;
+ if (p_realpath(buf, buf2) == NULL)
+ continue;
+
+ len = strlen(buf2);
+ if (len > 0 && buf2[len-1] == '/')
+ buf[--len] = '\0';
+
+ if (!strncmp(path, buf2, len) &&
+ path[len] == '/' &&
+ len > max_len)
+ {
+ max_len = len;
+ }
+ }
+
+ return (int)(max_len <= min_len ? min_len : max_len);
}
-static int load_workdir(git_repository *repo)
+/*
+ * Read the contents of `file_path` and set `path_out` to the repo dir that
+ * it points to. Before calling, set `path_out` to the base directory that
+ * should be used if the contents of `file_path` are a relative path.
+ */
+static int read_gitfile(git_buf *path_out, const char *file_path)
{
- int error;
- git_buf workdir_buf = GIT_BUF_INIT;
+ int error = 0;
+ git_buf file = GIT_BUF_INIT;
+ size_t prefix_len = strlen(GIT_FILE_CONTENT_PREFIX);
- if (repo->is_bare)
- return GIT_SUCCESS;
+ assert(path_out && file_path);
- git_path_dirname_r(&workdir_buf, repo->path_repository);
- git_path_to_dir(&workdir_buf);
+ if (git_futils_readbuffer(&file, file_path) < 0)
+ return -1;
- if ((error = git_buf_lasterror(&workdir_buf)) == GIT_SUCCESS)
- repo->workdir = git_buf_detach(&workdir_buf);
+ git_buf_rtrim(&file);
- git_buf_free(&workdir_buf);
+ if (file.size <= prefix_len ||
+ memcmp(file.ptr, GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
+ {
+ giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path);
+ error = -1;
+ }
+ else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) {
+ const char *gitlink = ((const char *)file.ptr) + prefix_len;
+ while (*gitlink && isspace(*gitlink)) gitlink++;
+ error = git_path_prettify_dir(path_out, gitlink, path_out->ptr);
+ }
+ git_buf_free(&file);
return error;
}
-int git_repository_open(git_repository **repo_out, const char *path)
+static int find_repo(
+ git_buf *repo_path,
+ git_buf *parent_path,
+ const char *start_path,
+ uint32_t flags,
+ const char *ceiling_dirs)
{
- int error = GIT_SUCCESS;
- git_buf path_buf = GIT_BUF_INIT;
- git_repository *repo = NULL;
+ int error;
+ git_buf path = GIT_BUF_INIT;
+ struct stat st;
+ dev_t initial_device = 0;
+ bool try_with_dot_git = false;
+ int ceiling_offset;
+
+ git_buf_free(repo_path);
+
+ if ((error = git_path_prettify_dir(&path, start_path, NULL)) < 0)
+ return error;
- error = git_path_prettify_dir(&path_buf, path, NULL);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- /**
- * Check if the path we've been given is actually the path
- * of the working dir, by testing if it contains a `.git`
- * folder inside of it.
- */
- if (git_path_contains_dir(&path_buf, GIT_DIR) == GIT_SUCCESS)
- git_buf_joinpath(&path_buf, path_buf.ptr, GIT_DIR);
-
- if (quickcheck_repository_dir(&path_buf) < GIT_SUCCESS) {
- error = git__throw(GIT_ENOTAREPO,
- "The given path (%s) is not a valid Git repository", git_buf_cstr(&path_buf));
- goto cleanup;
+ ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
+
+ if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
+ return error;
+
+ while (!error && !git_buf_len(repo_path)) {
+ if (p_stat(path.ptr, &st) == 0) {
+ /* check that we have not crossed device boundaries */
+ if (initial_device == 0)
+ initial_device = st.st_dev;
+ else if (st.st_dev != initial_device &&
+ (flags & GIT_REPOSITORY_OPEN_CROSS_FS) == 0)
+ break;
+
+ if (S_ISDIR(st.st_mode)) {
+ if (valid_repository_path(&path)) {
+ git_path_to_dir(&path);
+ git_buf_set(repo_path, path.ptr, path.size);
+ break;
+ }
+ }
+ else if (S_ISREG(st.st_mode)) {
+ git_buf repo_link = GIT_BUF_INIT;
+
+ if (!(error = read_gitfile(&repo_link, path.ptr))) {
+ if (valid_repository_path(&repo_link))
+ git_buf_swap(repo_path, &repo_link);
+
+ git_buf_free(&repo_link);
+ break;
+ }
+ git_buf_free(&repo_link);
+ }
+ }
+
+ /* move up one directory level */
+ if (git_path_dirname_r(&path, path.ptr) < 0) {
+ error = -1;
+ break;
+ }
+
+ if (try_with_dot_git) {
+ /* if we tried original dir with and without .git AND either hit
+ * directory ceiling or NO_SEARCH was requested, then be done.
+ */
+ if (path.ptr[ceiling_offset] == '\0' ||
+ (flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0)
+ break;
+ /* otherwise look first for .git item */
+ error = git_buf_joinpath(&path, path.ptr, DOT_GIT);
+ }
+ try_with_dot_git = !try_with_dot_git;
}
- repo = repository_alloc();
- if (repo == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
+ if (!error && parent_path != NULL) {
+ if (!git_buf_len(repo_path))
+ git_buf_clear(parent_path);
+ else {
+ git_path_dirname_r(parent_path, path.ptr);
+ git_path_to_dir(parent_path);
+ }
+ if (git_buf_oom(parent_path))
+ return -1;
}
- repo->path_repository = git_buf_detach(&path_buf);
- if (repo->path_repository == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
+ git_buf_free(&path);
+
+ if (!git_buf_len(repo_path) && !error) {
+ giterr_set(GITERR_REPOSITORY,
+ "Could not find repository from '%s'", start_path);
+ error = GIT_ENOTFOUND;
}
- error = load_config_data(repo);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ return error;
+}
- error = load_workdir(repo);
- if (error < GIT_SUCCESS)
- goto cleanup;
+int git_repository_open_ext(
+ git_repository **repo_ptr,
+ const char *start_path,
+ uint32_t flags,
+ const char *ceiling_dirs)
+{
+ int error;
+ git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
+ git_repository *repo;
- *repo_out = repo;
- return GIT_SUCCESS;
+ *repo_ptr = NULL;
- cleanup:
- git_repository_free(repo);
- git_buf_free(&path_buf);
- return error;
+ if ((error = find_repo(&path, &parent, start_path, flags, ceiling_dirs)) < 0)
+ return error;
+
+ repo = repository_alloc();
+ GITERR_CHECK_ALLOC(repo);
+
+ repo->path_repository = git_buf_detach(&path);
+ GITERR_CHECK_ALLOC(repo->path_repository);
+
+ if ((error = load_config_data(repo)) < 0 ||
+ (error = load_workdir(repo, &parent)) < 0)
+ {
+ git_repository_free(repo);
+ return error;
+ }
+
+ git_buf_free(&parent);
+ *repo_ptr = repo;
+ return 0;
+}
+
+int git_repository_open(git_repository **repo_out, const char *path)
+{
+ return git_repository_open_ext(
+ repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL);
+}
+
+int git_repository_discover(
+ char *repository_path,
+ size_t size,
+ const char *start_path,
+ int across_fs,
+ const char *ceiling_dirs)
+{
+ git_buf path = GIT_BUF_INIT;
+ uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0;
+
+ assert(start_path && repository_path && size > 0);
+
+ *repository_path = '\0';
+
+ if (find_repo(&path, NULL, start_path, flags, ceiling_dirs) < 0)
+ return -1;
+
+ if (size < (size_t)(path.size + 1)) {
+ giterr_set(GITERR_REPOSITORY,
+ "The given buffer is too long to store the discovered path");
+ git_buf_free(&path);
+ return -1;
+ }
+
+ /* success: we discovered a repository */
+ git_buf_copy_cstr(repository_path, size, &path);
+ git_buf_free(&path);
+ return 0;
}
static int load_config(
- git_config **out,
- git_repository *repo,
- const char *global_config_path,
- const char *system_config_path)
+ git_config **out,
+ git_repository *repo,
+ const char *global_config_path,
+ const char *system_config_path)
{
git_buf config_path = GIT_BUF_INIT;
- int error;
git_config *cfg = NULL;
assert(repo && out);
- error = git_config_new(&cfg);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_config_new(&cfg) < 0)
+ return -1;
+
+ if (git_buf_joinpath(
+ &config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO) < 0)
+ goto on_error;
- error = git_buf_joinpath(&config_path, repo->path_repository,
- GIT_CONFIG_FILENAME_INREPO);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_config_add_file_ondisk(cfg, config_path.ptr, 3) < 0)
+ goto on_error;
- error = git_config_add_file_ondisk(cfg, config_path.ptr, 3);
- git_buf_free(&config_path); /* done with config_path now */
- if (error < GIT_SUCCESS)
- goto cleanup;
+ git_buf_free(&config_path);
if (global_config_path != NULL) {
- error = git_config_add_file_ondisk(cfg, global_config_path, 2);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_config_add_file_ondisk(cfg, global_config_path, 2) < 0)
+ goto on_error;
}
if (system_config_path != NULL) {
- error = git_config_add_file_ondisk(cfg, system_config_path, 1);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_config_add_file_ondisk(cfg, system_config_path, 1) < 0)
+ goto on_error;
}
*out = cfg;
- return GIT_SUCCESS;
+ return 0;
-cleanup:
+on_error:
+ git_buf_free(&config_path);
git_config_free(cfg);
*out = NULL;
- return error;
+ return -1;
}
int git_repository_config__weakptr(git_config **out, git_repository *repo)
{
if (repo->_config == NULL) {
- int error;
git_buf global_buf = GIT_BUF_INIT, system_buf = GIT_BUF_INIT;
+ int res;
const char *global_config_path = NULL;
const char *system_config_path = NULL;
- if (git_config_find_global_r(&global_buf) == GIT_SUCCESS)
+ if (git_config_find_global_r(&global_buf) == 0)
global_config_path = global_buf.ptr;
- if (git_config_find_system_r(&system_buf) == GIT_SUCCESS)
+ if (git_config_find_system_r(&system_buf) == 0)
system_config_path = system_buf.ptr;
- error = load_config(&repo->_config, repo, global_config_path, system_config_path);
+ res = load_config(&repo->_config, repo, global_config_path, system_config_path);
git_buf_free(&global_buf);
git_buf_free(&system_buf);
- if (error < GIT_SUCCESS)
- return error;
+ if (res < 0)
+ return -1;
GIT_REFCOUNT_OWN(repo->_config, repo);
}
*out = repo->_config;
- return GIT_SUCCESS;
+ return 0;
}
int git_repository_config(git_config **out, git_repository *repo)
{
- int error = git_repository_config__weakptr(out, repo);
-
- if (error == GIT_SUCCESS) {
- GIT_REFCOUNT_INC(*out);
- }
+ if (git_repository_config__weakptr(out, repo) < 0)
+ return -1;
- return error;
+ GIT_REFCOUNT_INC(*out);
+ return 0;
}
void git_repository_set_config(git_repository *repo, git_config *config)
@@ -312,34 +515,32 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
assert(repo && out);
if (repo->_odb == NULL) {
- int error;
git_buf odb_path = GIT_BUF_INIT;
+ int res;
- error = git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR) < 0)
+ return -1;
- error = git_odb_open(&repo->_odb, odb_path.ptr);
+ res = git_odb_open(&repo->_odb, odb_path.ptr);
git_buf_free(&odb_path); /* done with path */
- if (error < GIT_SUCCESS)
- return error;
+
+ if (res < 0)
+ return -1;
GIT_REFCOUNT_OWN(repo->_odb, repo);
}
*out = repo->_odb;
- return GIT_SUCCESS;
+ return 0;
}
int git_repository_odb(git_odb **out, git_repository *repo)
{
- int error = git_repository_odb__weakptr(out, repo);
+ if (git_repository_odb__weakptr(out, repo) < 0)
+ return -1;
- if (error == GIT_SUCCESS) {
- GIT_REFCOUNT_INC(*out);
- }
-
- return error;
+ GIT_REFCOUNT_INC(*out);
+ return 0;
}
void git_repository_set_odb(git_repository *repo, git_odb *odb)
@@ -350,6 +551,7 @@ void git_repository_set_odb(git_repository *repo, git_odb *odb)
repo->_odb = odb;
GIT_REFCOUNT_OWN(repo->_odb, repo);
+ GIT_REFCOUNT_INC(odb);
}
int git_repository_index__weakptr(git_index **out, git_repository *repo)
@@ -357,34 +559,32 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo)
assert(out && repo);
if (repo->_index == NULL) {
- int error;
+ int res;
git_buf index_path = GIT_BUF_INIT;
- error = git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE) < 0)
+ return -1;
- error = git_index_open(&repo->_index, index_path.ptr);
+ res = git_index_open(&repo->_index, index_path.ptr);
git_buf_free(&index_path); /* done with path */
- if (error < GIT_SUCCESS)
- return error;
+
+ if (res < 0)
+ return -1;
GIT_REFCOUNT_OWN(repo->_index, repo);
}
*out = repo->_index;
- return GIT_SUCCESS;
+ return 0;
}
int git_repository_index(git_index **out, git_repository *repo)
{
- int error = git_repository_index__weakptr(out, repo);
-
- if (error == GIT_SUCCESS) {
- GIT_REFCOUNT_INC(*out);
- }
+ if (git_repository_index__weakptr(out, repo) < 0)
+ return -1;
- return error;
+ GIT_REFCOUNT_INC(*out);
+ return 0;
}
void git_repository_set_index(git_repository *repo, git_index *index)
@@ -395,337 +595,97 @@ void git_repository_set_index(git_repository *repo, git_index *index)
repo->_index = index;
GIT_REFCOUNT_OWN(repo->_index, repo);
-}
-
-
-static int retrieve_device(dev_t *device_out, const char *path)
-{
- struct stat path_info;
-
- assert(device_out);
-
- if (p_lstat(path, &path_info))
- return git__throw(GIT_EOSERR, "Failed to get file informations: %s", path);
-
- *device_out = path_info.st_dev;
-
- return GIT_SUCCESS;
-}
-
-/*
- * This function returns furthest offset into path where a ceiling dir
- * is found, so we can stop processing the path at that point.
- *
- * Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on
- * the stack could remove directories name limits, but at the cost of doing
- * repeated malloc/frees inside the loop below, so let's not do it now.
- */
-static int retrieve_ceiling_directories_offset(
- const char *path,
- const char *ceiling_directories)
-{
- char buf[GIT_PATH_MAX + 1];
- char buf2[GIT_PATH_MAX + 1];
- const char *ceil, *sep;
- int len, max_len = -1;
- int min_len;
-
- assert(path);
-
- min_len = git_path_root(path) + 1;
-
- if (ceiling_directories == NULL || min_len == 0)
- return min_len;
-
- for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) {
- for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);
- len = sep - ceil;
-
- if (len == 0 || len >= (int)sizeof(buf) || git_path_root(ceil) == -1)
- continue;
-
- strncpy(buf, ceil, len);
- buf[len] = '\0';
-
- if (p_realpath(buf, buf2) == NULL)
- continue;
-
- len = strlen(buf2);
- if (len > 0 && buf2[len-1] == '/')
- buf[--len] = '\0';
-
- if (!strncmp(path, buf2, len) &&
- path[len] == '/' &&
- len > max_len)
- {
- max_len = len;
- }
- }
-
- return max_len <= min_len ? min_len : max_len;
-}
-
-/*
- * Read the contents of `file_path` and set `path_out` to the repo dir that
- * it points to. Before calling, set `path_out` to the base directory that
- * should be used if the contents of `file_path` are a relative path.
- */
-static int read_gitfile(git_buf *path_out, const char *file_path, const char *base_path)
-{
- git_buf file = GIT_BUF_INIT;
- int error;
-
- assert(path_out && file_path);
-
- error = git_futils_readbuffer(&file, file_path);
- if (error < GIT_SUCCESS)
- return error;
-
- if (git__prefixcmp((char *)file.ptr, GIT_FILE_CONTENT_PREFIX)) {
- git_buf_free(&file);
- return git__throw(GIT_ENOTFOUND, "Invalid gitfile format `%s`", file_path);
- }
-
- git_buf_rtrim(&file);
-
- if (strlen(GIT_FILE_CONTENT_PREFIX) == file.size) {
- git_buf_free(&file);
- return git__throw(GIT_ENOTFOUND, "No path in git file `%s`", file_path);
- }
-
- error = git_path_prettify_dir(path_out,
- ((char *)file.ptr) + strlen(GIT_FILE_CONTENT_PREFIX), base_path);
-
- git_buf_free(&file);
-
- if (error == GIT_SUCCESS && git_path_exists(path_out->ptr) == 0)
- return GIT_SUCCESS;
-
- return git__throw(GIT_EOBJCORRUPTED, "The `.git` file points to a nonexistent path");
-}
-
-int git_repository_discover(
- char *repository_path,
- size_t size,
- const char *start_path,
- int across_fs,
- const char *ceiling_dirs)
-{
- int error, ceiling_offset;
- git_buf bare_path = GIT_BUF_INIT;
- git_buf normal_path = GIT_BUF_INIT;
- git_buf *found_path = NULL;
- dev_t current_device = 0;
-
- assert(start_path && repository_path);
-
- *repository_path = '\0';
-
- error = git_path_prettify_dir(&bare_path, start_path, NULL);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- if (!across_fs) {
- error = retrieve_device(&current_device, bare_path.ptr);
- if (error < GIT_SUCCESS)
- goto cleanup;
- }
-
- ceiling_offset = retrieve_ceiling_directories_offset(bare_path.ptr, ceiling_dirs);
-
- while(1) {
- error = git_buf_joinpath(&normal_path, bare_path.ptr, DOT_GIT);
- if (error < GIT_SUCCESS)
- break;
-
- /**
- * If the `.git` file is regular instead of
- * a directory, it should contain the path of the actual git repository
- */
- if (git_path_isfile(normal_path.ptr) == GIT_SUCCESS) {
- git_buf gitfile_path = GIT_BUF_INIT;
-
- error = read_gitfile(&gitfile_path, normal_path.ptr, bare_path.ptr);
- if (error < GIT_SUCCESS)
- git__rethrow(error, "Unable to read git file `%s`", normal_path.ptr);
- else if ((error = quickcheck_repository_dir(&gitfile_path)) < GIT_SUCCESS)
- git__throw(GIT_ENOTFOUND,
- "The `.git` file found at '%s' points "
- "to a nonexistent git folder", normal_path.ptr);
- else {
- git_buf_swap(&normal_path, &gitfile_path);
- found_path = &normal_path;
- }
-
- git_buf_free(&gitfile_path);
- break;
- }
-
- /**
- * If the `.git` file is a folder, we check inside of it
- */
- if (git_path_isdir(normal_path.ptr) == GIT_SUCCESS) {
- error = quickcheck_repository_dir(&normal_path);
- if (error == GIT_SUCCESS) {
- found_path = &normal_path;
- break;
- }
- }
-
- /**
- * Otherwise, the repository may be bare, let's check
- * the root anyway
- */
- error = quickcheck_repository_dir(&bare_path);
- if (error == GIT_SUCCESS) {
- found_path = &bare_path;
- break;
- }
-
- /**
- * If we didn't find it, walk up the tree
- */
- error = git_path_dirname_r(&normal_path, bare_path.ptr);
- if (error < GIT_SUCCESS) {
- git__rethrow(GIT_EOSERR, "Failed to dirname '%s'", bare_path.ptr);
- break;
- }
-
- git_buf_swap(&bare_path, &normal_path);
-
- if (!across_fs) {
- dev_t new_device;
- error = retrieve_device(&new_device, bare_path.ptr);
-
- if (error < GIT_SUCCESS || current_device != new_device) {
- error = git__throw(GIT_ENOTAREPO,
- "Not a git repository (or any parent up to mount parent %s)\n"
- "Stopping at filesystem boundary.", normal_path.ptr);
- break;
- }
- current_device = new_device;
- }
-
- /* nothing has been found, lets try the parent directory
- * but stop if we hit one of the ceiling directories
- */
- if (bare_path.ptr[ceiling_offset] == '\0') {
- error = git__throw(GIT_ENOTAREPO,
- "Not a git repository (or any of the parent directories): %s", start_path);
- break;
- }
- }
-
- assert(found_path || error != GIT_SUCCESS);
-
- if (found_path) {
- if ((error = git_path_to_dir(found_path)) < GIT_SUCCESS)
- git__rethrow(error, "Could not convert git repository to directory");
- else if (size < (size_t)(found_path->size + 1))
- error = git__throw(GIT_ESHORTBUFFER,
- "The repository buffer is not long enough to "
- "handle the repository path `%s`", found_path->ptr);
- else
- git_buf_copy_cstr(repository_path, size, found_path);
- }
-
-cleanup:
- git_buf_free(&bare_path);
- git_buf_free(&normal_path);
- return error;
+ GIT_REFCOUNT_INC(index);
}
static int check_repositoryformatversion(git_repository *repo)
{
git_config *config;
- int version, error = GIT_SUCCESS;
-
- if ((error = git_repository_config(&config, repo)) < GIT_SUCCESS)
- return git__throw(error, "Failed to open config file.");
+ int version;
- error = git_config_get_int32(config, GIT_CONFIG_CORE_REPOSITORYFORMATVERSION, &version);
+ if (git_repository_config__weakptr(&config, repo) < 0)
+ return -1;
- if (GIT_REPOSITORYFORMATVERSION < version)
- error = git__throw(GIT_ERROR, "Unsupported git repository version (Expected version <= %d, found %d).", GIT_REPOSITORYFORMATVERSION, version);
+ if (git_config_get_int32(config, GIT_CONFIG_CORE_REPOSITORYFORMATVERSION, &version) < 0)
+ return -1;
- git_config_free(config);
+ if (GIT_REPOSITORYFORMATVERSION < version) {
+ giterr_set(GITERR_REPOSITORY,
+ "Unsupported repository version %d. Only versions up to %d are supported.",
+ version, GIT_REPOSITORYFORMATVERSION);
+ return -1;
+ }
- return error;
+ return 0;
}
static int repo_init_reinit(git_repository **repo_out, const char *repository_path, int is_bare)
{
- int error;
git_repository *repo = NULL;
- if ((error = git_repository_open(&repo, repository_path)) < GIT_SUCCESS)
- goto error;
+ GIT_UNUSED(is_bare);
+
+ if (git_repository_open(&repo, repository_path) < 0)
+ return -1;
- if ((error = check_repositoryformatversion(repo)) < GIT_SUCCESS)
- goto error;
+ if (check_repositoryformatversion(repo) < 0) {
+ git_repository_free(repo);
+ return -1;
+ }
/* TODO: reinitialize the templates */
*repo_out = repo;
-
- return GIT_SUCCESS;
-
-error:
- git_repository_free(repo);
-
- return git__rethrow(error,
- "Failed to reinitialize the %srepository at '%s'. ",
- is_bare ? "bare " : "", repository_path);
+ return 0;
}
static int repo_init_createhead(const char *git_dir)
{
- int error;
git_buf ref_path = GIT_BUF_INIT;
git_filebuf ref = GIT_FILEBUF_INIT;
- if (!(error = git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE)) &&
- !(error = git_filebuf_open(&ref, ref_path.ptr, 0)) &&
- !(error = git_filebuf_printf(&ref, "ref: refs/heads/master\n")))
- error = git_filebuf_commit(&ref, GIT_REFS_FILE_MODE);
+ if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
+ git_filebuf_open(&ref, ref_path.ptr, 0) < 0 ||
+ git_filebuf_printf(&ref, "ref: refs/heads/master\n") < 0 ||
+ git_filebuf_commit(&ref, GIT_REFS_FILE_MODE) < 0)
+ return -1;
git_buf_free(&ref_path);
- return error;
+ return 0;
}
static int repo_init_config(const char *git_dir, int is_bare)
{
git_buf cfg_path = GIT_BUF_INIT;
git_config *config = NULL;
- int error = GIT_SUCCESS;
#define SET_REPO_CONFIG(type, name, val) {\
- error = git_config_set_##type(config, name, val);\
- if (error < GIT_SUCCESS)\
- goto cleanup;\
+ if (git_config_set_##type(config, name, val) < 0) { \
+ git_buf_free(&cfg_path); \
+ git_config_free(config); \
+ return -1; } \
}
- error = git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
+ return -1;
- error = git_config_open_ondisk(&config, cfg_path.ptr);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_config_open_ondisk(&config, cfg_path.ptr) < 0) {
+ git_buf_free(&cfg_path);
+ return -1;
+ }
SET_REPO_CONFIG(bool, "core.bare", is_bare);
SET_REPO_CONFIG(int32, GIT_CONFIG_CORE_REPOSITORYFORMATVERSION, GIT_REPOSITORYFORMATVERSION);
/* TODO: what other defaults? */
-cleanup:
git_buf_free(&cfg_path);
git_config_free(config);
- return error;
+ return 0;
}
static int repo_init_structure(const char *git_dir, int is_bare)
{
- int error, i;
+ int i;
struct { const char *dir; mode_t mode; } dirs[] = {
{ GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/info/' */
{ GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/pack/' */
@@ -735,136 +695,102 @@ static int repo_init_structure(const char *git_dir, int is_bare)
};
/* Make the base directory */
- error = git_futils_mkdir_r(git_dir, NULL, is_bare ?
- GIT_BARE_DIR_MODE : GIT_DIR_MODE);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to initialize repository structure. Could not mkdir");
+ if (git_futils_mkdir_r(git_dir, NULL, is_bare ? GIT_BARE_DIR_MODE : GIT_DIR_MODE) < 0)
+ return -1;
/* Hides the ".git" directory */
if (!is_bare) {
#ifdef GIT_WIN32
- error = p_hide_directory__w32(git_dir);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to initialize repository structure");
+ if (p_hide_directory__w32(git_dir) < 0) {
+ giterr_set(GITERR_REPOSITORY,
+ "Failed to mark Git repository folder as hidden");
+ return -1;
+ }
#endif
}
/* Make subdirectories as needed */
for (i = 0; dirs[i].dir != NULL; ++i) {
- error = git_futils_mkdir_r(dirs[i].dir, git_dir, dirs[i].mode);
- if (error < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to create repository folder `%s`", dirs[i].dir);
+ if (git_futils_mkdir_r(dirs[i].dir, git_dir, dirs[i].mode) < 0)
+ return -1;
}
/* TODO: what's left? templates? */
-
- return error;
+ return 0;
}
int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare)
{
- int error = GIT_SUCCESS;
- git_repository *repo = NULL;
git_buf repository_path = GIT_BUF_INIT;
assert(repo_out && path);
- error = git_buf_joinpath(&repository_path, path, is_bare ? "" : GIT_DIR);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&repository_path, path, is_bare ? "" : GIT_DIR) < 0)
+ return -1;
- if (git_path_isdir(repository_path.ptr) == GIT_SUCCESS) {
- if (quickcheck_repository_dir(&repository_path) == GIT_SUCCESS) {
- error = repo_init_reinit(repo_out, repository_path.ptr, is_bare);
+ if (git_path_isdir(repository_path.ptr) == true) {
+ if (valid_repository_path(&repository_path) == true) {
+ int res = repo_init_reinit(repo_out, repository_path.ptr, is_bare);
git_buf_free(&repository_path);
- return error;
+ return res;
}
}
- if (!(error = repo_init_structure(repository_path.ptr, is_bare)) &&
- !(error = repo_init_config(repository_path.ptr, is_bare)) &&
- !(error = repo_init_createhead(repository_path.ptr)))
- error = git_repository_open(repo_out, repository_path.ptr);
- else
- git_repository_free(repo);
+ if (repo_init_structure(repository_path.ptr, is_bare) < 0 ||
+ repo_init_config(repository_path.ptr, is_bare) < 0 ||
+ repo_init_createhead(repository_path.ptr) < 0 ||
+ git_repository_open(repo_out, repository_path.ptr) < 0) {
+ git_buf_free(&repository_path);
+ return -1;
+ }
git_buf_free(&repository_path);
-
- if (error != GIT_SUCCESS)
- git__rethrow(error, "Failed to (re)init the repository `%s`", path);
-
- return error;
+ return 0;
}
int git_repository_head_detached(git_repository *repo)
{
git_reference *ref;
- int error;
- size_t _size;
- git_otype type;
git_odb *odb = NULL;
+ int exists;
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ return -1;
- error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
+ return -1;
if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
git_reference_free(ref);
return 0;
}
- error = git_odb_read_header(&_size, &type, odb, git_reference_oid(ref));
+ exists = git_odb_exists(odb, git_reference_oid(ref));
git_reference_free(ref);
-
- if (error < GIT_SUCCESS)
- return error;
-
- if (type != GIT_OBJ_COMMIT)
- return git__throw(GIT_EOBJCORRUPTED, "HEAD is not a commit");
-
- return 1;
+ return exists;
}
int git_repository_head(git_reference **head_out, git_repository *repo)
{
- git_reference *ref, *resolved_ref;
- int error;
-
- *head_out = NULL;
-
- error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE);
- if (error < GIT_SUCCESS)
- return git__rethrow(GIT_ENOTAREPO, "Failed to locate the HEAD");
-
- error = git_reference_resolve(&resolved_ref, ref);
- if (error < GIT_SUCCESS) {
- git_reference_free(ref);
- return git__rethrow(error, "Failed to resolve the HEAD");
- }
-
- git_reference_free(ref);
-
- *head_out = resolved_ref;
- return GIT_SUCCESS;
+ return git_reference_lookup_resolved(head_out, repo, GIT_HEAD_FILE, -1);
}
int git_repository_head_orphan(git_repository *repo)
{
- git_reference *ref;
+ git_reference *ref = NULL;
int error;
error = git_repository_head(&ref, repo);
+ git_reference_free(ref);
- if (error == GIT_SUCCESS)
- git_reference_free(ref);
+ if (error == GIT_ENOTFOUND)
+ return 1;
- return error == GIT_ENOTFOUND ? 1 : error;
+ if (error < 0)
+ return -1;
+
+ return 0;
}
int git_repository_is_empty(git_repository *repo)
@@ -872,9 +798,8 @@ int git_repository_is_empty(git_repository *repo)
git_reference *head = NULL, *branch = NULL;
int error;
- error = git_reference_lookup(&head, repo, "HEAD");
- if (error < GIT_SUCCESS)
- return git__throw(error, "Corrupted repository. HEAD does not exist");
+ if (git_reference_lookup(&head, repo, "HEAD") < 0)
+ return -1;
if (git_reference_type(head) != GIT_REF_SYMBOLIC) {
git_reference_free(head);
@@ -891,7 +816,13 @@ int git_repository_is_empty(git_repository *repo)
git_reference_free(head);
git_reference_free(branch);
- return error == GIT_ENOTFOUND ? 1 : error;
+ if (error == GIT_ENOTFOUND)
+ return 1;
+
+ if (error < 0)
+ return -1;
+
+ return 0;
}
const char *git_repository_path(git_repository *repo)
@@ -912,16 +843,18 @@ const char *git_repository_workdir(git_repository *repo)
int git_repository_set_workdir(git_repository *repo, const char *workdir)
{
+ git_buf path = GIT_BUF_INIT;
+
assert(repo && workdir);
- free(repo->workdir);
+ if (git_path_prettify_dir(&path, workdir, NULL) < 0)
+ return -1;
- repo->workdir = git__strdup(workdir);
- if (repo->workdir == NULL)
- return GIT_ENOMEM;
+ git__free(repo->workdir);
+ repo->workdir = git_buf_detach(&path);
repo->is_bare = 0;
- return GIT_SUCCESS;
+ return 0;
}
int git_repository_is_bare(git_repository *repo)
diff --git a/src/repository.h b/src/repository.h
index b5dcc1340..1ffac58f1 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -13,13 +13,13 @@
#include "git2/repository.h"
#include "git2/object.h"
-#include "hashtable.h"
#include "index.h"
#include "cache.h"
#include "refs.h"
#include "buffer.h"
#include "odb.h"
#include "attr.h"
+#include "strmap.h"
#define DOT_GIT ".git"
#define GIT_DIR DOT_GIT "/"
@@ -83,6 +83,7 @@ struct git_repository {
git_cache objects;
git_refcache references;
git_attr_cache attrcache;
+ git_strmap *submodules;
char *path_repository;
char *workdir;
@@ -100,6 +101,11 @@ void git_object__free(void *object);
int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
+GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
+{
+ return &repo->attrcache;
+}
+
/*
* Weak pointers to repository internals.
*
@@ -120,4 +126,9 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo);
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar);
void git_repository__cvar_cache_clear(git_repository *repo);
+/*
+ * Submodule cache
+ */
+extern void git_submodule_config_free(git_repository *repo);
+
#endif
diff --git a/src/revwalk.c b/src/revwalk.c
index 997771f08..c62bb4e0e 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -8,20 +8,30 @@
#include "common.h"
#include "commit.h"
#include "odb.h"
-#include "hashtable.h"
#include "pqueue.h"
+#include "pool.h"
+#include "oidmap.h"
#include "git2/revwalk.h"
+#include "git2/merge.h"
#include <regex.h>
+GIT__USE_OIDMAP;
+
+#define PARENT1 (1 << 0)
+#define PARENT2 (1 << 1)
+#define RESULT (1 << 2)
+#define STALE (1 << 3)
+
typedef struct commit_object {
git_oid oid;
uint32_t time;
unsigned int seen:1,
uninteresting:1,
topo_delay:1,
- parsed:1;
+ parsed:1,
+ flags : 4;
unsigned short in_degree;
unsigned short out_degree;
@@ -38,7 +48,8 @@ struct git_revwalk {
git_repository *repo;
git_odb *odb;
- git_hashtable *commits;
+ git_oidmap *commits;
+ git_pool commit_pool;
commit_list *iterator_topo;
commit_list *iterator_rand;
@@ -48,22 +59,47 @@ struct git_revwalk {
int (*get_next)(commit_object **, git_revwalk *);
int (*enqueue)(git_revwalk *, commit_object *);
- git_vector memory_alloc;
- size_t chunk_size;
-
unsigned walking:1;
unsigned int sorting;
+
+ /* merge base calculation */
+ commit_object *one;
+ git_vector twos;
};
+static int commit_time_cmp(void *a, void *b)
+{
+ commit_object *commit_a = (commit_object *)a;
+ commit_object *commit_b = (commit_object *)b;
+
+ return (commit_a->time < commit_b->time);
+}
+
static commit_list *commit_list_insert(commit_object *item, commit_list **list_p)
{
commit_list *new_list = git__malloc(sizeof(commit_list));
- new_list->item = item;
- new_list->next = *list_p;
+ if (new_list != NULL) {
+ new_list->item = item;
+ new_list->next = *list_p;
+ }
*list_p = new_list;
return new_list;
}
+static commit_list *commit_list_insert_by_date(commit_object *item, commit_list **list_p)
+{
+ commit_list **pp = list_p;
+ commit_list *p;
+
+ while ((p = *pp) != NULL) {
+ if (commit_time_cmp(p->item, item) < 0)
+ break;
+
+ pp = &p->next;
+ }
+
+ return commit_list_insert(item, pp);
+}
static void commit_list_free(commit_list **list_p)
{
commit_list *list = *list_p;
@@ -89,68 +125,36 @@ static commit_object *commit_list_pop(commit_list **stack)
return item;
}
-static int commit_time_cmp(void *a, void *b)
-{
- commit_object *commit_a = (commit_object *)a;
- commit_object *commit_b = (commit_object *)b;
-
- return (commit_a->time < commit_b->time);
-}
-
-static uint32_t object_table_hash(const void *key, int hash_id)
-{
- uint32_t r;
- const git_oid *id = key;
-
- memcpy(&r, id->id + (hash_id * sizeof(uint32_t)), sizeof(r));
- return r;
-}
-
-#define COMMITS_PER_CHUNK 128
-#define CHUNK_STEP 64
-#define PARENTS_PER_COMMIT ((CHUNK_STEP - sizeof(commit_object)) / sizeof(commit_object *))
-
-static int alloc_chunk(git_revwalk *walk)
-{
- void *chunk;
-
- chunk = git__calloc(COMMITS_PER_CHUNK, CHUNK_STEP);
- if (chunk == NULL)
- return GIT_ENOMEM;
-
- walk->chunk_size = 0;
- return git_vector_insert(&walk->memory_alloc, chunk);
-}
+#define PARENTS_PER_COMMIT 2
+#define COMMIT_ALLOC \
+ (sizeof(commit_object) + PARENTS_PER_COMMIT * sizeof(commit_object *))
static commit_object *alloc_commit(git_revwalk *walk)
{
- unsigned char *chunk;
-
- if (walk->chunk_size == COMMITS_PER_CHUNK)
- alloc_chunk(walk);
-
- chunk = git_vector_get(&walk->memory_alloc, walk->memory_alloc.length - 1);
- chunk += (walk->chunk_size * CHUNK_STEP);
- walk->chunk_size++;
-
- return (commit_object *)chunk;
+ return (commit_object *)git_pool_malloc(&walk->commit_pool, COMMIT_ALLOC);
}
-static commit_object **alloc_parents(commit_object *commit, size_t n_parents)
+static commit_object **alloc_parents(
+ git_revwalk *walk, commit_object *commit, size_t n_parents)
{
if (n_parents <= PARENTS_PER_COMMIT)
- return (commit_object **)((unsigned char *)commit + sizeof(commit_object));
+ return (commit_object **)((char *)commit + sizeof(commit_object));
- return git__malloc(n_parents * sizeof(commit_object *));
+ return (commit_object **)git_pool_malloc(
+ &walk->commit_pool, (uint32_t)(n_parents * sizeof(commit_object *)));
}
static commit_object *commit_lookup(git_revwalk *walk, const git_oid *oid)
{
commit_object *commit;
+ khiter_t pos;
+ int ret;
- if ((commit = git_hashtable_lookup(walk->commits, oid)) != NULL)
- return commit;
+ /* lookup and reserve space if not already present */
+ pos = kh_get(oid, walk->commits, oid);
+ if (pos != kh_end(walk->commits))
+ return kh_value(walk->commits, pos);
commit = alloc_commit(walk);
if (commit == NULL)
@@ -158,17 +162,16 @@ static commit_object *commit_lookup(git_revwalk *walk, const git_oid *oid)
git_oid_cpy(&commit->oid, oid);
- if (git_hashtable_insert(walk->commits, &commit->oid, commit) < GIT_SUCCESS) {
- git__free(commit);
- return NULL;
- }
+ pos = kh_put(oid, walk->commits, &commit->oid, &ret);
+ assert(ret != 0);
+ kh_value(walk->commits, pos) = commit;
return commit;
}
static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawobj *raw)
{
- const int parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
+ const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
unsigned char *buffer = raw->data;
unsigned char *buffer_end = buffer + raw->len;
@@ -185,39 +188,44 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
buffer += parent_len;
}
- commit->parents = alloc_parents(commit, parents);
- if (commit->parents == NULL)
- return GIT_ENOMEM;
+ commit->parents = alloc_parents(walk, commit, parents);
+ GITERR_CHECK_ALLOC(commit->parents);
buffer = parents_start;
for (i = 0; i < parents; ++i) {
git_oid oid;
- if (git_oid_fromstr(&oid, (char *)buffer + strlen("parent ")) < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Parent object is corrupted");
+ if (git_oid_fromstr(&oid, (char *)buffer + strlen("parent ")) < 0)
+ return -1;
commit->parents[i] = commit_lookup(walk, &oid);
if (commit->parents[i] == NULL)
- return GIT_ENOMEM;
+ return -1;
buffer += parent_len;
}
commit->out_degree = (unsigned short)parents;
- if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Object is corrupted");
+ if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL) {
+ giterr_set(GITERR_ODB, "Failed to parse commit. Object is corrupted");
+ return -1;
+ }
buffer = memchr(buffer, '>', buffer_end - buffer);
- if (buffer == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Can't find author");
+ if (buffer == NULL) {
+ giterr_set(GITERR_ODB, "Failed to parse commit. Can't find author");
+ return -1;
+ }
- if (git__strtol32(&commit_time, (char *)buffer + 2, NULL, 10) < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Can't parse commit time");
+ if (git__strtol32(&commit_time, (char *)buffer + 2, NULL, 10) < 0) {
+ giterr_set(GITERR_ODB, "Failed to parse commit. Can't parse commit time");
+ return -1;
+ }
commit->time = (time_t)commit_time;
commit->parsed = 1;
- return GIT_SUCCESS;
+ return 0;
}
static int commit_parse(git_revwalk *walk, commit_object *commit)
@@ -226,19 +234,159 @@ static int commit_parse(git_revwalk *walk, commit_object *commit)
int error;
if (commit->parsed)
- return GIT_SUCCESS;
+ return 0;
- if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to parse commit. Can't read object");
+ if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0)
+ return error;
if (obj->raw.type != GIT_OBJ_COMMIT) {
git_odb_object_free(obj);
- return git__throw(GIT_EOBJTYPE, "Failed to parse commit. Object is no commit object");
+ giterr_set(GITERR_INVALID, "Failed to parse commit. Object is no commit object");
+ return -1;
}
error = commit_quick_parse(walk, commit, &obj->raw);
git_odb_object_free(obj);
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse commit");
+ return error;
+}
+
+static int interesting(git_pqueue *list)
+{
+ unsigned int i;
+ for (i = 1; i < git_pqueue_size(list); i++) {
+ commit_object *commit = list->d[i];
+ if ((commit->flags & STALE) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int merge_bases_many(commit_list **out, git_revwalk *walk, commit_object *one, git_vector *twos)
+{
+ int error;
+ unsigned int i;
+ commit_object *two;
+ commit_list *result = NULL, *tmp = NULL;
+ git_pqueue list;
+
+ /* if the commit is repeated, we have a our merge base already */
+ git_vector_foreach(twos, i, two) {
+ if (one == two)
+ return commit_list_insert(one, out) ? 0 : -1;
+ }
+
+ if (git_pqueue_init(&list, twos->length * 2, commit_time_cmp) < 0)
+ return -1;
+
+ if (commit_parse(walk, one) < 0)
+ return -1;
+
+ one->flags |= PARENT1;
+ if (git_pqueue_insert(&list, one) < 0)
+ return -1;
+
+ git_vector_foreach(twos, i, two) {
+ commit_parse(walk, two);
+ two->flags |= PARENT2;
+ if (git_pqueue_insert(&list, two) < 0)
+ return -1;
+ }
+
+ /* as long as there are non-STALE commits */
+ while (interesting(&list)) {
+ commit_object *commit;
+ int flags;
+
+ commit = git_pqueue_pop(&list);
+
+ flags = commit->flags & (PARENT1 | PARENT2 | STALE);
+ if (flags == (PARENT1 | PARENT2)) {
+ if (!(commit->flags & RESULT)) {
+ commit->flags |= RESULT;
+ if (commit_list_insert(commit, &result) == NULL)
+ return -1;
+ }
+ /* we mark the parents of a merge stale */
+ flags |= STALE;
+ }
+
+ for (i = 0; i < commit->out_degree; i++) {
+ commit_object *p = commit->parents[i];
+ if ((p->flags & flags) == flags)
+ continue;
+
+ if ((error = commit_parse(walk, p)) < GIT_SUCCESS)
+ return error;
+
+ p->flags |= flags;
+ if (git_pqueue_insert(&list, p) < 0)
+ return -1;
+ }
+ }
+
+ git_pqueue_free(&list);
+
+ /* filter out any stale commits in the results */
+ tmp = result;
+ result = NULL;
+
+ while (tmp) {
+ struct commit_list *next = tmp->next;
+ if (!(tmp->item->flags & STALE))
+ if (commit_list_insert_by_date(tmp->item, &result) == NULL)
+ return -1;
+
+ git__free(tmp);
+ tmp = next;
+ }
+
+ *out = result;
+ return 0;
+}
+
+int git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *two)
+{
+ git_revwalk *walk;
+ git_vector list;
+ commit_list *result = NULL;
+ commit_object *commit;
+ void *contents[1];
+
+ if (git_revwalk_new(&walk, repo) < 0)
+ return -1;
+
+ commit = commit_lookup(walk, two);
+ if (commit == NULL)
+ goto on_error;
+
+ /* This is just one value, so we can do it on the stack */
+ memset(&list, 0x0, sizeof(git_vector));
+ contents[0] = commit;
+ list.length = 1;
+ list.contents = contents;
+
+ commit = commit_lookup(walk, one);
+ if (commit == NULL)
+ goto on_error;
+
+ if (merge_bases_many(&result, walk, commit, &list) < 0)
+ goto on_error;
+
+ if (!result) {
+ git_revwalk_free(walk);
+ return GIT_ENOTFOUND;
+ }
+
+ git_oid_cpy(out, &result->item->oid);
+ commit_list_free(&result);
+ git_revwalk_free(walk);
+
+ return 0;
+
+on_error:
+ git_revwalk_free(walk);
+ return -1;
}
static void mark_uninteresting(commit_object *commit)
@@ -248,6 +396,10 @@ static void mark_uninteresting(commit_object *commit)
commit->uninteresting = 1;
+ /* This means we've reached a merge base, so there's no need to walk any more */
+ if ((commit->flags & (RESULT | STALE)) == RESULT)
+ return;
+
for (i = 0; i < commit->out_degree; ++i)
if (!commit->parents[i]->uninteresting)
mark_uninteresting(commit->parents[i]);
@@ -261,12 +413,12 @@ static int process_commit(git_revwalk *walk, commit_object *commit, int hide)
mark_uninteresting(commit);
if (commit->seen)
- return GIT_SUCCESS;
+ return 0;
commit->seen = 1;
- if ((error = commit_parse(walk, commit)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to process commit");
+ if ((error = commit_parse(walk, commit)) < 0)
+ return error;
return walk->enqueue(walk, commit);
}
@@ -274,13 +426,12 @@ static int process_commit(git_revwalk *walk, commit_object *commit, int hide)
static int process_commit_parents(git_revwalk *walk, commit_object *commit)
{
unsigned short i;
- int error = GIT_SUCCESS;
+ int error = 0;
- for (i = 0; i < commit->out_degree && error == GIT_SUCCESS; ++i) {
+ for (i = 0; i < commit->out_degree && !error; ++i)
error = process_commit(walk, commit->parents[i], commit->uninteresting);
- }
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to process commit parents");
+ return error;
}
static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting)
@@ -289,9 +440,17 @@ static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting)
commit = commit_lookup(walk, oid);
if (commit == NULL)
- return git__throw(GIT_ENOTFOUND, "Failed to push commit. Object not found");
+ return -1; /* error already reported by failed lookup */
- return process_commit(walk, commit, uninteresting);
+ commit->uninteresting = uninteresting;
+ if (walk->one == NULL && !uninteresting) {
+ walk->one = commit;
+ } else {
+ if (git_vector_insert(&walk->twos, commit) < 0)
+ return -1;
+ }
+
+ return 0;
}
int git_revwalk_push(git_revwalk *walk, const git_oid *oid)
@@ -307,6 +466,16 @@ int git_revwalk_hide(git_revwalk *walk, const git_oid *oid)
return push_commit(walk, oid, 1);
}
+static int push_ref(git_revwalk *walk, const char *refname, int hide)
+{
+ git_oid oid;
+
+ if (git_reference_name_to_oid(&oid, walk->repo, refname) < 0)
+ return -1;
+
+ return push_commit(walk, &oid, hide);
+}
+
struct push_cb_data {
git_revwalk *walk;
const char *glob;
@@ -317,30 +486,16 @@ static int push_glob_cb(const char *refname, void *data_)
{
struct push_cb_data *data = (struct push_cb_data *)data_;
- if (!git__fnmatch(data->glob, refname, 0)) {
- git_reference *ref, *resolved;
- int error;
-
- error = git_reference_lookup(&ref, data->walk->repo, refname);
- if (error < GIT_SUCCESS)
- return error;
- error = git_reference_resolve(&resolved, ref);
- git_reference_free(ref);
- if (error < GIT_SUCCESS)
- return error;
- error = push_commit(data->walk, git_reference_oid(resolved), data->hide);
- git_reference_free(resolved);
- return error;
- }
+ if (!git__fnmatch(data->glob, refname, 0))
+ return push_ref(data->walk, refname, data->hide);
- return GIT_SUCCESS;
+ return 0;
}
static int push_glob(git_revwalk *walk, const char *glob, int hide)
{
git_buf buf = GIT_BUF_INIT;
struct push_cb_data data;
- int error;
regex_t preg;
assert(walk && glob);
@@ -355,28 +510,33 @@ static int push_glob(git_revwalk *walk, const char *glob, int hide)
/* If no '?', '*' or '[' exist, we append '/ *' to the glob */
memset(&preg, 0x0, sizeof(regex_t));
if (regcomp(&preg, "[?*[]", REG_EXTENDED)) {
- error = git__throw(GIT_EOSERR, "Regex failed to compile");
- goto cleanup;
+ giterr_set(GITERR_OS, "Regex failed to compile");
+ git_buf_free(&buf);
+ return -1;
}
if (regexec(&preg, glob, 0, NULL, 0))
git_buf_puts(&buf, "/*");
- if (git_buf_oom(&buf)) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ if (git_buf_oom(&buf))
+ goto on_error;
data.walk = walk;
data.glob = git_buf_cstr(&buf);
data.hide = hide;
- error = git_reference_foreach(walk->repo, GIT_REF_LISTALL, push_glob_cb, &data);
+ if (git_reference_foreach(
+ walk->repo, GIT_REF_LISTALL, push_glob_cb, &data) < 0)
+ goto on_error;
-cleanup:
regfree(&preg);
git_buf_free(&buf);
- return error;
+ return 0;
+
+on_error:
+ regfree(&preg);
+ git_buf_free(&buf);
+ return -1;
}
int git_revwalk_push_glob(git_revwalk *walk, const char *glob)
@@ -391,37 +551,28 @@ int git_revwalk_hide_glob(git_revwalk *walk, const char *glob)
return push_glob(walk, glob, 1);
}
-static int push_head(git_revwalk *walk, int hide)
-{
- git_reference *ref, *resolved;
- int error;
-
- error = git_reference_lookup(&ref, walk->repo, "HEAD");
- if (error < GIT_SUCCESS) {
- return error;
- }
- error = git_reference_resolve(&resolved, ref);
- if (error < GIT_SUCCESS) {
- return error;
- }
- git_reference_free(ref);
-
- error = push_commit(walk, git_reference_oid(resolved), hide);
-
- git_reference_free(resolved);
- return error;
-}
-
int git_revwalk_push_head(git_revwalk *walk)
{
assert(walk);
- return push_head(walk, 0);
+ return push_ref(walk, GIT_HEAD_FILE, 0);
}
int git_revwalk_hide_head(git_revwalk *walk)
{
assert(walk);
- return push_head(walk, 1);
+ return push_ref(walk, GIT_HEAD_FILE, 1);
+}
+
+int git_revwalk_push_ref(git_revwalk *walk, const char *refname)
+{
+ assert(walk && refname);
+ return push_ref(walk, refname, 0);
+}
+
+int git_revwalk_hide_ref(git_revwalk *walk, const char *refname)
+{
+ assert(walk && refname);
+ return push_ref(walk, refname, 1);
}
static int revwalk_enqueue_timesort(git_revwalk *walk, commit_object *commit)
@@ -431,7 +582,7 @@ static int revwalk_enqueue_timesort(git_revwalk *walk, commit_object *commit)
static int revwalk_enqueue_unsorted(git_revwalk *walk, commit_object *commit)
{
- return commit_list_insert(commit, &walk->iterator_rand) ? GIT_SUCCESS : GIT_ENOMEM;
+ return commit_list_insert(commit, &walk->iterator_rand) ? 0 : -1;
}
static int revwalk_next_timesort(commit_object **object_out, git_revwalk *walk)
@@ -440,16 +591,16 @@ static int revwalk_next_timesort(commit_object **object_out, git_revwalk *walk)
commit_object *next;
while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) {
- if ((error = process_commit_parents(walk, next)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to load next revision");
+ if ((error = process_commit_parents(walk, next)) < 0)
+ return error;
if (!next->uninteresting) {
*object_out = next;
- return GIT_SUCCESS;
+ return 0;
}
}
- return git__throw(GIT_EREVWALKOVER, "Failed to load next revision");
+ return GIT_EREVWALKOVER;
}
static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk)
@@ -458,16 +609,16 @@ static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk)
commit_object *next;
while ((next = commit_list_pop(&walk->iterator_rand)) != NULL) {
- if ((error = process_commit_parents(walk, next)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to load next revision");
+ if ((error = process_commit_parents(walk, next)) < 0)
+ return error;
if (!next->uninteresting) {
*object_out = next;
- return GIT_SUCCESS;
+ return 0;
}
}
- return git__throw(GIT_EREVWALKOVER, "Failed to load next revision");
+ return GIT_EREVWALKOVER;
}
static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk)
@@ -478,7 +629,7 @@ static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk)
for (;;) {
next = commit_list_pop(&walk->iterator_topo);
if (next == NULL)
- return git__throw(GIT_EREVWALKOVER, "Failed to load next revision");
+ return GIT_EREVWALKOVER;
if (next->in_degree > 0) {
next->topo_delay = 1;
@@ -490,58 +641,83 @@ static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk)
if (--parent->in_degree == 0 && parent->topo_delay) {
parent->topo_delay = 0;
- commit_list_insert(parent, &walk->iterator_topo);
+ if (commit_list_insert(parent, &walk->iterator_topo) == NULL)
+ return -1;
}
}
*object_out = next;
- return GIT_SUCCESS;
+ return 0;
}
}
static int revwalk_next_reverse(commit_object **object_out, git_revwalk *walk)
{
*object_out = commit_list_pop(&walk->iterator_reverse);
- return *object_out ? GIT_SUCCESS : GIT_EREVWALKOVER;
+ return *object_out ? 0 : GIT_EREVWALKOVER;
}
static int prepare_walk(git_revwalk *walk)
{
int error;
- commit_object *next;
+ unsigned int i;
+ commit_object *next, *two;
+ commit_list *bases = NULL;
+
+ /*
+ * If walk->one is NULL, there were no positive references,
+ * so we know that the walk is already over.
+ */
+ if (walk->one == NULL)
+ return GIT_EREVWALKOVER;
+
+ /* first figure out what the merge bases are */
+ if (merge_bases_many(&bases, walk, walk->one, &walk->twos) < 0)
+ return -1;
+
+ commit_list_free(&bases);
+ if (process_commit(walk, walk->one, walk->one->uninteresting) < 0)
+ return -1;
+
+ git_vector_foreach(&walk->twos, i, two) {
+ if (process_commit(walk, two, two->uninteresting) < 0)
+ return -1;
+ }
if (walk->sorting & GIT_SORT_TOPOLOGICAL) {
unsigned short i;
- while ((error = walk->get_next(&next, walk)) == GIT_SUCCESS) {
+ while ((error = walk->get_next(&next, walk)) == 0) {
for (i = 0; i < next->out_degree; ++i) {
commit_object *parent = next->parents[i];
parent->in_degree++;
}
- commit_list_insert(next, &walk->iterator_topo);
+ if (commit_list_insert(next, &walk->iterator_topo) == NULL)
+ return -1;
}
if (error != GIT_EREVWALKOVER)
- return git__rethrow(error, "Failed to prepare revision walk");
+ return error;
walk->get_next = &revwalk_next_toposort;
}
if (walk->sorting & GIT_SORT_REVERSE) {
- while ((error = walk->get_next(&next, walk)) == GIT_SUCCESS)
- commit_list_insert(next, &walk->iterator_reverse);
+ while ((error = walk->get_next(&next, walk)) == 0)
+ if (commit_list_insert(next, &walk->iterator_reverse) == NULL)
+ return -1;
if (error != GIT_EREVWALKOVER)
- return git__rethrow(error, "Failed to prepare revision walk");
+ return error;
walk->get_next = &revwalk_next_reverse;
}
walk->walking = 1;
- return GIT_SUCCESS;
+ return 0;
}
@@ -550,69 +726,48 @@ static int prepare_walk(git_revwalk *walk)
int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
{
- int error;
git_revwalk *walk;
walk = git__malloc(sizeof(git_revwalk));
- if (walk == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(walk);
memset(walk, 0x0, sizeof(git_revwalk));
- walk->commits = git_hashtable_alloc(64,
- object_table_hash,
- (git_hash_keyeq_ptr)git_oid_cmp);
-
- if (walk->commits == NULL) {
- git__free(walk);
- return GIT_ENOMEM;
- }
+ walk->commits = git_oidmap_alloc();
+ GITERR_CHECK_ALLOC(walk->commits);
- git_pqueue_init(&walk->iterator_time, 8, commit_time_cmp);
- git_vector_init(&walk->memory_alloc, 8, NULL);
- alloc_chunk(walk);
+ if (git_pqueue_init(&walk->iterator_time, 8, commit_time_cmp) < 0 ||
+ git_vector_init(&walk->twos, 4, NULL) < 0 ||
+ git_pool_init(&walk->commit_pool, 1,
+ git_pool__suggest_items_per_page(COMMIT_ALLOC) * COMMIT_ALLOC) < 0)
+ return -1;
walk->get_next = &revwalk_next_unsorted;
walk->enqueue = &revwalk_enqueue_unsorted;
walk->repo = repo;
- error = git_repository_odb(&walk->odb, repo);
- if (error < GIT_SUCCESS) {
+ if (git_repository_odb(&walk->odb, repo) < 0) {
git_revwalk_free(walk);
- return error;
+ return -1;
}
*revwalk_out = walk;
- return GIT_SUCCESS;
+ return 0;
}
void git_revwalk_free(git_revwalk *walk)
{
- unsigned int i;
- commit_object *commit;
-
if (walk == NULL)
return;
git_revwalk_reset(walk);
git_odb_free(walk->odb);
- /* if the parent has more than PARENTS_PER_COMMIT parents,
- * we had to allocate a separate array for those parents.
- * make sure it's being free'd */
- GIT_HASHTABLE_FOREACH_VALUE(walk->commits, commit, {
- if (commit->out_degree > PARENTS_PER_COMMIT)
- git__free(commit->parents);
- });
-
- git_hashtable_free(walk->commits);
+ git_oidmap_free(walk->commits);
+ git_pool_clear(&walk->commit_pool);
git_pqueue_free(&walk->iterator_time);
-
- for (i = 0; i < walk->memory_alloc.length; ++i)
- git__free(git_vector_get(&walk->memory_alloc, i));
-
- git_vector_free(&walk->memory_alloc);
+ git_vector_free(&walk->twos);
git__free(walk);
}
@@ -648,8 +803,8 @@ int git_revwalk_next(git_oid *oid, git_revwalk *walk)
assert(walk && oid);
if (!walk->walking) {
- if ((error = prepare_walk(walk)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to load next revision");
+ if ((error = prepare_walk(walk)) < 0)
+ return error;
}
error = walk->get_next(&next, walk);
@@ -659,11 +814,10 @@ int git_revwalk_next(git_oid *oid, git_revwalk *walk)
return GIT_EREVWALKOVER;
}
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to load next revision");
+ if (!error)
+ git_oid_cpy(oid, &next->oid);
- git_oid_cpy(oid, &next->oid);
- return GIT_SUCCESS;
+ return error;
}
void git_revwalk_reset(git_revwalk *walk)
@@ -672,12 +826,12 @@ void git_revwalk_reset(git_revwalk *walk)
assert(walk);
- GIT_HASHTABLE_FOREACH_VALUE(walk->commits, commit,
+ kh_foreach_value(walk->commits, commit, {
commit->seen = 0;
commit->in_degree = 0;
commit->topo_delay = 0;
commit->uninteresting = 0;
- );
+ });
git_pqueue_clear(&walk->iterator_time);
commit_list_free(&walk->iterator_topo);
diff --git a/src/sha1.c b/src/sha1.c
index af3c2d2d0..8aaedeb8f 100644
--- a/src/sha1.c
+++ b/src/sha1.c
@@ -232,7 +232,7 @@ void git__blk_SHA1_Init(blk_SHA_CTX *ctx)
ctx->H[4] = 0xc3d2e1f0;
}
-void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
+void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, size_t len)
{
unsigned int lenW = ctx->size & 63;
@@ -242,7 +242,7 @@ void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
if (lenW) {
unsigned int left = 64 - lenW;
if (len < left)
- left = len;
+ left = (unsigned int)len;
memcpy(lenW + (char *)ctx->W, data, left);
lenW = (lenW + left) & 63;
len -= left;
diff --git a/src/sha1.h b/src/sha1.h
index 117e93106..93a244d76 100644
--- a/src/sha1.h
+++ b/src/sha1.h
@@ -12,7 +12,7 @@ typedef struct {
} blk_SHA_CTX;
void git__blk_SHA1_Init(blk_SHA_CTX *ctx);
-void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len);
+void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, size_t len);
void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);
#define SHA_CTX blk_SHA_CTX
diff --git a/src/sha1_lookup.c b/src/sha1_lookup.c
index 58d70aeb7..096da1739 100644
--- a/src/sha1_lookup.c
+++ b/src/sha1_lookup.c
@@ -158,7 +158,8 @@ int sha1_entry_pos(const void *table,
#endif
if (!(lo <= mi && mi < hi)) {
- return git__throw(GIT_ERROR, "Assertion failure. Binary search invariant is false");
+ giterr_set(GITERR_INVALID, "Assertion failure. Binary search invariant is false");
+ return -1;
}
mi_key = base + elem_size * mi + key_offset;
diff --git a/src/signature.c b/src/signature.c
index 1b6ba2149..4d6d11c70 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -38,31 +38,38 @@ static const char *skip_trailing_spaces(const char *buffer_start, const char *bu
return buffer_end;
}
+static int signature_error(const char *msg)
+{
+ giterr_set(GITERR_INVALID, "Failed to parse signature - %s", msg);
+ return -1;
+}
+
static int process_trimming(const char *input, char **storage, const char *input_end, int fail_when_empty)
{
const char *left, *right;
- int trimmed_input_length;
+ size_t trimmed_input_length;
+
+ assert(storage);
left = skip_leading_spaces(input, input_end);
right = skip_trailing_spaces(input, input_end - 1);
if (right < left) {
if (fail_when_empty)
- return git__throw(GIT_EINVALIDARGS, "Failed to trim. Input is either empty or only contains spaces");
- else
- right = left - 1;
+ return signature_error("input is either empty of contains only spaces");
+
+ right = left - 1;
}
trimmed_input_length = right - left + 1;
*storage = git__malloc(trimmed_input_length + 1);
- if (*storage == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(*storage);
memcpy(*storage, left, trimmed_input_length);
(*storage)[trimmed_input_length] = 0;
- return GIT_SUCCESS;
+ return 0;
}
int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset)
@@ -74,23 +81,14 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
*sig_out = NULL;
- if ((p = git__malloc(sizeof(git_signature))) == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
-
- memset(p, 0x0, sizeof(git_signature));
-
- error = process_trimming(name, &p->name, name + strlen(name), 1);
- if (error < GIT_SUCCESS) {
- git__rethrow(GIT_EINVALIDARGS, "Failed to create signature. 'name' argument is invalid");
- goto cleanup;
- }
+ p = git__calloc(1, sizeof(git_signature));
+ GITERR_CHECK_ALLOC(p);
- error = process_trimming(email, &p->email, email + strlen(email), 1);
- if (error < GIT_SUCCESS) {
- git__rethrow(GIT_EINVALIDARGS, "Failed to create signature. 'email' argument is invalid");
- goto cleanup;
+ if ((error = process_trimming(name, &p->name, name + strlen(name), 1)) < 0 ||
+ (error = process_trimming(email, &p->email, email + strlen(email), 1)) < 0)
+ {
+ git_signature_free(p);
+ return error;
}
p->when.time = time;
@@ -98,24 +96,19 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
*sig_out = p;
- return error;
-
-cleanup:
- git_signature_free(p);
- return error;
+ return 0;
}
git_signature *git_signature_dup(const git_signature *sig)
{
git_signature *new;
- if (git_signature_new(&new, sig->name, sig->email, sig->when.time, sig->when.offset) < GIT_SUCCESS)
+ if (git_signature_new(&new, sig->name, sig->email, sig->when.time, sig->when.offset) < 0)
return NULL;
return new;
}
int git_signature_now(git_signature **sig_out, const char *name, const char *email)
{
- int error;
time_t now;
time_t offset;
struct tm *utc_tm, *local_tm;
@@ -148,12 +141,18 @@ int git_signature_now(git_signature **sig_out, const char *name, const char *ema
if (local_tm->tm_isdst)
offset += 60;
- if ((error = git_signature_new(&sig, name, email, now, (int)offset)) < GIT_SUCCESS)
- return error;
+ if (git_signature_new(&sig, name, email, now, (int)offset) < 0)
+ return -1;
*sig_out = sig;
- return error;
+ return 0;
+}
+
+static int timezone_error(const char *msg)
+{
+ giterr_set(GITERR_INVALID, "Failed to parse TZ offset - %s", msg);
+ return -1;
}
static int parse_timezone_offset(const char *buffer, int *offset_out)
@@ -172,28 +171,28 @@ static int parse_timezone_offset(const char *buffer, int *offset_out)
}
if (offset_start[0] != '-' && offset_start[0] != '+')
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It doesn't start with '+' or '-'");
+ return timezone_error("does not start with '+' or '-'");
if (offset_start[1] < '0' || offset_start[1] > '9')
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset.");
+ return timezone_error("expected initial digit");
if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It isn't a number");
+ return timezone_error("not a valid number");
if (offset_end - offset_start != 5)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Invalid length");
+ return timezone_error("invalid length");
if (dec_offset > 1400)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Value too large");
+ return timezone_error("value too large");
hours = dec_offset / 100;
mins = dec_offset % 100;
if (hours > 14) // see http://www.worldtimezone.com/faq.html
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Hour value too large");
+ return timezone_error("hour value too large");
if (mins > 59)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Minute value too large");
+ return timezone_error("minutes value too large");
offset = (hours * 60) + mins;
@@ -202,22 +201,22 @@ static int parse_timezone_offset(const char *buffer, int *offset_out)
*offset_out = offset;
- return GIT_SUCCESS;
+ return 0;
}
static int process_next_token(const char **buffer_out, char **storage,
const char *token_end, const char *right_boundary)
{
int error = process_trimming(*buffer_out, storage, token_end, 0);
- if (error < GIT_SUCCESS)
+ if (error < 0)
return error;
*buffer_out = token_end + 1;
if (*buffer_out > right_boundary)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Signature too short");
+ return signature_error("signature is too short");
- return GIT_SUCCESS;
+ return 0;
}
static const char *scan_for_previous_token(const char *buffer, const char *left_boundary)
@@ -241,17 +240,17 @@ static int parse_time(git_time_t *time_out, const char *buffer)
int time;
int error;
- if (*buffer == '+' || *buffer == '-')
- return git__throw(GIT_ERROR, "Failed while parsing time. '%s' rather look like a timezone offset.", buffer);
+ if (*buffer == '+' || *buffer == '-') {
+ giterr_set(GITERR_INVALID, "Failed while parsing time. '%s' actually looks like a timezone offset.", buffer);
+ return -1;
+ }
error = git__strtol32(&time, buffer, &buffer, 10);
- if (error < GIT_SUCCESS)
- return error;
+ if (!error)
+ *time_out = (git_time_t)time;
- *time_out = (git_time_t)time;
-
- return GIT_SUCCESS;
+ return error;
}
int git_signature__parse(git_signature *sig, const char **buffer_out,
@@ -259,40 +258,40 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
{
const char *buffer = *buffer_out;
const char *line_end, *name_end, *email_end, *tz_start, *time_start;
- int error = GIT_SUCCESS;
+ int error = 0;
memset(sig, 0x0, sizeof(git_signature));
if ((line_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. No newline given");
+ return signature_error("no newline given");
if (header) {
const size_t header_len = strlen(header);
if (memcmp(buffer, header, header_len) != 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Expected prefix '%s' doesn't match actual", header);
+ return signature_error("expected prefix doesn't match actual");
buffer += header_len;
}
if (buffer > line_end)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Signature too short");
+ return signature_error("signature too short");
if ((name_end = strchr(buffer, '<')) == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Cannot find '<' in signature");
+ return signature_error("character '<' not allowed in signature");
if ((email_end = strchr(name_end, '>')) == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Cannot find '>' in signature");
+ return signature_error("character '>' not allowed in signature");
if (email_end < name_end)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Malformed e-mail");
+ return signature_error("malformed e-mail");
error = process_next_token(&buffer, &sig->name, name_end, line_end);
- if (error < GIT_SUCCESS)
+ if (error < 0)
return error;
error = process_next_token(&buffer, &sig->email, email_end, line_end);
- if (error < GIT_SUCCESS)
+ if (error < 0)
return error;
tz_start = scan_for_previous_token(line_end - 1, buffer);
@@ -301,19 +300,19 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
goto clean_exit; /* No timezone nor date */
time_start = scan_for_previous_token(tz_start - 1, buffer);
- if (time_start == NULL || parse_time(&sig->when.time, time_start) < GIT_SUCCESS) {
+ if (time_start == NULL || parse_time(&sig->when.time, time_start) < 0) {
/* The tz_start might point at the time */
parse_time(&sig->when.time, tz_start);
goto clean_exit;
}
- if (parse_timezone_offset(tz_start, &sig->when.offset) < GIT_SUCCESS) {
+ if (parse_timezone_offset(tz_start, &sig->when.offset) < 0) {
sig->when.time = 0; /* Bogus timezone, we reset the time */
}
clean_exit:
*buffer_out = line_end + 1;
- return GIT_SUCCESS;
+ return 0;
}
void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig)
diff --git a/src/status.c b/src/status.c
index 106e5005d..356cbeb98 100644
--- a/src/status.c
+++ b/src/status.c
@@ -15,6 +15,197 @@
#include "repository.h"
#include "ignore.h"
+#include "git2/diff.h"
+#include "diff.h"
+
+static int resolve_head_to_tree(git_tree **tree, git_repository *repo)
+{
+ git_oid head_oid;
+ git_object *obj = NULL;
+
+ if (git_reference_name_to_oid(&head_oid, repo, GIT_HEAD_FILE) < 0) {
+ /* cannot resolve HEAD - probably brand new repo */
+ giterr_clear();
+ *tree = NULL;
+ return 0;
+ }
+
+ if (git_object_lookup(&obj, repo, &head_oid, GIT_OBJ_ANY) < 0)
+ goto fail;
+
+ switch (git_object_type(obj)) {
+ case GIT_OBJ_TREE:
+ *tree = (git_tree *)obj;
+ break;
+ case GIT_OBJ_COMMIT:
+ if (git_commit_tree(tree, (git_commit *)obj) < 0)
+ goto fail;
+ git_object_free(obj);
+ break;
+ default:
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ git_object_free(obj);
+ return -1;
+}
+
+static unsigned int index_delta2status(git_delta_t index_status)
+{
+ unsigned int st = GIT_STATUS_CURRENT;
+
+ switch (index_status) {
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_COPIED:
+ case GIT_DELTA_RENAMED:
+ st = GIT_STATUS_INDEX_NEW;
+ break;
+ case GIT_DELTA_DELETED:
+ st = GIT_STATUS_INDEX_DELETED;
+ break;
+ case GIT_DELTA_MODIFIED:
+ st = GIT_STATUS_INDEX_MODIFIED;
+ break;
+ default:
+ break;
+ }
+
+ return st;
+}
+
+static unsigned int workdir_delta2status(git_delta_t workdir_status)
+{
+ unsigned int st = GIT_STATUS_CURRENT;
+
+ switch (workdir_status) {
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_COPIED:
+ case GIT_DELTA_RENAMED:
+ case GIT_DELTA_UNTRACKED:
+ st = GIT_STATUS_WT_NEW;
+ break;
+ case GIT_DELTA_DELETED:
+ st = GIT_STATUS_WT_DELETED;
+ break;
+ case GIT_DELTA_MODIFIED:
+ st = GIT_STATUS_WT_MODIFIED;
+ break;
+ case GIT_DELTA_IGNORED:
+ st = GIT_STATUS_IGNORED;
+ break;
+ default:
+ break;
+ }
+
+ return st;
+}
+
+int git_status_foreach_ext(
+ git_repository *repo,
+ git_status_options *opts,
+ int (*cb)(const char *, unsigned int, void *),
+ void *cbdata)
+{
+ int err = 0, cmp;
+ git_diff_options diffopt;
+ git_diff_list *idx2head = NULL, *wd2idx = NULL;
+ git_tree *head = NULL;
+ git_status_show_t show =
+ opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ git_diff_delta *i2h, *w2i;
+ unsigned int i, j, i_max, j_max;
+
+ assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
+
+ switch (resolve_head_to_tree(&head, repo)) {
+ case 0: break;
+ case GIT_ENOTFOUND: return 0;
+ default: return -1;
+ }
+
+ memset(&diffopt, 0, sizeof(diffopt));
+ memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
+
+ if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
+ if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED;
+ if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED;
+ if ((opts->flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+ /* TODO: support EXCLUDE_SUBMODULES flag */
+
+ if (show != GIT_STATUS_SHOW_WORKDIR_ONLY && head != NULL &&
+ (err = git_diff_index_to_tree(repo, &diffopt, head, &idx2head)) < 0)
+ goto cleanup;
+
+ if (show != GIT_STATUS_SHOW_INDEX_ONLY &&
+ (err = git_diff_workdir_to_index(repo, &diffopt, &wd2idx)) < 0)
+ goto cleanup;
+
+ if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
+ for (i = 0; !err && i < idx2head->deltas.length; i++) {
+ i2h = GIT_VECTOR_GET(&idx2head->deltas, i);
+ err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata);
+ }
+ git_diff_list_free(idx2head);
+ idx2head = NULL;
+ }
+
+ i_max = idx2head ? idx2head->deltas.length : 0;
+ j_max = wd2idx ? wd2idx->deltas.length : 0;
+
+ for (i = 0, j = 0; !err && (i < i_max || j < j_max); ) {
+ i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
+ w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
+
+ cmp = !w2i ? -1 : !i2h ? 1 : strcmp(i2h->old_file.path, w2i->old_file.path);
+
+ if (cmp < 0) {
+ err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata);
+ i++;
+ } else if (cmp > 0) {
+ err = cb(w2i->old_file.path, workdir_delta2status(w2i->status), cbdata);
+ j++;
+ } else {
+ err = cb(i2h->old_file.path, index_delta2status(i2h->status) |
+ workdir_delta2status(w2i->status), cbdata);
+ i++; j++;
+ }
+ }
+
+cleanup:
+ git_tree_free(head);
+ git_diff_list_free(idx2head);
+ git_diff_list_free(wd2idx);
+ return err;
+}
+
+int git_status_foreach(
+ git_repository *repo,
+ int (*callback)(const char *, unsigned int, void *),
+ void *payload)
+{
+ git_status_options opts;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ return git_status_foreach_ext(repo, &opts, callback, payload);
+}
+
+
+/*
+ * the old stuff
+ */
+
struct status_entry {
git_index_time mtime;
@@ -76,15 +267,17 @@ static int status_entry_update_from_workdir(struct status_entry *e, const char*
{
struct stat filest;
- if (p_stat(full_path, &filest) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to determine status of file '%s'. Can't read file", full_path);
+ if (p_stat(full_path, &filest) < 0) {
+ giterr_set(GITERR_OS, "Cannot access file '%s'", full_path);
+ return GIT_ENOTFOUND;
+ }
if (e->mtime.seconds == (git_time_t)filest.st_mtime)
git_oid_cpy(&e->wt_oid, &e->index_oid);
else
git_odb_hashfile(&e->wt_oid, full_path, GIT_OBJ_BLOB);
- return GIT_SUCCESS;
+ return 0;
}
static int status_entry_update_flags(struct status_entry *e)
@@ -115,7 +308,7 @@ static int status_entry_update_flags(struct status_entry *e)
else if (git_oid_cmp(&e->index_oid, &e->wt_oid) != 0)
e->status_flags |= GIT_STATUS_WT_MODIFIED;
- return GIT_SUCCESS;
+ return 0;
}
static int status_entry_is_ignorable(struct status_entry *e)
@@ -126,432 +319,17 @@ static int status_entry_is_ignorable(struct status_entry *e)
static int status_entry_update_ignore(struct status_entry *e, git_ignores *ignores, const char *path)
{
- int error, ignored;
-
- if ((error = git_ignore__lookup(ignores, path, &ignored)) == GIT_SUCCESS &&
- ignored)
- e->status_flags =
- (e->status_flags & ~GIT_STATUS_WT_NEW) | GIT_STATUS_IGNORED;
-
- return error;
-}
-
-struct status_st {
- git_repository *repo;
- git_vector *vector;
- git_index *index;
- git_tree *tree;
- git_ignores *ignores;
-
- int workdir_path_len;
- git_buf head_tree_relative_path;
- int head_tree_relative_path_len;
- unsigned int tree_position;
- unsigned int index_position;
- int is_dir:1;
-};
-
-static int retrieve_head_tree(git_tree **tree_out, git_repository *repo)
-{
- git_reference *resolved_head_ref;
- git_commit *head_commit = NULL;
- git_tree *tree;
- int error = GIT_SUCCESS;
-
- *tree_out = NULL;
-
- error = git_repository_head(&resolved_head_ref, repo);
- /*
- * We assume that a situation where HEAD exists but can not be resolved is valid.
- * A new repository fits this description for instance.
- */
- if (error == GIT_ENOTFOUND)
- return GIT_SUCCESS;
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "HEAD can't be resolved");
-
- if ((error = git_commit_lookup(&head_commit, repo, git_reference_oid(resolved_head_ref))) < GIT_SUCCESS)
- return git__rethrow(error, "The tip of HEAD can't be retrieved");
-
- git_reference_free(resolved_head_ref);
-
- if ((error = git_commit_tree(&tree, head_commit)) < GIT_SUCCESS) {
- error = git__rethrow(error, "The tree of HEAD can't be retrieved");
- goto exit;
- }
-
- *tree_out = tree;
-
-exit:
- git_commit_free(head_commit);
- return error;
-}
-
-enum path_type {
- GIT_STATUS_PATH_NULL,
- GIT_STATUS_PATH_IGNORE,
- GIT_STATUS_PATH_FILE,
- GIT_STATUS_PATH_FOLDER,
-};
-
-static int dirent_cb(void *state, git_buf *full_path);
-static int alphasorted_futils_direach(
- git_buf *path, int (*fn)(void *, git_buf *), void *arg);
-
-static int process_folder(
- struct status_st *st,
- const git_tree_entry *tree_entry,
- git_buf *full_path,
- enum path_type path_type)
-{
- git_object *subtree = NULL;
- git_tree *pushed_tree = NULL;
- int error, pushed_tree_position = 0;
- git_otype tree_entry_type = GIT_OBJ_BAD;
-
- if (tree_entry != NULL) {
- tree_entry_type = git_tree_entry_type(tree_entry);
-
- switch (tree_entry_type) {
- case GIT_OBJ_TREE:
- error = git_tree_entry_2object(&subtree, ((git_object *)(st->tree))->repo, tree_entry);
- pushed_tree = st->tree;
- pushed_tree_position = st->tree_position;
- st->tree = (git_tree *)subtree;
- st->tree_position = 0;
- st->head_tree_relative_path_len += 1 + tree_entry->filename_len; /* path + '/' + name */
- break;
-
- case GIT_OBJ_BLOB:
- /* No op */
- break;
-
- case GIT_OBJ_COMMIT:
- /* TODO: proper submodule support */
- break;
-
- default:
- return git__throw(GIT_EINVALIDTYPE, "Unexpected tree entry type");
- }
- }
-
-
- if (full_path != NULL && path_type == GIT_STATUS_PATH_FOLDER) {
- git_ignores ignores, *old_ignores;
-
- if ((error = git_ignore__for_path(st->repo,
- full_path->ptr + st->workdir_path_len, &ignores)) == GIT_SUCCESS)
- {
- old_ignores = st->ignores;
- st->ignores = &ignores;
-
- error = alphasorted_futils_direach(full_path, dirent_cb, st);
-
- git_ignore__free(st->ignores);
- st->ignores = old_ignores;
- }
- } else {
- error = dirent_cb(st, NULL);
- }
-
- if (tree_entry_type == GIT_OBJ_TREE) {
- git_object_free(subtree);
- st->head_tree_relative_path_len -= 1 + tree_entry->filename_len;
- st->tree = pushed_tree;
- st->tree_position = pushed_tree_position;
- st->tree_position++;
- }
-
- return error;
-}
-
-static int store_if_changed(struct status_st *st, struct status_entry *e)
-{
- int error;
- if ((error = status_entry_update_flags(e)) < GIT_SUCCESS)
- return git__throw(error, "Failed to process the file '%s'. It doesn't exist in the workdir, in the HEAD nor in the index", e->path);
-
- if (status_entry_is_ignorable(e) &&
- (error = status_entry_update_ignore(e, st->ignores, e->path)) < GIT_SUCCESS)
- return error;
-
- if (e->status_flags == GIT_STATUS_CURRENT) {
- git__free(e);
- return GIT_SUCCESS;
- }
-
- return git_vector_insert(st->vector, e);
-}
-
-static int determine_status(
- struct status_st *st,
- int in_head, int in_index, int in_workdir,
- const git_tree_entry *tree_entry,
- const git_index_entry *index_entry,
- git_buf *full_path,
- const char *status_path,
- enum path_type path_type)
-{
- struct status_entry *e;
- int error = GIT_SUCCESS;
- git_otype tree_entry_type = GIT_OBJ_BAD;
-
- if (tree_entry != NULL)
- tree_entry_type = git_tree_entry_type(tree_entry);
-
- /* If we're dealing with a directory in the workdir, let's recursively tackle it first */
- if (path_type == GIT_STATUS_PATH_FOLDER)
- return process_folder(st, tree_entry, full_path, path_type);
-
- /* Are we dealing with a file somewhere? */
- if (in_workdir || in_index || (in_head && tree_entry_type == GIT_OBJ_BLOB)) {
- e = status_entry_new(NULL, status_path);
-
- if (in_head && tree_entry_type == GIT_OBJ_BLOB) {
- status_entry_update_from_tree_entry(e, tree_entry);
- st->tree_position++;
- }
-
- if (in_index) {
- status_entry_update_from_index_entry(e, index_entry);
- st->index_position++;
- }
-
- if (in_workdir)
- if ((error = status_entry_update_from_workdir(
- e, full_path->ptr)) < GIT_SUCCESS)
- return error; /* The callee has already set the error message */
-
- return store_if_changed(st, e);
- }
-
- /* Are we dealing with a subtree? */
- if (tree_entry_type == GIT_OBJ_TREE) {
- assert(in_head && !in_index && !in_workdir);
- return process_folder(st, tree_entry, full_path, path_type);
- }
-
- /* We're dealing with something else -- most likely a submodule;
- * skip it for now */
- if (in_head)
- st->tree_position++;
- if (in_index)
- st->index_position++;
- return GIT_SUCCESS;
-}
-
-static int path_type_from(git_buf *full_path, int is_dir)
-{
- if (full_path == NULL)
- return GIT_STATUS_PATH_NULL;
+ int ignored;
- if (!is_dir)
- return GIT_STATUS_PATH_FILE;
-
- if (!git__suffixcmp(full_path->ptr, "/" DOT_GIT "/"))
- return GIT_STATUS_PATH_IGNORE;
-
- return GIT_STATUS_PATH_FOLDER;
-}
-
-static const char *status_path(const char *first, const char *second, const char *third)
-{
- /* At least one of them can not be NULL */
- assert(first != NULL || second != NULL || third != NULL);
-
- /* TODO: Fixme. Ensure that when non null, they're all equal */
- if (first != NULL)
- return first;
-
- if (second != NULL)
- return second;
-
- return third;
-}
-
-static int compare(const char *left, const char *right)
-{
- if (left == NULL && right == NULL)
- return 0;
-
- if (left == NULL)
- return 1;
-
- if (right == NULL)
+ if (git_ignore__lookup(ignores, path, &ignored) < 0)
return -1;
- return strcmp(left, right);
-}
-
-/* Greatly inspired from JGit IndexTreeWalker */
-/* https://github.com/spearce/jgit/blob/ed47e29c777accfa78c6f50685a5df2b8f5b8ff5/org.spearce.jgit/src/org/spearce/jgit/lib/IndexTreeWalker.java#L88 */
-
-static int dirent_cb(void *state, git_buf *a)
-{
- const git_tree_entry *m;
- const git_index_entry *entry;
- enum path_type path_type;
- int cmpma, cmpmi, cmpai, error;
- const char *pm, *pa, *pi;
- const char *m_name, *i_name, *a_name;
- struct status_st *st = (struct status_st *)state;
-
- path_type = path_type_from(a, st->is_dir);
-
- if (path_type == GIT_STATUS_PATH_IGNORE)
- return GIT_SUCCESS; /* Let's skip the ".git" directory */
-
- a_name = (path_type != GIT_STATUS_PATH_NULL) ? a->ptr + st->workdir_path_len : NULL;
-
- while (1) {
- if (st->tree == NULL)
- m = NULL;
- else
- m = git_tree_entry_byindex(st->tree, st->tree_position);
-
- entry = git_index_get(st->index, st->index_position);
-
- if ((m == NULL) && (a == NULL) && (entry == NULL))
- return GIT_SUCCESS;
-
- if (m != NULL) {
- git_buf_truncate(&st->head_tree_relative_path,
- st->head_tree_relative_path_len);
- git_buf_joinpath(&st->head_tree_relative_path,
- st->head_tree_relative_path.ptr, m->filename);
- /* When the tree entry is a folder, append a forward slash to its name */
- if (git_tree_entry_type(m) == GIT_OBJ_TREE)
- git_path_to_dir(&st->head_tree_relative_path);
-
- error = git_buf_lasterror(&st->head_tree_relative_path);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "An error occured while "
- "determining the status of '%s'", a->ptr);
-
- m_name = st->head_tree_relative_path.ptr;
- } else
- m_name = NULL;
-
- i_name = (entry != NULL) ? entry->path : NULL;
-
- cmpma = compare(m_name, a_name);
- cmpmi = compare(m_name, i_name);
- cmpai = compare(a_name, i_name);
-
- pm = ((cmpma <= 0) && (cmpmi <= 0)) ? m_name : NULL;
- pa = ((cmpma >= 0) && (cmpai <= 0)) ? a_name : NULL;
- pi = ((cmpmi >= 0) && (cmpai >= 0)) ? i_name : NULL;
-
- if ((error = determine_status(st, pm != NULL, pi != NULL, pa != NULL,
- m, entry, a, status_path(pm, pi, pa), path_type)) < GIT_SUCCESS)
- return git__rethrow(error, "An error occured while determining the status of '%s'", a->ptr);
-
- if ((pa != NULL) || (path_type == GIT_STATUS_PATH_FOLDER))
- return GIT_SUCCESS;
- }
-}
-
-static int status_cmp(const void *a, const void *b)
-{
- const struct status_entry *entry_a = (const struct status_entry *)(a);
- const struct status_entry *entry_b = (const struct status_entry *)(b);
-
- return strcmp(entry_a->path, entry_b->path);
-}
-
-#define DEFAULT_SIZE 16
-
-int git_status_foreach(
- git_repository *repo,
- int (*callback)(const char *, unsigned int, void *),
- void *payload)
-{
- git_vector entries;
- git_ignores ignores;
- git_index *index = NULL;
- git_buf temp_path = GIT_BUF_INIT;
- struct status_st dirent_st = {0};
- int error = GIT_SUCCESS;
- unsigned int i;
- git_tree *tree;
- struct status_entry *e;
- const char *workdir;
-
- if ((workdir = git_repository_workdir(repo)) == NULL)
- return git__throw(GIT_ERROR,
- "Cannot retrieve status on a bare repository");
-
- if ((error = git_repository_index__weakptr(&index, repo)) < GIT_SUCCESS) {
- return git__rethrow(error,
- "Failed to determine statuses. Index can't be opened");
- }
-
- if ((error = retrieve_head_tree(&tree, repo)) < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to determine statuses");
- goto exit;
- }
-
- git_vector_init(&entries, DEFAULT_SIZE, status_cmp);
-
- dirent_st.repo = repo;
- dirent_st.vector = &entries;
- dirent_st.index = index;
- dirent_st.tree = tree;
- dirent_st.ignores = &ignores;
- dirent_st.workdir_path_len = strlen(workdir);
- git_buf_init(&dirent_st.head_tree_relative_path, 0);
- dirent_st.head_tree_relative_path_len = 0;
- dirent_st.tree_position = 0;
- dirent_st.index_position = 0;
- dirent_st.is_dir = 1;
-
- if (git_path_isdir(workdir)) {
- error = git__throw(GIT_EINVALIDPATH,
- "Failed to determine status of file '%s'. "
- "The given path doesn't lead to a folder", workdir);
- goto exit;
- }
-
- git_buf_sets(&temp_path, workdir);
-
- error = git_ignore__for_path(repo, "", dirent_st.ignores);
- if (error < GIT_SUCCESS)
- goto exit;
-
- error = alphasorted_futils_direach(
- &temp_path, dirent_cb, &dirent_st);
-
- if (error < GIT_SUCCESS)
- error = git__rethrow(error,
- "Failed to determine statuses. "
- "An error occured while processing the working directory");
-
- if ((error == GIT_SUCCESS) &&
- ((error = dirent_cb(&dirent_st, NULL)) < GIT_SUCCESS))
- error = git__rethrow(error,
- "Failed to determine statuses. "
- "An error occured while post-processing the HEAD tree and the index");
-
- for (i = 0; i < entries.length; ++i) {
- e = (struct status_entry *)git_vector_get(&entries, i);
-
- if (error == GIT_SUCCESS) {
- error = callback(e->path, e->status_flags, payload);
- if (error < GIT_SUCCESS)
- error = git__rethrow(error,
- "Failed to determine statuses. User callback failed");
- }
-
- git__free(e);
- }
+ if (ignored)
+ /* toggle off WT_NEW and on IGNORED */
+ e->status_flags =
+ (e->status_flags & ~GIT_STATUS_WT_NEW) | GIT_STATUS_IGNORED;
-exit:
- git_buf_free(&dirent_st.head_tree_relative_path);
- git_buf_free(&temp_path);
- git_vector_free(&entries);
- git_ignore__free(&ignores);
- git_tree_free(tree);
- return error;
+ return 0;
}
static int recurse_tree_entry(git_tree *tree, struct status_entry *e, const char *path)
@@ -559,236 +337,117 @@ static int recurse_tree_entry(git_tree *tree, struct status_entry *e, const char
char *dir_sep;
const git_tree_entry *tree_entry;
git_tree *subtree;
- int error = GIT_SUCCESS;
+ int error;
dir_sep = strchr(path, '/');
if (!dir_sep) {
- tree_entry = git_tree_entry_byname(tree, path);
- if (tree_entry == NULL)
- return GIT_SUCCESS; /* The leaf doesn't exist in the tree*/
-
- status_entry_update_from_tree_entry(e, tree_entry);
- return GIT_SUCCESS;
+ if ((tree_entry = git_tree_entry_byname(tree, path)) != NULL)
+ /* The leaf exists in the tree*/
+ status_entry_update_from_tree_entry(e, tree_entry);
+ return 0;
}
/* Retrieve subtree name */
*dir_sep = '\0';
- tree_entry = git_tree_entry_byname(tree, path);
- if (tree_entry == NULL)
- return GIT_SUCCESS; /* The subtree doesn't exist in the tree*/
+ if ((tree_entry = git_tree_entry_byname(tree, path)) == NULL)
+ return 0; /* The subtree doesn't exist in the tree*/
*dir_sep = '/';
/* Retreive subtree */
- if ((error = git_tree_lookup(&subtree, tree->object.repo, &tree_entry->oid)) < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Can't find tree object '%s'", tree_entry->filename);
+ error = git_tree_lookup(&subtree, tree->object.repo, &tree_entry->oid);
+ if (!error) {
+ error = recurse_tree_entry(subtree, e, dir_sep+1);
+ git_tree_free(subtree);
+ }
- error = recurse_tree_entry(subtree, e, dir_sep+1);
- git_tree_free(subtree);
return error;
}
-int git_status_file(unsigned int *status_flags, git_repository *repo, const char *path)
+int git_status_file(
+ unsigned int *status_flags, git_repository *repo, const char *path)
{
struct status_entry *e;
git_index *index = NULL;
git_buf temp_path = GIT_BUF_INIT;
- int error = GIT_SUCCESS;
+ int error = 0;
git_tree *tree = NULL;
const char *workdir;
assert(status_flags && repo && path);
- if ((workdir = git_repository_workdir(repo)) == NULL)
- return git__throw(GIT_ERROR,
- "Cannot retrieve status on a bare repository");
+ if ((workdir = git_repository_workdir(repo)) == NULL) {
+ giterr_set(GITERR_OS, "Cannot get file status from bare repo");
+ return GIT_ENOTFOUND;
+ }
- if ((error = git_buf_joinpath(&temp_path, workdir, path)) < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to determine status of file '%s'", path);
+ if (git_buf_joinpath(&temp_path, workdir, path) < 0)
+ return -1;
- if (git_path_isdir(temp_path.ptr) == GIT_SUCCESS) {
+ if (git_path_isdir(temp_path.ptr)) {
+ giterr_set(GITERR_OS, "Cannot get file status for directory '%s'", temp_path.ptr);
git_buf_free(&temp_path);
- return git__throw(GIT_EINVALIDPATH,
- "Failed to determine status of file '%s'. "
- "Given path leads to a folder, not a file", path);
+ return GIT_ENOTFOUND;
}
e = status_entry_new(NULL, path);
- if (e == NULL) {
- git_buf_free(&temp_path);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(e);
/* Find file in Workdir */
- if (git_path_exists(temp_path.ptr) == GIT_SUCCESS) {
- if ((error = status_entry_update_from_workdir(e, temp_path.ptr)) < GIT_SUCCESS)
- goto cleanup; /* The callee has already set the error message */
- }
+ if (git_path_exists(temp_path.ptr) == true &&
+ (error = status_entry_update_from_workdir(e, temp_path.ptr)) < 0)
+ goto cleanup;
/* Find file in Index */
- if ((error = git_repository_index__weakptr(&index, repo)) < GIT_SUCCESS) {
- git__rethrow(error,
- "Failed to determine status of file '%s'."
- "Index can't be opened", path);
+ if ((error = git_repository_index__weakptr(&index, repo)) < 0)
goto cleanup;
- }
-
status_entry_update_from_index(e, index);
- if ((error = retrieve_head_tree(&tree, repo)) < GIT_SUCCESS) {
- git__rethrow(error,
- "Failed to determine status of file '%s'", path);
+ /* Try to find file in HEAD */
+ if ((error = resolve_head_to_tree(&tree, repo)) < 0)
goto cleanup;
- }
- /* If the repository is not empty, try and locate the file in HEAD */
if (tree != NULL) {
- if ((error = git_buf_sets(&temp_path, path)) < GIT_SUCCESS) {
- git__rethrow(error,
- "Failed to determine status of file '%s'", path);
+ if ((error = git_buf_sets(&temp_path, path)) < 0 ||
+ (error = recurse_tree_entry(tree, e, temp_path.ptr)) < 0)
goto cleanup;
- }
-
- error = recurse_tree_entry(tree, e, temp_path.ptr);
- if (error < GIT_SUCCESS) {
- git__rethrow(error,
- "Failed to determine status of file '%s'. "
- "An error occured while processing the tree", path);
- goto cleanup;
- }
}
/* Determine status */
- if ((error = status_entry_update_flags(e)) < GIT_SUCCESS) {
- git__throw(error, "Nonexistent file");
- goto cleanup;
- }
+ if ((error = status_entry_update_flags(e)) < 0)
+ giterr_set(GITERR_OS, "Cannot find file '%s' to determine status", path);
- if (status_entry_is_ignorable(e)) {
+ if (!error && status_entry_is_ignorable(e)) {
git_ignores ignores;
- if ((error = git_ignore__for_path(repo, path, &ignores)) == GIT_SUCCESS)
+ if ((error = git_ignore__for_path(repo, path, &ignores)) == 0)
error = status_entry_update_ignore(e, &ignores, path);
git_ignore__free(&ignores);
-
- if (error < GIT_SUCCESS)
- goto cleanup;
}
- *status_flags = e->status_flags;
+ if (!error)
+ *status_flags = e->status_flags;
cleanup:
git_buf_free(&temp_path);
git_tree_free(tree);
git__free(e);
- return error;
-}
-
-/*
- * git_path_direach is not supposed to return entries in an ordered manner.
- * alphasorted_futils_direach wraps git_path_direach and invokes the callback
- * function by passing it alphabeticcally sorted paths parameters.
- *
- */
-
-static char *alphasorted_dirent_info_new(const git_buf *path)
-{
- char *di = git__malloc(path->size + 2);
- if (!di)
- return di;
-
- git_buf_copy_cstr(di, path->size + 1, path);
-
- if (git_path_isdir(path->ptr) == GIT_SUCCESS) {
- /*
- * Append a forward slash to the name to force folders
- * to be ordered in a similar way than in a tree
- *
- * The file "subdir" should appear before the file "subdir.txt"
- * The folder "subdir" should appear after the file "subdir.txt"
- */
- di[path->size] = '/';
- di[path->size + 1] = '\0';
- }
-
- return di;
-}
-
-static int alphasorted_dirent_cb(void *state, git_buf *full_path)
-{
- char *entry;
- git_vector *entry_names;
-
- entry_names = (git_vector *)state;
- entry = alphasorted_dirent_info_new(full_path);
-
- if (entry == NULL)
- return GIT_ENOMEM;
-
- if (git_vector_insert(entry_names, entry) < GIT_SUCCESS) {
- git__free(entry);
- return GIT_ENOMEM;
- }
-
- return GIT_SUCCESS;
-}
-static int alphasorted_futils_direach(
- git_buf *path,
- int (*fn)(void *, git_buf *),
- void *arg)
-{
- char *entry;
- git_vector entry_names;
- unsigned int idx;
- int error = GIT_SUCCESS;
- git_buf entry_path = GIT_BUF_INIT;
-
- if (git_vector_init(&entry_names, 16, git__strcmp_cb) < GIT_SUCCESS)
- return GIT_ENOMEM;
-
- error = git_path_direach(path, alphasorted_dirent_cb, &entry_names);
-
- git_vector_sort(&entry_names);
-
- for (idx = 0; idx < entry_names.length; ++idx) {
- entry = (char *)git_vector_get(&entry_names, idx);
-
- /* We have to walk the entire vector even if there was an error,
- * in order to free up memory, but we stop making callbacks after
- * an error.
- */
- if (error == GIT_SUCCESS)
- error = git_buf_sets(&entry_path, entry);
-
- if (error == GIT_SUCCESS) {
- ((struct status_st *)arg)->is_dir =
- (entry[entry_path.size - 1] == '/');
- error = fn(arg, &entry_path);
- }
-
- git__free(entry);
- }
-
- git_buf_free(&entry_path);
- git_vector_free(&entry_names);
return error;
}
-
int git_status_should_ignore(git_repository *repo, const char *path, int *ignored)
{
int error;
git_ignores ignores;
- if ((error = git_ignore__for_path(repo, path, &ignores)) == GIT_SUCCESS)
- error = git_ignore__lookup(&ignores, path, ignored);
+ if (git_ignore__for_path(repo, path, &ignores) < 0)
+ return -1;
+ error = git_ignore__lookup(&ignores, path, ignored);
git_ignore__free(&ignores);
-
return error;
}
diff --git a/src/strmap.h b/src/strmap.h
new file mode 100644
index 000000000..55fbd7c6e
--- /dev/null
+++ b/src/strmap.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 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.
+ */
+#ifndef INCLUDE_strmap_h__
+#define INCLUDE_strmap_h__
+
+#include "common.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kfree git__free
+#include "khash.h"
+
+__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)
+
+#define git_strmap_alloc() kh_init(str)
+#define git_strmap_free(h) kh_destroy(str, h), h = NULL
+#define git_strmap_clear(h) kh_clear(str, h)
+
+#define git_strmap_num_entries(h) kh_size(h)
+
+#define git_strmap_lookup_index(h, k) kh_get(str, h, k)
+#define git_strmap_valid_index(h, idx) (idx != kh_end(h))
+
+#define git_strmap_exists(h, k) (kh_get(str, h, k) != kh_end(h))
+
+#define git_strmap_value_at(h, idx) kh_val(h, idx)
+#define git_strmap_set_value_at(h, idx, v) kh_val(h, idx) = v
+#define git_strmap_delete_at(h, idx) kh_del(str, h, idx)
+
+#define git_strmap_insert(h, key, val, err) do { \
+ khiter_t __pos = kh_put(str, h, key, &err); \
+ if (err >= 0) kh_val(h, __pos) = val; \
+ } while (0)
+
+#define git_strmap_insert2(h, key, val, old, err) do { \
+ khiter_t __pos = kh_put(str, h, key, &err); \
+ if (err >= 0) { \
+ old = (err == 0) ? kh_val(h, __pos) : NULL; \
+ kh_val(h, __pos) = val; \
+ } } while (0)
+
+#define git_strmap_foreach kh_foreach
+#define git_strmap_foreach_value kh_foreach_value
+
+#endif
diff --git a/src/submodule.c b/src/submodule.c
new file mode 100644
index 000000000..1b5b59f45
--- /dev/null
+++ b/src/submodule.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 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 "common.h"
+#include "git2/config.h"
+#include "git2/types.h"
+#include "git2/repository.h"
+#include "git2/index.h"
+#include "git2/submodule.h"
+#include "buffer.h"
+#include "vector.h"
+#include "posix.h"
+#include "config_file.h"
+#include "config.h"
+#include "repository.h"
+
+static git_cvar_map _sm_update_map[] = {
+ {GIT_CVAR_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},
+ {GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
+ {GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE}
+};
+
+static git_cvar_map _sm_ignore_map[] = {
+ {GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
+ {GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
+ {GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
+ {GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE}
+};
+
+static inline khint_t str_hash_no_trailing_slash(const char *s)
+{
+ khint_t h;
+
+ for (h = 0; *s; ++s)
+ if (s[1] || *s != '/')
+ h = (h << 5) - h + *s;
+
+ return h;
+}
+
+static 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;
+
+ if (alen && a[alen] == '/')
+ alen--;
+ if (blen && b[blen] == '/')
+ blen--;
+
+ 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);
+
+static git_submodule *submodule_alloc(const char *name)
+{
+ git_submodule *sm = git__calloc(1, sizeof(git_submodule));
+ if (sm == NULL)
+ return sm;
+
+ sm->path = sm->name = git__strdup(name);
+ if (!sm->name) {
+ git__free(sm);
+ return NULL;
+ }
+
+ return sm;
+}
+
+static void submodule_release(git_submodule *sm, int decr)
+{
+ if (!sm)
+ return;
+
+ sm->refcount -= decr;
+
+ if (sm->refcount == 0) {
+ if (sm->name != sm->path)
+ git__free(sm->path);
+ git__free(sm->name);
+ git__free(sm->url);
+ git__free(sm);
+ }
+}
+
+static int submodule_from_entry(
+ git_strmap *smcfg, git_index_entry *entry)
+{
+ git_submodule *sm;
+ void *old_sm;
+ khiter_t pos;
+ int error;
+
+ pos = git_strmap_lookup_index(smcfg, entry->path);
+
+ if (git_strmap_valid_index(smcfg, pos))
+ sm = git_strmap_value_at(smcfg, pos);
+ else
+ sm = submodule_alloc(entry->path);
+
+ git_oid_cpy(&sm->oid, &entry->oid);
+
+ if (strcmp(sm->path, entry->path) != 0) {
+ if (sm->path != sm->name) {
+ git__free(sm->path);
+ sm->path = sm->name;
+ }
+ sm->path = git__strdup(entry->path);
+ if (!sm->path)
+ goto fail;
+ }
+
+ git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);
+ if (error < 0)
+ goto fail;
+ sm->refcount++;
+
+ if (old_sm && ((git_submodule *)old_sm) != sm) {
+ /* TODO: log warning about multiple entrys for same submodule path */
+ submodule_release(old_sm, 1);
+ }
+
+ return 0;
+
+fail:
+ submodule_release(sm, 0);
+ return -1;
+}
+
+static int submodule_from_config(
+ const char *key, const char *value, void *data)
+{
+ git_strmap *smcfg = data;
+ const char *namestart;
+ const char *property;
+ git_buf name = GIT_BUF_INIT;
+ git_submodule *sm;
+ void *old_sm = NULL;
+ bool is_path;
+ khiter_t pos;
+ int error;
+
+ if (git__prefixcmp(key, "submodule.") != 0)
+ return 0;
+
+ namestart = key + strlen("submodule.");
+ property = strrchr(namestart, '.');
+ if (property == NULL)
+ return 0;
+ property++;
+ is_path = (strcmp(property, "path") == 0);
+
+ if (git_buf_set(&name, namestart, property - namestart - 1) < 0)
+ return -1;
+
+ pos = git_strmap_lookup_index(smcfg, name.ptr);
+ if (!git_strmap_valid_index(smcfg, pos) && is_path)
+ pos = git_strmap_lookup_index(smcfg, value);
+ if (!git_strmap_valid_index(smcfg, pos))
+ sm = submodule_alloc(name.ptr);
+ else
+ sm = git_strmap_value_at(smcfg, pos);
+ if (!sm)
+ goto fail;
+
+ if (strcmp(sm->name, name.ptr) != 0) {
+ assert(sm->path == sm->name);
+ sm->name = git_buf_detach(&name);
+
+ git_strmap_insert2(smcfg, sm->name, sm, old_sm, error);
+ if (error < 0)
+ goto fail;
+ sm->refcount++;
+ }
+ else if (is_path && strcmp(sm->path, value) != 0) {
+ assert(sm->path == sm->name);
+ sm->path = git__strdup(value);
+ if (sm->path == NULL)
+ goto fail;
+
+ git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);
+ if (error < 0)
+ goto fail;
+ sm->refcount++;
+ }
+ git_buf_free(&name);
+
+ if (old_sm && ((git_submodule *)old_sm) != sm) {
+ /* TODO: log warning about multiple submodules with same path */
+ submodule_release(old_sm, 1);
+ }
+
+ if (is_path)
+ return 0;
+
+ /* copy other properties into submodule entry */
+ if (strcmp(property, "url") == 0) {
+ if (sm->url) {
+ git__free(sm->url);
+ sm->url = NULL;
+ }
+ if ((sm->url = git__strdup(value)) == NULL)
+ goto fail;
+ }
+ else if (strcmp(property, "update") == 0) {
+ int val;
+ if (git_config_lookup_map_value(
+ _sm_update_map, ARRAY_SIZE(_sm_update_map), value, &val) < 0) {
+ giterr_set(GITERR_INVALID,
+ "Invalid value for submodule update property: '%s'", value);
+ goto fail;
+ }
+ sm->update = (git_submodule_update_t)val;
+ }
+ else if (strcmp(property, "fetchRecurseSubmodules") == 0) {
+ if (git_config_parse_bool(&sm->fetch_recurse, value) < 0)
+ goto fail;
+ }
+ else if (strcmp(property, "ignore") == 0) {
+ int val;
+ if (git_config_lookup_map_value(
+ _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value, &val) < 0) {
+ giterr_set(GITERR_INVALID,
+ "Invalid value for submodule ignore property: '%s'", value);
+ goto fail;
+ }
+ sm->ignore = (git_submodule_ignore_t)val;
+ }
+ /* ignore other unknown submodule properties */
+
+ return 0;
+
+fail:
+ submodule_release(sm, 0);
+ git_buf_free(&name);
+ return -1;
+}
+
+static int load_submodule_config(git_repository *repo)
+{
+ int error;
+ git_index *index;
+ unsigned int i, max_i;
+ git_oid gitmodules_oid;
+ git_strmap *smcfg;
+ struct git_config_file *mods = NULL;
+
+ if (repo->submodules)
+ return 0;
+
+ /* submodule data is kept in a hashtable with each submodule stored
+ * under both its name and its path. These are usually the same, but
+ * that is not guaranteed.
+ */
+ smcfg = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(smcfg);
+
+ /* scan index for gitmodules (and .gitmodules entry) */
+ if ((error = git_repository_index__weakptr(&index, repo)) < 0)
+ goto cleanup;
+ memset(&gitmodules_oid, 0, sizeof(gitmodules_oid));
+ max_i = git_index_entrycount(index);
+
+ for (i = 0; i < max_i; i++) {
+ git_index_entry *entry = git_index_get(index, i);
+ if (S_ISGITLINK(entry->mode)) {
+ if ((error = submodule_from_entry(smcfg, entry)) < 0)
+ goto cleanup;
+ }
+ else if (strcmp(entry->path, ".gitmodules") == 0)
+ git_oid_cpy(&gitmodules_oid, &entry->oid);
+ }
+
+ /* load .gitmodules from workdir if it exists */
+ if (git_repository_workdir(repo) != NULL) {
+ /* look in workdir for .gitmodules */
+ git_buf path = GIT_BUF_INIT;
+ if (!git_buf_joinpath(
+ &path, git_repository_workdir(repo), ".gitmodules") &&
+ git_path_isfile(path.ptr))
+ {
+ if (!(error = git_config_file__ondisk(&mods, path.ptr)))
+ error = git_config_file_open(mods);
+ }
+ git_buf_free(&path);
+ }
+
+ /* load .gitmodules from object cache if not in workdir */
+ if (!error && mods == NULL && !git_oid_iszero(&gitmodules_oid)) {
+ /* TODO: is it worth loading gitmodules from object cache? */
+ }
+
+ /* process .gitmodules info */
+ if (!error && mods != NULL)
+ error = git_config_file_foreach(mods, submodule_from_config, smcfg);
+
+ /* store submodule config in repo */
+ if (!error)
+ repo->submodules = smcfg;
+
+cleanup:
+ if (mods != NULL)
+ git_config_file_free(mods);
+ if (error)
+ git_strmap_free(smcfg);
+ return error;
+}
+
+void git_submodule_config_free(git_repository *repo)
+{
+ git_strmap *smcfg = repo->submodules;
+ git_submodule *sm;
+
+ repo->submodules = NULL;
+
+ if (smcfg == NULL)
+ return;
+
+ git_strmap_foreach_value(smcfg, sm, {
+ submodule_release(sm,1);
+ });
+ git_strmap_free(smcfg);
+}
+
+static int submodule_cmp(const void *a, const void *b)
+{
+ return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
+}
+
+int git_submodule_foreach(
+ git_repository *repo,
+ int (*callback)(const char *name, void *payload),
+ void *payload)
+{
+ int error;
+ git_submodule *sm;
+ git_vector seen = GIT_VECTOR_INIT;
+ seen._cmp = submodule_cmp;
+
+ if ((error = load_submodule_config(repo)) < 0)
+ return error;
+
+ git_strmap_foreach_value(repo->submodules, sm, {
+ /* usually the following will not come into play */
+ if (sm->refcount > 1) {
+ if (git_vector_bsearch(&seen, sm) != GIT_ENOTFOUND)
+ continue;
+ if ((error = git_vector_insert(&seen, sm)) < 0)
+ break;
+ }
+
+ if ((error = callback(sm->name, payload)) < 0)
+ break;
+ });
+
+ git_vector_free(&seen);
+
+ return error;
+}
+
+int git_submodule_lookup(
+ git_submodule **sm_ptr, /* NULL allowed if user only wants to test */
+ git_repository *repo,
+ const char *name) /* trailing slash is allowed */
+{
+ khiter_t pos;
+
+ if (load_submodule_config(repo) < 0)
+ return -1;
+
+ pos = git_strmap_lookup_index(repo->submodules, name);
+ if (!git_strmap_valid_index(repo->submodules, pos))
+ return GIT_ENOTFOUND;
+
+ if (sm_ptr)
+ *sm_ptr = git_strmap_value_at(repo->submodules, pos);
+
+ return 0;
+}
diff --git a/src/tag.c b/src/tag.c
index 6076eb6e8..ff22bf79f 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -61,26 +61,32 @@ const char *git_tag_message(git_tag *t)
return t->message;
}
+static int tag_error(const char *str)
+{
+ giterr_set(GITERR_TAG, "Failed to parse tag. %s", str);
+ return -1;
+}
+
int git_tag__parse_buffer(git_tag *tag, const char *buffer, size_t length)
{
static const char *tag_types[] = {
NULL, "commit\n", "tree\n", "blob\n", "tag\n"
};
- unsigned int i, text_len;
+ unsigned int i;
+ size_t text_len;
char *search;
- int error;
const char *buffer_end = buffer + length;
- if ((error = git_oid__parse(&tag->target, &buffer, buffer_end, "object ")) < 0)
- return git__rethrow(error, "Failed to parse tag. Object field invalid");
+ if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)
+ return tag_error("Object field invalid");
if (buffer + 5 >= buffer_end)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short");
+ return tag_error("Object too short");
if (memcmp(buffer, "type ", 5) != 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Type field not found");
+ return tag_error("Type field not found");
buffer += 5;
tag->type = GIT_OBJ_BAD;
@@ -89,7 +95,7 @@ int git_tag__parse_buffer(git_tag *tag, const char *buffer, size_t length)
size_t type_length = strlen(tag_types[i]);
if (buffer + type_length >= buffer_end)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short");
+ return tag_error("Object too short");
if (memcmp(buffer, tag_types[i], type_length) == 0) {
tag->type = i;
@@ -99,25 +105,24 @@ int git_tag__parse_buffer(git_tag *tag, const char *buffer, size_t length)
}
if (tag->type == GIT_OBJ_BAD)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Invalid object type");
+ return tag_error("Invalid object type");
if (buffer + 4 >= buffer_end)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short");
+ return tag_error("Object too short");
if (memcmp(buffer, "tag ", 4) != 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Tag field not found");
+ return tag_error("Tag field not found");
buffer += 4;
search = memchr(buffer, '\n', buffer_end - buffer);
if (search == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short");
+ return tag_error("Object too short");
text_len = search - buffer;
tag->tag_name = git__malloc(text_len + 1);
- if (tag->tag_name == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(tag->tag_name);
memcpy(tag->tag_name, buffer, text_len);
tag->tag_name[text_len] = '\0';
@@ -127,27 +132,24 @@ int git_tag__parse_buffer(git_tag *tag, const char *buffer, size_t length)
tag->tagger = NULL;
if (*buffer != '\n') {
tag->tagger = git__malloc(sizeof(git_signature));
- if (tag->tagger == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(tag->tagger);
- if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n') != 0)) {
- return git__rethrow(error, "Failed to parse tag");
- }
+ if (git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n') < 0)
+ return -1;
}
if( *buffer != '\n' )
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. No new line before message");
+ return tag_error("No new line before message");
text_len = buffer_end - ++buffer;
tag->message = git__malloc(text_len + 1);
- if (tag->message == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(tag->message);
memcpy(tag->message, buffer, text_len);
tag->message[text_len] = '\0';
- return GIT_SUCCESS;
+ return 0;
}
static int retrieve_tag_reference(
@@ -161,17 +163,28 @@ static int retrieve_tag_reference(
*tag_reference_out = NULL;
- error = git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to retrieve tag reference");
+ if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
+ return -1;
error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);
if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to retrieve tag reference");
+ return error; /* Be it not foundo or corrupted */
*tag_reference_out = tag_ref;
- return GIT_SUCCESS;
+ return 0;
+}
+
+static int retrieve_tag_reference_oid(
+ git_oid *oid,
+ git_buf *ref_name_out,
+ git_repository *repo,
+ const char *tag_name)
+{
+ if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
+ return -1;
+
+ return git_reference_name_to_oid(oid, repo, ref_name_out->ptr);
}
static int write_tag_annotation(
@@ -182,7 +195,6 @@ static int write_tag_annotation(
const git_signature *tagger,
const char *message)
{
- int error = GIT_SUCCESS;
git_buf tag = GIT_BUF_INIT;
git_odb *odb;
@@ -193,25 +205,20 @@ static int write_tag_annotation(
git_buf_putc(&tag, '\n');
git_buf_puts(&tag, message);
- error = git_buf_lasterror(&tag);
- if (error < GIT_SUCCESS) {
- git_buf_free(&tag);
- return git__rethrow(error, "Not enough memory to build the tag data");
- }
-
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS) {
- git_buf_free(&tag);
- return error;
- }
+ if (git_buf_oom(&tag))
+ goto on_error;
- error = git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJ_TAG);
- git_buf_free(&tag);
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ goto on_error;
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create tag annotation");
+ if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJ_TAG) < 0)
+ goto on_error;
- return error;
+ git_buf_free(&tag);
+ return 0;
+on_error:
+ git_buf_free(&tag);
+ return -1;
}
static int git_tag_create__internal(
@@ -227,51 +234,38 @@ static int git_tag_create__internal(
git_reference *new_ref = NULL;
git_buf ref_name = GIT_BUF_INIT;
- int error, should_update_ref = 0;
- const char *errmsg = "Failed to create tag";
+ int error;
assert(repo && tag_name && target);
assert(!create_tag_annotation || (tagger && message));
- if (git_object_owner(target) != repo)
- return git__throw(GIT_EINVALIDARGS,
- "The given target does not belong to this repository");
+ if (git_object_owner(target) != repo) {
+ giterr_set(GITERR_INVALID, "The given target does not belong to this repository");
+ return -1;
+ }
- error = retrieve_tag_reference(&new_ref, &ref_name, repo, tag_name);
+ error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
- goto cleanup;
+ return -1;
/** Ensure the tag name doesn't conflict with an already existing
* reference unless overwriting has explictly been requested **/
- if (new_ref != NULL) {
- if (!allow_ref_overwrite) {
- git_oid_cpy(oid, git_reference_oid(new_ref));
- error = GIT_EEXISTS;
- errmsg = "Tag already exists";
- goto cleanup;
- } else {
- should_update_ref = 1;
- }
+ if (error == 0 && !allow_ref_overwrite) {
+ git_buf_free(&ref_name);
+ giterr_set(GITERR_TAG, "Tag already exists");
+ return GIT_EEXISTS;
}
if (create_tag_annotation) {
- if ((error = write_tag_annotation(oid, repo, tag_name, target, tagger, message)) < GIT_SUCCESS)
- goto cleanup;
+ if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)
+ return -1;
} else
git_oid_cpy(oid, git_object_id(target));
- if (!should_update_ref)
- error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, 0);
- else
- error = git_reference_set_oid(new_ref, oid);
+ error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite);
-cleanup:
git_reference_free(new_ref);
git_buf_free(&ref_name);
-
- if (error < GIT_SUCCESS)
- git__rethrow(error, "%s", errmsg);
-
return error;
}
@@ -300,8 +294,7 @@ int git_tag_create_lightweight(
int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
{
git_tag tag;
- int error, should_update_ref = 0;
- const char *errmsg = "Failed to create tag";
+ int error;
git_odb *odb;
git_odb_stream *stream;
git_odb_object *target_obj;
@@ -313,71 +306,66 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
memset(&tag, 0, sizeof(tag));
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ return -1;
/* validate the buffer */
- if ((error = git_tag__parse_buffer(&tag, buffer, strlen(buffer))) < GIT_SUCCESS)
- goto cleanup;
+ if (git_tag__parse_buffer(&tag, buffer, strlen(buffer)) < 0)
+ return -1;
/* validate the target */
- if ((error = git_odb_read(&target_obj, odb, &tag.target)) < GIT_SUCCESS)
- goto cleanup;
+ if (git_odb_read(&target_obj, odb, &tag.target) < 0)
+ goto on_error;
if (tag.type != target_obj->raw.type) {
- error = GIT_EINVALIDTYPE;
- errmsg = "The type for the given target is invalid";
- goto cleanup;
+ giterr_set(GITERR_TAG, "The type for the given target is invalid");
+ goto on_error;
}
- git_odb_object_free(target_obj);
-
- error = retrieve_tag_reference(&new_ref, &ref_name, repo, tag.tag_name);
+ error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);
if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
- goto cleanup;
+ goto on_error;
+
+ /* We don't need these objects after this */
+ git_signature_free(tag.tagger);
+ git__free(tag.tag_name);
+ git__free(tag.message);
+ git_odb_object_free(target_obj);
/** Ensure the tag name doesn't conflict with an already existing
* reference unless overwriting has explictly been requested **/
- if (new_ref != NULL) {
- if (!allow_ref_overwrite) {
- git_oid_cpy(oid, git_reference_oid(new_ref));
- error = GIT_EEXISTS;
- errmsg = "Tag already exists";
- goto cleanup;
- } else {
- should_update_ref = 1;
- }
+ if (error == 0 && !allow_ref_overwrite) {
+ giterr_set(GITERR_TAG, "Tag already exists");
+ return GIT_EEXISTS;
}
/* write the buffer */
- if ((error = git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG)) < GIT_SUCCESS)
- goto cleanup;
+ if (git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG) < 0)
+ return -1;
stream->write(stream, buffer, strlen(buffer));
error = stream->finalize_write(oid, stream);
stream->free(stream);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (error < 0) {
+ git_buf_free(&ref_name);
+ return -1;
+ }
- if (!should_update_ref)
- error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, 0);
- else
- error = git_reference_set_oid(new_ref, oid);
+ error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite);
-cleanup:
git_reference_free(new_ref);
- git_signature_free(tag.tagger);
- git__free(tag.tag_name);
- git__free(tag.message);
git_buf_free(&ref_name);
- if (error < GIT_SUCCESS)
- git__rethrow(error, "%s", errmsg);
-
return error;
+
+on_error:
+ git_signature_free(tag.tagger);
+ git__free(tag.tag_name);
+ git__free(tag.message);
+ git_odb_object_free(target_obj);
+ return -1;
}
int git_tag_delete(git_repository *repo, const char *tag_name)
@@ -390,8 +378,8 @@ int git_tag_delete(git_repository *repo, const char *tag_name)
git_buf_free(&ref_name);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to delete tag");
+ if (error < 0)
+ return -1;
return git_reference_delete(tag_ref);
}
@@ -414,13 +402,13 @@ static int tag_list_cb(const char *tag_name, void *payload)
tag_filter_data *filter;
if (git__prefixcmp(tag_name, GIT_REFS_TAGS_DIR) != 0)
- return GIT_SUCCESS;
+ return 0;
filter = (tag_filter_data *)payload;
if (!*filter->pattern || p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == GIT_SUCCESS)
return git_vector_insert(filter->taglist, git__strdup(tag_name));
- return GIT_SUCCESS;
+ return 0;
}
int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo)
@@ -438,17 +426,37 @@ int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_reposit
filter.pattern = pattern;
error = git_reference_foreach(repo, GIT_REF_OID|GIT_REF_PACKED, &tag_list_cb, (void *)&filter);
- if (error < GIT_SUCCESS) {
+ if (error < 0) {
git_vector_free(&taglist);
- return git__rethrow(error, "Failed to list tags");
+ return -1;
}
tag_names->strings = (char **)taglist.contents;
tag_names->count = taglist.length;
- return GIT_SUCCESS;
+ return 0;
}
int git_tag_list(git_strarray *tag_names, git_repository *repo)
{
return git_tag_list_match(tag_names, "", repo);
}
+
+int git_tag_peel(git_object **tag_target, git_tag *tag)
+{
+ int error;
+ git_object *target;
+
+ assert(tag_target && tag);
+
+ if (git_tag_target(&target, tag) < 0)
+ return -1;
+
+ if (git_object_type(target) == GIT_OBJ_TAG) {
+ error = git_tag_peel(tag_target, (git_tag *)target);
+ git_object_free(target);
+ return error;
+ }
+
+ *tag_target = target;
+ return 0;
+}
diff --git a/src/transport.c b/src/transport.c
index 4910f2433..0c88e44d3 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -54,7 +54,8 @@ static git_transport_cb transport_find_fn(const char *url)
int git_transport_dummy(git_transport **transport)
{
GIT_UNUSED(transport);
- return git__throw(GIT_ENOTIMPLEMENTED, "This protocol isn't implemented. Sorry");
+ giterr_set(GITERR_NET, "This transport isn't implemented. Sorry");
+ return -1;
}
int git_transport_new(git_transport **out, const char *url)
@@ -70,11 +71,10 @@ int git_transport_new(git_transport **out, const char *url)
error = fn(&transport);
if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create new transport");
+ return error;
transport->url = git__strdup(url);
- if (transport->url == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(transport->url);
*out = transport;
diff --git a/src/transport.h b/src/transport.h
index 63dd7dab6..125df2745 100644
--- a/src/transport.h
+++ b/src/transport.h
@@ -8,6 +8,7 @@
#define INCLUDE_transport_h__
#include "git2/net.h"
+#include "git2/indexer.h"
#include "vector.h"
#define GIT_CAP_OFS_DELTA "ofs-delta"
@@ -66,22 +67,14 @@ struct git_transport {
*/
int (*push)(struct git_transport *transport);
/**
- * Send a 'done' message
- */
- int (*send_done)(struct git_transport *transport);
- /**
* Negotiate the minimal amount of objects that need to be
* retrieved
*/
int (*negotiate_fetch)(struct git_transport *transport, git_repository *repo, const git_vector *wants);
/**
- * Send a flush
- */
- int (*send_flush)(struct git_transport *transport);
- /**
* Download the packfile
*/
- int (*download_pack)(char **out, struct git_transport *transport, git_repository *repo);
+ int (*download_pack)(struct git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
/**
* Fetch the changes
*/
diff --git a/src/transports/git.c b/src/transports/git.c
index 88e7e8160..31bc21c96 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -46,11 +46,13 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
char *delim, *repo;
char default_command[] = "git-upload-pack";
char host[] = "host=";
- int len;
+ size_t len;
delim = strchr(url, '/');
- if (delim == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to create proto-request: malformed URL");
+ if (delim == NULL) {
+ giterr_set(GITERR_NET, "Malformed URL");
+ return -1;
+ }
repo = delim;
@@ -64,11 +66,15 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1;
git_buf_grow(request, len);
- git_buf_printf(request, "%04x%s %s%c%s", len, cmd, repo, 0, host);
+ git_buf_printf(request, "%04x%s %s%c%s",
+ (unsigned int)(len & 0x0FFFF), cmd, repo, 0, host);
git_buf_put(request, url, delim - url);
git_buf_putc(request, '\0');
- return git_buf_lasterror(request);
+ if (git_buf_oom(request))
+ return GIT_ENOMEM;
+
+ return 0;
}
static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
@@ -77,7 +83,7 @@ static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
git_buf request = GIT_BUF_INIT;
error = gen_proto(&request, cmd, url);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
error = gitno_send(s, request.ptr, request.size, 0);
@@ -102,9 +108,8 @@ static int do_connect(transport_git *t, const char *url)
if (!git__prefixcmp(url, prefix))
url += strlen(prefix);
- error = gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT);
- if (error < GIT_SUCCESS)
- return error;
+ if (gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT) < 0)
+ return -1;
s = gitno_connect(host, port);
connected = 1;
@@ -115,9 +120,11 @@ static int do_connect(transport_git *t, const char *url)
git__free(port);
if (error < GIT_SUCCESS && s > 0)
- close(s);
- if (!connected)
- error = git__throw(GIT_EOSERR, "Failed to connect to any of the addresses");
+ gitno_close(s);
+ if (!connected) {
+ giterr_set(GITERR_NET, "Failed to connect to the host");
+ return -1;
+ }
return error;
}
@@ -128,33 +135,30 @@ static int do_connect(transport_git *t, const char *url)
static int store_refs(transport_git *t)
{
gitno_buffer *buf = &t->buf;
- int error = GIT_SUCCESS;
+ int ret = 0;
while (1) {
- error = gitno_recv(buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(GIT_EOSERR, "Failed to receive data");
- if (error == GIT_SUCCESS) /* Orderly shutdown, so exit */
- return GIT_SUCCESS;
-
- error = git_protocol_store_refs(&t->proto, buf->data, buf->offset);
- if (error == GIT_ESHORTBUFFER) {
+ if ((ret = gitno_recv(buf)) < 0)
+ return -1;
+ if (ret == 0) /* Orderly shutdown, so exit */
+ return 0;
+
+ ret = git_protocol_store_refs(&t->proto, buf->data, buf->offset);
+ if (ret == GIT_ESHORTBUFFER) {
gitno_consume_n(buf, buf->len);
continue;
}
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to store refs");
+ if (ret < 0)
+ return ret;
gitno_consume_n(buf, buf->offset);
if (t->proto.flush) { /* No more refs */
t->proto.flush = 0;
- return GIT_SUCCESS;
+ return 0;
}
}
-
- return error;
}
static int detect_caps(transport_git *t)
@@ -167,7 +171,7 @@ static int detect_caps(transport_git *t)
pkt = git_vector_get(refs, 0);
/* No refs or capabilites, odd but not a problem */
if (pkt == NULL || pkt->capabilities == NULL)
- return GIT_SUCCESS;
+ return 0;
ptr = pkt->capabilities;
while (ptr != NULL && *ptr != '\0') {
@@ -184,7 +188,7 @@ static int detect_caps(transport_git *t)
ptr = strchr(ptr, ' ');
}
- return GIT_SUCCESS;
+ return 0;
}
/*
@@ -194,36 +198,33 @@ static int detect_caps(transport_git *t)
static int git_connect(git_transport *transport, int direction)
{
transport_git *t = (transport_git *) transport;
- int error = GIT_SUCCESS;
- if (direction == GIT_DIR_PUSH)
- return git__throw(GIT_EINVALIDARGS, "Pushing is not supported with the git protocol");
+ if (direction == GIT_DIR_PUSH) {
+ giterr_set(GITERR_NET, "Pushing over git:// is not supported");
+ return -1;
+ }
t->parent.direction = direction;
- error = git_vector_init(&t->refs, 16, NULL);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_vector_init(&t->refs, 16, NULL) < 0)
+ return -1;
/* Connect and ask for the refs */
- error = do_connect(t, transport->url);
- if (error < GIT_SUCCESS)
- return error;
+ if (do_connect(t, transport->url) < 0)
+ goto cleanup;
gitno_buffer_setup(&t->buf, t->buff, sizeof(t->buff), t->socket);
t->parent.connected = 1;
- error = store_refs(t);
- if (error < GIT_SUCCESS)
- return error;
+ if (store_refs(t) < 0)
+ goto cleanup;
- error = detect_caps(t);
+ if (detect_caps(t) < 0)
+ goto cleanup;
+ return 0;
cleanup:
- if (error < GIT_SUCCESS) {
- git_vector_free(&t->refs);
- }
-
- return error;
+ git_vector_free(&t->refs);
+ return -1;
}
static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque)
@@ -241,149 +242,129 @@ static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaqu
pkt = (git_pkt_ref *)p;
- if (list_cb(&pkt->head, opaque) < 0)
- return git__throw(GIT_ERROR,
- "The user callback returned an error code");
+ if (list_cb(&pkt->head, opaque) < 0) {
+ giterr_set(GITERR_NET, "User callback returned error");
+ return -1;
+ }
}
- return GIT_SUCCESS;
+ return 0;
+}
+
+/* Wait until we get an ack from the */
+static int recv_pkt(gitno_buffer *buf)
+{
+ const char *ptr = buf->data, *line_end;
+ git_pkt *pkt;
+ int pkt_type, error;
+
+ do {
+ /* Wait for max. 1 second */
+ if ((error = gitno_select_in(buf, 1, 0)) < 0) {
+ return -1;
+ } else if (error == 0) {
+ /*
+ * Some servers don't respond immediately, so if this
+ * happens, we keep sending information until it
+ * answers. Pretend we received a NAK to convince higher
+ * layers to do so.
+ */
+ return GIT_PKT_NAK;
+ }
+
+ if ((error = gitno_recv(buf)) < 0)
+ return -1;
+
+ error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
+ if (error == GIT_ESHORTBUFFER)
+ continue;
+ if (error < 0)
+ return -1;
+ } while (error);
+
+ gitno_consume(buf, line_end);
+ pkt_type = pkt->type;
+ git__free(pkt);
+
+ return pkt_type;
}
static int git_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
{
transport_git *t = (transport_git *) transport;
git_revwalk *walk;
- git_reference *ref;
- git_strarray refs;
git_oid oid;
int error;
unsigned int i;
+ git_buf data = GIT_BUF_INIT;
gitno_buffer *buf = &t->buf;
- error = git_pkt_send_wants(wants, &t->caps, t->socket);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to send wants list");
-
- error = git_reference_listall(&refs, repo, GIT_REF_LISTALL);
- if (error < GIT_ERROR)
- return git__rethrow(error, "Failed to list all references");
-
- error = git_revwalk_new(&walk, repo);
- if (error < GIT_ERROR) {
- error = git__rethrow(error, "Failed to list all references");
- goto cleanup;
- }
- git_revwalk_sorting(walk, GIT_SORT_TIME);
+ if (git_pkt_buffer_wants(wants, &t->caps, &data) < 0)
+ return -1;
- for (i = 0; i < refs.count; ++i) {
- /* No tags */
- if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
- continue;
+ if (git_fetch_setup_walk(&walk, repo) < 0)
+ goto on_error;
- error = git_reference_lookup(&ref, repo, refs.strings[i]);
- if (error < GIT_ERROR) {
- error = git__rethrow(error, "Failed to lookup %s", refs.strings[i]);
- goto cleanup;
- }
-
- if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
- continue;
-
- error = git_revwalk_push(walk, git_reference_oid(ref));
- if (error < GIT_ERROR) {
- error = git__rethrow(error, "Failed to push %s", refs.strings[i]);
- goto cleanup;
- }
- }
- git_strarray_free(&refs);
+ if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
+ goto on_error;
+ git_buf_clear(&data);
/*
* We don't support any kind of ACK extensions, so the negotiation
* boils down to sending what we have and listening for an ACK
* every once in a while.
*/
i = 0;
- while ((error = git_revwalk_next(&oid, walk)) == GIT_SUCCESS) {
- error = git_pkt_send_have(&oid, t->socket);
+ while ((error = git_revwalk_next(&oid, walk)) == 0) {
+ git_pkt_buffer_have(&oid, &data);
i++;
if (i % 20 == 0) {
- const char *ptr = buf->data, *line_end;
- git_pkt *pkt;
- git_pkt_send_flush(t->socket);
- while (1) {
- /* Wait for max. 1 second */
- error = gitno_select_in(buf, 1, 0);
- if (error < GIT_SUCCESS) {
- error = git__throw(GIT_EOSERR, "Error in select");
- } else if (error == 0) {
- /*
- * Some servers don't respond immediately, so if this
- * happens, we keep sending information until it
- * answers.
- */
- break;
- }
-
- error = gitno_recv(buf);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Error receiving data");
- goto cleanup;
- }
- error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
- if (error == GIT_ESHORTBUFFER)
- continue;
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to get answer");
- goto cleanup;
- }
-
- gitno_consume(buf, line_end);
-
- if (pkt->type == GIT_PKT_ACK) {
- git__free(pkt);
- error = GIT_SUCCESS;
- goto done;
- } else if (pkt->type == GIT_PKT_NAK) {
- git__free(pkt);
- break;
- } else {
- error = git__throw(GIT_ERROR, "Got unexpected pkt type");
- goto cleanup;
- }
- }
- }
- }
- if (error == GIT_EREVWALKOVER)
- error = GIT_SUCCESS;
+ int pkt_type;
-done:
- git_pkt_send_flush(t->socket);
- git_pkt_send_done(t->socket);
+ git_pkt_buffer_flush(&data);
+ if (git_buf_oom(&data))
+ goto on_error;
-cleanup:
- git_revwalk_free(walk);
+ if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
+ goto on_error;
- return error;
-}
+ pkt_type = recv_pkt(buf);
-static int git_send_flush(git_transport *transport)
-{
- transport_git *t = (transport_git *) transport;
+ if (pkt_type == GIT_PKT_ACK) {
+ break;
+ } else if (pkt_type == GIT_PKT_NAK) {
+ continue;
+ } else {
+ giterr_set(GITERR_NET, "Unexpected pkt type");
+ goto on_error;
+ }
- return git_pkt_send_flush(t->socket);
-}
+ }
+ }
+ if (error < 0 && error != GIT_EREVWALKOVER)
+ goto on_error;
-static int git_send_done(git_transport *transport)
-{
- transport_git *t = (transport_git *) transport;
+ /* Tell the other end that we're done negotiating */
+ git_buf_clear(&data);
+ git_pkt_buffer_flush(&data);
+ git_pkt_buffer_done(&data);
+ if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
+ goto on_error;
- return git_pkt_send_done(t->socket);
+ git_buf_free(&data);
+ git_revwalk_free(walk);
+ return 0;
+
+on_error:
+ git_buf_free(&data);
+ git_revwalk_free(walk);
+ return -1;
}
-static int git_download_pack(char **out, git_transport *transport, git_repository *repo)
+static int git_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats)
{
transport_git *t = (transport_git *) transport;
- int error = GIT_SUCCESS;
+ int error = 0, read_bytes;
gitno_buffer *buf = &t->buf;
git_pkt *pkt;
const char *line_end, *ptr;
@@ -391,7 +372,7 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor
/*
* For now, we ignore everything and wait for the pack
*/
- while (1) {
+ do {
ptr = buf->data;
/* Whilst we're searching for the pack */
while (1) {
@@ -408,7 +389,7 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor
if (pkt->type == GIT_PKT_PACK) {
git__free(pkt);
- return git_fetch__download_pack(out, buf->data, buf->offset, t->socket, repo);
+ return git_fetch__download_pack(buf->data, buf->offset, t->socket, repo, bytes, stats);
}
/* For now we don't care about anything */
@@ -416,34 +397,28 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor
gitno_consume(buf, line_end);
}
- error = gitno_recv(buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(GIT_EOSERR, "Failed to receive data");
- if (error == 0) { /* Orderly shutdown */
- return GIT_SUCCESS;
- }
+ read_bytes = gitno_recv(buf);
+ } while (read_bytes);
- }
+ return read_bytes;
}
-
static int git_close(git_transport *transport)
{
transport_git *t = (transport_git*) transport;
- int error;
/* Can't do anything if there's an error, so don't bother checking */
git_pkt_send_flush(t->socket);
- error = gitno_close(t->socket);
-
- if (error < 0)
- error = git__throw(GIT_EOSERR, "Failed to close socket");
+ if (gitno_close(t->socket) < 0) {
+ giterr_set(GITERR_NET, "Failed to close socket");
+ return -1;
+ }
#ifdef GIT_WIN32
WSACleanup();
#endif
- return error;
+ return 0;
}
static void git_free(git_transport *transport)
@@ -472,16 +447,13 @@ int git_transport_git(git_transport **out)
#endif
t = git__malloc(sizeof(transport_git));
- if (t == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(t);
memset(t, 0x0, sizeof(transport_git));
t->parent.connect = git_connect;
t->parent.ls = git_ls;
t->parent.negotiate_fetch = git_negotiate_fetch;
- t->parent.send_flush = git_send_flush;
- t->parent.send_done = git_send_done;
t->parent.download_pack = git_download_pack;
t->parent.close = git_close;
t->parent.free = git_free;
@@ -494,9 +466,10 @@ int git_transport_git(git_transport **out)
ret = WSAStartup(MAKEWORD(2,2), &t->wsd);
if (ret != 0) {
git_free(*out);
- return git__throw(GIT_EOSERR, "Winsock init failed");
+ giterr_set(GITERR_NET, "Winsock init failed");
+ return -1;
}
#endif
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/transports/http.c b/src/transports/http.c
index 2842d08fd..3690f3ded 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -32,7 +32,7 @@ typedef struct {
git_protocol proto;
git_vector refs;
git_vector common;
- int socket;
+ GIT_SOCKET socket;
git_buf buf;
git_remote_head **heads;
int error;
@@ -77,7 +77,10 @@ static int gen_request(git_buf *buf, const char *url, const char *host, const ch
}
git_buf_puts(buf, "\r\n");
- return git_buf_lasterror(buf);
+ if (git_buf_oom(buf))
+ return GIT_ENOMEM;
+
+ return 0;
}
static int do_connect(transport_http *t, const char *host, const char *port)
@@ -85,16 +88,15 @@ static int do_connect(transport_http *t, const char *host, const char *port)
GIT_SOCKET s = -1;
if (t->parent.connected && http_should_keep_alive(&t->parser))
- return GIT_SUCCESS;
+ return 0;
+
+ if ((s = gitno_connect(host, port)) < 0)
+ return -1;
- s = gitno_connect(host, port);
- if (s < GIT_SUCCESS) {
- return git__rethrow(s, "Failed to connect to host");
- }
t->socket = s;
t->parent.connected = 1;
- return GIT_SUCCESS;
+ return 0;
}
/*
@@ -116,8 +118,7 @@ static int on_header_field(http_parser *parser, const char *str, size_t len)
t->ct_finished = 1;
t->ct_found = 0;
t->content_type = git__strdup(git_buf_cstr(buf));
- if (t->content_type == NULL)
- return t->error = GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(t->content_type);
git_buf_clear(buf);
}
@@ -164,6 +165,12 @@ static int on_headers_complete(http_parser *parser)
transport_http *t = (transport_http *) parser->data;
git_buf *buf = &t->buf;
+ /* The content-type is text/plain for 404, so don't validate */
+ if (parser->status_code == 404) {
+ git_buf_clear(buf);
+ return 0;
+ }
+
if (t->content_type == NULL) {
t->content_type = git__strdup(git_buf_cstr(buf));
if (t->content_type == NULL)
@@ -173,10 +180,10 @@ static int on_headers_complete(http_parser *parser)
git_buf_clear(buf);
git_buf_printf(buf, "application/x-git-%s-advertisement", t->service);
if (git_buf_oom(buf))
- return GIT_ENOMEM;
+ return t->error = GIT_ENOMEM;
if (strcmp(t->content_type, git_buf_cstr(buf)))
- return t->error = git__throw(GIT_EOBJCORRUPTED, "Content-Type '%s' is wrong", t->content_type);
+ return t->error = -1;
git_buf_clear(buf);
return 0;
@@ -186,6 +193,10 @@ static int on_body_store_refs(http_parser *parser, const char *str, size_t len)
{
transport_http *t = (transport_http *) parser->data;
+ if (parser->status_code == 404) {
+ return git_buf_put(&t->buf, str, len);
+ }
+
return git_protocol_store_refs(&t->proto, str, len);
}
@@ -194,16 +205,22 @@ static int on_message_complete(http_parser *parser)
transport_http *t = (transport_http *) parser->data;
t->transfer_finished = 1;
+
+ if (parser->status_code == 404) {
+ giterr_set(GITERR_NET, "Remote error: %s", git_buf_cstr(&t->buf));
+ t->error = -1;
+ }
+
return 0;
}
static int store_refs(transport_http *t)
{
- int error = GIT_SUCCESS;
http_parser_settings settings;
char buffer[1024];
gitno_buffer buf;
git_pkt *pkt;
+ int ret;
http_parser_init(&t->parser, HTTP_RESPONSE);
t->parser.data = t;
@@ -219,83 +236,76 @@ static int store_refs(transport_http *t)
while(1) {
size_t parsed;
- error = gitno_recv(&buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Error receiving data from network");
+ if ((ret = gitno_recv(&buf)) < 0)
+ return -1;
parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);
/* Both should happen at the same time */
- if (parsed != buf.offset || t->error < GIT_SUCCESS)
- return git__rethrow(t->error, "Error parsing HTTP data");
+ if (parsed != buf.offset || t->error < 0)
+ return t->error;
gitno_consume_n(&buf, parsed);
- if (error == 0 || t->transfer_finished)
- return GIT_SUCCESS;
+ if (ret == 0 || t->transfer_finished)
+ return 0;
}
pkt = git_vector_get(&t->refs, 0);
- if (pkt == NULL || pkt->type != GIT_PKT_COMMENT)
- return t->error = git__throw(GIT_EOBJCORRUPTED, "Not a valid smart HTTP response");
- else
+ if (pkt == NULL || pkt->type != GIT_PKT_COMMENT) {
+ giterr_set(GITERR_NET, "Invalid HTTP response");
+ return t->error = -1;
+ } else {
git_vector_remove(&t->refs, 0);
+ }
- return error;
+ return 0;
}
static int http_connect(git_transport *transport, int direction)
{
transport_http *t = (transport_http *) transport;
- int error;
+ int ret;
git_buf request = GIT_BUF_INIT;
const char *service = "upload-pack";
const char *url = t->parent.url, *prefix = "http://";
- if (direction == GIT_DIR_PUSH)
- return git__throw(GIT_EINVALIDARGS, "Pushing over HTTP is not supported");
+ if (direction == GIT_DIR_PUSH) {
+ giterr_set(GITERR_NET, "Pushing over HTTP is not implemented");
+ return -1;
+ }
t->parent.direction = direction;
- error = git_vector_init(&t->refs, 16, NULL);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to init refs vector");
+ if (git_vector_init(&t->refs, 16, NULL) < 0)
+ return -1;
if (!git__prefixcmp(url, prefix))
url += strlen(prefix);
- error = gitno_extract_host_and_port(&t->host, &t->port, url, "80");
- if (error < GIT_SUCCESS)
+ if ((ret = gitno_extract_host_and_port(&t->host, &t->port, url, "80")) < 0)
goto cleanup;
t->service = git__strdup(service);
- if (t->service == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(t->service);
- error = do_connect(t, t->host, t->port);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to connect to host");
+ if ((ret = do_connect(t, t->host, t->port)) < 0)
goto cleanup;
- }
/* Generate and send the HTTP request */
- error = gen_request(&request, url, t->host, "GET", service, 0, 1);
- if (error < GIT_SUCCESS) {
- error = git__throw(error, "Failed to generate request");
+ if ((ret = gen_request(&request, url, t->host, "GET", service, 0, 1)) < 0) {
+ giterr_set(GITERR_NET, "Failed to generate request");
goto cleanup;
}
- error = gitno_send(t->socket, request.ptr, request.size, 0);
- if (error < GIT_SUCCESS)
- error = git__rethrow(error, "Failed to send the HTTP request");
+ if ((ret = gitno_send(t->socket, request.ptr, request.size, 0)) < 0)
+ goto cleanup;
- error = store_refs(t);
+ ret = store_refs(t);
cleanup:
git_buf_free(&request);
git_buf_clear(&t->buf);
- return error;
+ return ret;
}
static int http_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque)
@@ -309,12 +319,13 @@ static int http_ls(git_transport *transport, git_headlist_cb list_cb, void *opaq
if (p->type != GIT_PKT_REF)
continue;
- if (list_cb(&p->head, opaque) < 0)
- return git__throw(GIT_ERROR,
- "The user callback returned an error code");
+ if (list_cb(&p->head, opaque) < 0) {
+ giterr_set(GITERR_NET, "The user callback returned error");
+ return -1;
+ }
}
- return GIT_SUCCESS;
+ return 0;
}
static int on_body_parse_response(http_parser *parser, const char *str, size_t len)
@@ -326,10 +337,12 @@ static int on_body_parse_response(http_parser *parser, const char *str, size_t l
const char *line_end, *ptr;
if (len == 0) { /* EOF */
- if (buf->size != 0)
- return t->error = git__throw(GIT_ERROR, "EOF and unprocessed data");
- else
+ if (git_buf_len(buf) != 0) {
+ giterr_set(GITERR_NET, "Unexpected EOF");
+ return t->error = -1;
+ } else {
return 0;
+ }
}
git_buf_put(buf, str, len);
@@ -337,15 +350,15 @@ static int on_body_parse_response(http_parser *parser, const char *str, size_t l
while (1) {
git_pkt *pkt;
- if (buf->size == 0)
+ if (git_buf_len(buf) == 0)
return 0;
- error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->size);
+ error = git_pkt_parse_line(&pkt, ptr, &line_end, git_buf_len(buf));
if (error == GIT_ESHORTBUFFER) {
return 0; /* Ask for more */
}
if (error < GIT_SUCCESS)
- return t->error = git__rethrow(error, "Failed to parse pkt-line");
+ return t->error = -1;
git_buf_consume(buf, line_end);
@@ -365,9 +378,8 @@ static int on_body_parse_response(http_parser *parser, const char *str, size_t l
continue;
}
- error = git_vector_insert(common, pkt);
- if (error < GIT_SUCCESS)
- return t->error = git__rethrow(error, "Failed to add pkt to list");
+ if (git_vector_insert(common, pkt) < 0)
+ return -1;
}
return error;
@@ -376,7 +388,7 @@ static int on_body_parse_response(http_parser *parser, const char *str, size_t l
static int parse_response(transport_http *t)
{
- int error = GIT_SUCCESS;
+ int ret = 0;
http_parser_settings settings;
char buffer[1024];
gitno_buffer buf;
@@ -396,74 +408,28 @@ static int parse_response(transport_http *t)
while(1) {
size_t parsed;
- error = gitno_recv(&buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Error receiving data from network");
+ if ((ret = gitno_recv(&buf)) < 0)
+ return -1;
parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);
/* Both should happen at the same time */
- if (parsed != buf.offset || t->error < GIT_SUCCESS)
- return git__rethrow(t->error, "Error parsing HTTP data");
+ if (parsed != buf.offset || t->error < 0)
+ return t->error;
gitno_consume_n(&buf, parsed);
- if (error == 0 || t->transfer_finished || t->pack_ready) {
- return GIT_SUCCESS;
- }
- }
-
- return error;
-}
-
-static int setup_walk(git_revwalk **out, git_repository *repo)
-{
- git_revwalk *walk;
- git_strarray refs;
- unsigned int i;
- git_reference *ref;
- int error;
-
- error = git_reference_listall(&refs, repo, GIT_REF_LISTALL);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to list references");
-
- error = git_revwalk_new(&walk, repo);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to setup walk");
-
- git_revwalk_sorting(walk, GIT_SORT_TIME);
-
- for (i = 0; i < refs.count; ++i) {
- /* No tags */
- if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
- continue;
-
- error = git_reference_lookup(&ref, repo, refs.strings[i]);
- if (error < GIT_ERROR) {
- error = git__rethrow(error, "Failed to lookup %s", refs.strings[i]);
- goto cleanup;
- }
-
- if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
- continue;
- error = git_revwalk_push(walk, git_reference_oid(ref));
- if (error < GIT_ERROR) {
- error = git__rethrow(error, "Failed to push %s", refs.strings[i]);
- goto cleanup;
+ if (ret == 0 || t->transfer_finished || t->pack_ready) {
+ return 0;
}
}
- *out = walk;
-cleanup:
- git_strarray_free(&refs);
-
- return error;
+ return ret;
}
static int http_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
{
transport_http *t = (transport_http *) transport;
- int error;
+ int ret;
unsigned int i;
char buff[128];
gitno_buffer buf;
@@ -479,82 +445,55 @@ static int http_negotiate_fetch(git_transport *transport, git_repository *repo,
if (!git__prefixcmp(url, prefix))
url += strlen(prefix);
- error = git_vector_init(common, 16, NULL);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to init common vector");
+ if (git_vector_init(common, 16, NULL) < 0)
+ return -1;
- error = setup_walk(&walk, repo);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to setup walk");
- goto cleanup;
- }
+ if (git_fetch_setup_walk(&walk, repo) < 0)
+ return -1;
do {
- error = do_connect(t, t->host, t->port);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to connect to host");
+ if ((ret = do_connect(t, t->host, t->port)) < 0)
goto cleanup;
- }
- error = git_pkt_buffer_wants(wants, &t->caps, &data);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to send wants");
+ if ((ret = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)
goto cleanup;
- }
/* We need to send these on each connection */
git_vector_foreach (common, i, pkt) {
- error = git_pkt_buffer_have(&pkt->oid, &data);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to buffer common have");
+ if ((ret = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
goto cleanup;
- }
}
i = 0;
- while ((i < 20) && ((error = git_revwalk_next(&oid, walk)) == GIT_SUCCESS)) {
- error = git_pkt_buffer_have(&oid, &data);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to buffer have");
+ while ((i < 20) && ((ret = git_revwalk_next(&oid, walk)) == 0)) {
+ if ((ret = git_pkt_buffer_have(&oid, &data)) < 0)
goto cleanup;
- }
+
i++;
}
git_pkt_buffer_done(&data);
- error = gen_request(&request, url, t->host, "POST", "upload-pack", data.size, 0);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to generate request");
+ if ((ret = gen_request(&request, url, t->host, "POST", "upload-pack", data.size, 0)) < 0)
goto cleanup;
- }
- error = gitno_send(t->socket, request.ptr, request.size, 0);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to send request");
+ if ((ret = gitno_send(t->socket, request.ptr, request.size, 0)) < 0)
goto cleanup;
- }
- error = gitno_send(t->socket, data.ptr, data.size, 0);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to send data");
+ if ((ret = gitno_send(t->socket, data.ptr, data.size, 0)) < 0)
goto cleanup;
- }
git_buf_clear(&request);
git_buf_clear(&data);
- if (error < GIT_SUCCESS || i >= 256)
+ if (ret < GIT_SUCCESS || i >= 256)
break;
- error = parse_response(t);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Error parsing the response");
+ if ((ret = parse_response(t)) < 0)
goto cleanup;
- }
if (t->pack_ready) {
- error = GIT_SUCCESS;
+ ret = 0;
goto cleanup;
}
@@ -564,11 +503,12 @@ cleanup:
git_buf_free(&request);
git_buf_free(&data);
git_revwalk_free(walk);
- return error;
+ return ret;
}
typedef struct {
- git_filebuf *file;
+ git_indexer_stream *idx;
+ git_indexer_stats *stats;
transport_http *transport;
} download_pack_cbdata;
@@ -584,10 +524,10 @@ static int on_body_download_pack(http_parser *parser, const char *str, size_t le
{
download_pack_cbdata *data = (download_pack_cbdata *) parser->data;
transport_http *t = data->transport;
- git_filebuf *file = data->file;
+ git_indexer_stream *idx = data->idx;
+ git_indexer_stats *stats = data->stats;
-
- return t->error = git_filebuf_write(file, str, len);
+ return t->error = git_indexer_stream_add(idx, str, len, stats);
}
/*
@@ -596,96 +536,81 @@ static int on_body_download_pack(http_parser *parser, const char *str, size_t le
* the simple downloader. Furthermore, we're using keep-alive
* connections, so the simple downloader would just hang.
*/
-static int http_download_pack(char **out, git_transport *transport, git_repository *repo)
+static int http_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats)
{
transport_http *t = (transport_http *) transport;
git_buf *oldbuf = &t->buf;
- int error = GIT_SUCCESS;
+ int recvd;
http_parser_settings settings;
char buffer[1024];
gitno_buffer buf;
+ git_indexer_stream *idx = NULL;
download_pack_cbdata data;
- git_filebuf file = GIT_FILEBUF_INIT;
- git_buf path = GIT_BUF_INIT;
- char suff[] = "/objects/pack/pack-received\0";
+
+ gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
+
+ if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) {
+ giterr_set(GITERR_NET, "The pack doesn't start with a pack signature");
+ return -1;
+ }
+
+ if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
+ return -1;
+
/*
* This is part of the previous response, so we don't want to
* re-init the parser, just set these two callbacks.
*/
- data.file = &file;
+ memset(stats, 0, sizeof(git_indexer_stats));
+ data.stats = stats;
+ data.idx = idx;
data.transport = t;
t->parser.data = &data;
t->transfer_finished = 0;
memset(&settings, 0x0, sizeof(settings));
settings.on_message_complete = on_message_complete_download_pack;
settings.on_body = on_body_download_pack;
+ *bytes = git_buf_len(oldbuf);
- gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
+ if (git_indexer_stream_add(idx, git_buf_cstr(oldbuf), git_buf_len(oldbuf), stats) < 0)
+ goto on_error;
- if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) {
- return git__throw(GIT_ERROR, "The pack doesn't start with the signature");
- }
-
- error = git_buf_joinpath(&path, repo->path_repository, suff);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- /* Part of the packfile has been received, don't loose it */
- error = git_filebuf_write(&file, oldbuf->ptr, oldbuf->size);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- while(1) {
+ do {
size_t parsed;
- error = gitno_recv(&buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Error receiving data from network");
+ if ((recvd = gitno_recv(&buf)) < 0)
+ goto on_error;
parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);
- /* Both should happen at the same time */
- if (parsed != buf.offset || t->error < GIT_SUCCESS)
- return git__rethrow(t->error, "Error parsing HTTP data");
+ if (parsed != buf.offset || t->error < 0)
+ goto on_error;
+ *bytes += recvd;
gitno_consume_n(&buf, parsed);
+ } while (recvd > 0 && !t->transfer_finished);
- if (error == 0 || t->transfer_finished) {
- break;
- }
- }
-
- *out = git__strdup(file.path_lock);
- if (*out == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ if (git_indexer_stream_finalize(idx, stats) < 0)
+ goto on_error;
- /* A bit dodgy, but we need to keep the pack at the temporary path */
- error = git_filebuf_commit_at(&file, file.path_lock, GIT_PACK_FILE_MODE);
-
-cleanup:
- if (error < GIT_SUCCESS)
- git_filebuf_cleanup(&file);
- git_buf_free(&path);
+ git_indexer_stream_free(idx);
+ return 0;
- return error;
+on_error:
+ git_indexer_stream_free(idx);
+ return -1;
}
static int http_close(git_transport *transport)
{
transport_http *t = (transport_http *) transport;
- int error;
- error = gitno_close(t->socket);
- if (error < 0)
- return git__throw(GIT_EOSERR, "Failed to close the socket: %s", strerror(errno));
+ if (gitno_close(t->socket) < 0) {
+ giterr_set(GITERR_OS, "Failed to close the socket: %s", strerror(errno));
+ return -1;
+ }
- return GIT_SUCCESS;
+ return 0;
}
@@ -729,8 +654,7 @@ int git_transport_http(git_transport **out)
transport_http *t;
t = git__malloc(sizeof(transport_http));
- if (t == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(t);
memset(t, 0x0, sizeof(transport_http));
@@ -748,10 +672,11 @@ int git_transport_http(git_transport **out)
* before any socket calls can be performed */
if (WSAStartup(MAKEWORD(2,2), &t->wsd) != 0) {
http_free((git_transport *) t);
- return git__throw(GIT_EOSERR, "Winsock init failed");
+ giterr_set(GITERR_OS, "Winsock init failed");
+ return -1;
}
#endif
*out = (git_transport *) t;
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/transports/local.c b/src/transports/local.c
index eb24db0fd..5dc350103 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -26,110 +26,94 @@ static int add_ref(transport_local *t, const char *name)
{
const char peeled[] = "^{}";
git_remote_head *head;
- git_reference *ref, *resolved_ref;
- git_object *obj = NULL;
- int error = GIT_SUCCESS, peel_len, ret;
+ git_object *obj = NULL, *target = NULL;
+ git_buf buf = GIT_BUF_INIT;
head = git__malloc(sizeof(git_remote_head));
- if (head == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(head);
head->name = git__strdup(name);
- if (head->name == NULL) {
- error = GIT_ENOMEM;
- goto out;
- }
-
- error = git_reference_lookup(&ref, t->repo, name);
- if (error < GIT_SUCCESS)
- goto out;
+ GITERR_CHECK_ALLOC(head->name);
- error = git_reference_resolve(&resolved_ref, ref);
- if (error < GIT_SUCCESS)
- goto out;
-
- git_oid_cpy(&head->oid, git_reference_oid(resolved_ref));
-
- error = git_vector_insert(&t->refs, head);
- if (error < GIT_SUCCESS)
- goto out;
+ if (git_reference_name_to_oid(&head->oid, t->repo, name) < 0 ||
+ git_vector_insert(&t->refs, head) < 0)
+ {
+ git__free(head->name);
+ git__free(head);
+ return -1;
+ }
/* If it's not a tag, we don't need to try to peel it */
if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))
- goto out;
+ return 0;
- error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY);
- if (error < GIT_SUCCESS) {
- git__rethrow(error, "Failed to lookup object");
- }
+ if (git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY) < 0)
+ return -1;
head = NULL;
/* If it's not an annotated tag, just get out */
- if (git_object_type(obj) != GIT_OBJ_TAG)
- goto out;
+ if (git_object_type(obj) != GIT_OBJ_TAG) {
+ git_object_free(obj);
+ return 0;
+ }
/* And if it's a tag, peel it, and add it to the list */
head = git__malloc(sizeof(git_remote_head));
- peel_len = strlen(name) + strlen(peeled);
- head->name = git__malloc(peel_len + 1);
- ret = p_snprintf(head->name, peel_len + 1, "%s%s", name, peeled);
+ GITERR_CHECK_ALLOC(head);
+ if (git_buf_join(&buf, 0, name, peeled) < 0)
+ return -1;
- assert(ret < peel_len + 1);
- (void)ret;
+ head->name = git_buf_detach(&buf);
- git_oid_cpy(&head->oid, git_tag_target_oid((git_tag *) obj));
+ if (git_tag_peel(&target, (git_tag *) obj) < 0)
+ goto on_error;
- error = git_vector_insert(&t->refs, head);
- if (error < GIT_SUCCESS)
- goto out;
+ git_oid_cpy(&head->oid, git_object_id(target));
+ git_object_free(obj);
+ git_object_free(target);
- out:
- git_reference_free(ref);
- git_reference_free(resolved_ref);
+ if (git_vector_insert(&t->refs, head) < 0)
+ return -1;
- git_object_free(obj);
- if (head && error < GIT_SUCCESS) {
- git__free(head->name);
- git__free(head);
- }
+ return 0;
- return error;
+on_error:
+ git_object_free(obj);
+ git_object_free(target);
+ return -1;
}
static int store_refs(transport_local *t)
{
- int error;
unsigned int i;
git_strarray ref_names = {0};
assert(t);
- error = git_vector_init(&t->refs, ref_names.count, NULL);
- if (error < GIT_SUCCESS)
- return error;
-
- error = git_reference_listall(&ref_names, t->repo, GIT_REF_LISTALL);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to list remote heads");
+ if (git_reference_listall(&ref_names, t->repo, GIT_REF_LISTALL) < 0 ||
+ git_vector_init(&t->refs, (unsigned int)ref_names.count, NULL) < 0)
+ goto on_error;
/* Sort the references first */
git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
/* Add HEAD */
- error = add_ref(t, GIT_HEAD_FILE);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (add_ref(t, GIT_HEAD_FILE) < 0)
+ goto on_error;
for (i = 0; i < ref_names.count; ++i) {
- error = add_ref(t, ref_names.strings[i]);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (add_ref(t, ref_names.strings[i]) < 0)
+ goto on_error;
}
-cleanup:
git_strarray_free(&ref_names);
- return error;
+ return 0;
+
+on_error:
+ git_vector_free(&t->refs);
+ git_strarray_free(&ref_names);
+ return -1;
}
static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
@@ -143,11 +127,10 @@ static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *pay
git_vector_foreach(refs, i, h) {
if (list_cb(h, payload) < 0)
- return git__throw(GIT_ERROR,
- "The user callback returned an error code");
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
/*
@@ -166,32 +149,31 @@ static int local_connect(git_transport *transport, int direction)
/* The repo layer doesn't want the prefix */
if (!git__prefixcmp(transport->url, "file://")) {
- error = git_path_fromurl(&buf, transport->url);
- if (error < GIT_SUCCESS) {
+ if (git_path_fromurl(&buf, transport->url) < 0) {
git_buf_free(&buf);
- return git__rethrow(error, "Failed to parse remote path");
+ return -1;
}
path = git_buf_cstr(&buf);
- } else /* We assume transport->url is already a path */
+ } else { /* We assume transport->url is already a path */
path = transport->url;
+ }
error = git_repository_open(&repo, path);
git_buf_free(&buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to open remote");
+ if (error < 0)
+ return -1;
t->repo = repo;
- error = store_refs(t);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to retrieve references");
+ if (store_refs(t) < 0)
+ return -1;
t->parent.connected = 1;
- return GIT_SUCCESS;
+ return 0;
}
static int local_close(git_transport *transport)
@@ -201,7 +183,7 @@ static int local_close(git_transport *transport)
git_repository_free(t->repo);
t->repo = NULL;
- return GIT_SUCCESS;
+ return 0;
}
static void local_free(git_transport *transport)
@@ -232,8 +214,7 @@ int git_transport_local(git_transport **out)
transport_local *t;
t = git__malloc(sizeof(transport_local));
- if (t == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(t);
memset(t, 0x0, sizeof(transport_local));
@@ -244,5 +225,5 @@ int git_transport_local(git_transport **out)
*out = (git_transport *) t;
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/tree.c b/src/tree.c
index 8a0971abd..09ed1a3e8 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -201,41 +201,38 @@ unsigned int git_tree_entrycount(git_tree *tree)
return tree->entries.length;
}
-static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buffer_end)
+static int tree_error(const char *str)
{
- int error = GIT_SUCCESS;
+ giterr_set(GITERR_TREE, "%s", str);
+ return -1;
+}
- if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < GIT_SUCCESS)
- return GIT_ENOMEM;
+static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buffer_end)
+{
+ if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0)
+ return -1;
while (buffer < buffer_end) {
git_tree_entry *entry;
int tmp;
entry = git__calloc(1, sizeof(git_tree_entry));
- if (entry == NULL) {
- error = GIT_ENOMEM;
- break;
- }
+ GITERR_CHECK_ALLOC(entry);
- if (git_vector_insert(&tree->entries, entry) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (git_vector_insert(&tree->entries, entry) < 0)
+ return -1;
- if (git__strtol32(&tmp, buffer, &buffer, 8) < GIT_SUCCESS ||
+ if (git__strtol32(&tmp, buffer, &buffer, 8) < 0 ||
!buffer || !valid_attributes(tmp))
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Can't parse attributes");
+ return tree_error("Failed to parse tree. Can't parse attributes");
entry->attr = tmp;
- if (*buffer++ != ' ') {
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted");
- break;
- }
+ if (*buffer++ != ' ')
+ return tree_error("Failed to parse tree. Object is corrupted");
- if (memchr(buffer, 0, buffer_end - buffer) == NULL) {
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted");
- break;
- }
+ 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);
@@ -249,7 +246,7 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf
buffer += GIT_OID_RAWSZ;
}
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse buffer");
+ return 0;
}
int git_tree__parse(git_tree *tree, git_odb_object *obj)
@@ -280,10 +277,9 @@ static int append_entry(git_treebuilder *bld, const char *filename, const git_oi
{
git_tree_entry *entry;
- if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL)
- return GIT_ENOMEM;
+ entry = git__calloc(1, sizeof(git_tree_entry));
+ GITERR_CHECK_ALLOC(entry);
- memset(entry, 0x0, sizeof(git_tree_entry));
entry->filename = git__strdup(filename);
entry->filename_len = strlen(entry->filename);
@@ -291,9 +287,9 @@ static int append_entry(git_treebuilder *bld, const char *filename, const git_oi
entry->attr = attributes;
if (git_vector_insert(&bld->entries, entry) < 0)
- return GIT_ENOMEM;
+ return -1;
- return GIT_SUCCESS;
+ return 0;
}
static int write_tree(
@@ -318,7 +314,7 @@ static int write_tree(
error = git_treebuilder_create(&bld, NULL);
if (bld == NULL) {
- return GIT_ENOMEM;
+ return -1;
}
/*
@@ -354,16 +350,13 @@ static int write_tree(
char *subdir, *last_comp;
subdir = git__strndup(entry->path, next_slash - entry->path);
- if (subdir == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(subdir);
/* Write out the subtree */
written = write_tree(&sub_oid, repo, index, subdir, i);
if (written < 0) {
- error = git__rethrow(written, "Failed to write subtree %s", subdir);
- goto cleanup;
+ tree_error("Failed to write subtree");
+ goto on_error;
} else {
i = written - 1; /* -1 because of the loop increment */
}
@@ -382,51 +375,49 @@ static int write_tree(
}
error = append_entry(bld, last_comp, &sub_oid, S_IFDIR);
git__free(subdir);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to insert dir");
- goto cleanup;
+ if (error < 0) {
+ tree_error("Failed to insert dir");
+ goto on_error;
}
} else {
error = append_entry(bld, filename, &entry->oid, entry->mode);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to insert file");
+ if (error < 0) {
+ tree_error("Failed to insert file");
+ goto on_error;
}
}
}
- error = git_treebuilder_write(oid, repo, bld);
- if (error < GIT_SUCCESS)
- error = git__rethrow(error, "Failed to write tree to db");
+ if (git_treebuilder_write(oid, repo, bld) < 0)
+ goto on_error;
- cleanup:
git_treebuilder_free(bld);
+ return i;
- if (error < GIT_SUCCESS)
- return error;
- else
- return i;
+on_error:
+ git_treebuilder_free(bld);
+ return -1;
}
int git_tree_create_fromindex(git_oid *oid, git_index *index)
{
+ int ret;
git_repository *repo;
- int error;
repo = (git_repository *)GIT_REFCOUNT_OWNER(index);
if (repo == NULL)
- return git__throw(GIT_EBAREINDEX,
- "Failed to create tree. "
- "The index file is not backed up by an existing repository");
+ return tree_error("Failed to create tree. "
+ "The index file is not backed up by an existing repository");
if (index->tree != NULL && index->tree->entries >= 0) {
git_oid_cpy(oid, &index->tree->oid);
- return GIT_SUCCESS;
+ return 0;
}
/* The tree cache didn't help us */
- error = write_tree(oid, repo, index, "", 0);
- return (error < GIT_SUCCESS) ? git__rethrow(error, "Failed to create tree") : GIT_SUCCESS;
+ ret = write_tree(oid, repo, index, "", 0);
+ return ret < 0 ? ret : 0;
}
static void sort_entries(git_treebuilder *bld)
@@ -442,30 +433,29 @@ int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
assert(builder_p);
bld = git__calloc(1, sizeof(git_treebuilder));
- if (bld == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(bld);
if (source != NULL)
source_entries = source->entries.length;
- if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < GIT_SUCCESS) {
- git__free(bld);
- return GIT_ENOMEM;
- }
+ if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < 0)
+ goto on_error;
if (source != NULL) {
for (i = 0; i < source->entries.length; ++i) {
git_tree_entry *entry_src = source->entries.contents[i];
- if (append_entry(bld, entry_src->filename, &entry_src->oid, entry_src->attr) < 0) {
- git_treebuilder_free(bld);
- return GIT_ENOMEM;
- }
+ if (append_entry(bld, entry_src->filename, &entry_src->oid, entry_src->attr) < 0)
+ goto on_error;
}
}
*builder_p = bld;
- return GIT_SUCCESS;
+ return 0;
+
+on_error:
+ git_treebuilder_free(bld);
+ return -1;
}
int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes)
@@ -476,10 +466,10 @@ int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, con
assert(bld && id && filename);
if (!valid_attributes(attributes))
- return git__throw(GIT_ERROR, "Failed to insert entry. Invalid attributes");
+ return tree_error("Failed to insert entry. Invalid attributes");
if (!valid_entry_name(filename))
- return git__throw(GIT_ERROR, "Failed to insert entry. Invalid name for a tree entry");
+ return tree_error("Failed to insert entry. Invalid name for a tree entry");
pos = tree_key_search(&bld->entries, filename);
@@ -488,10 +478,9 @@ int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, con
if (entry->removed)
entry->removed = 0;
} else {
- if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL)
- return GIT_ENOMEM;
+ entry = git__calloc(1, sizeof(git_tree_entry));
+ GITERR_CHECK_ALLOC(entry);
- memset(entry, 0x0, sizeof(git_tree_entry));
entry->filename = git__strdup(filename);
entry->filename_len = strlen(entry->filename);
}
@@ -501,13 +490,13 @@ int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, con
if (pos == GIT_ENOTFOUND) {
if (git_vector_insert(&bld->entries, entry) < 0)
- return GIT_ENOMEM;
+ return -1;
}
if (entry_out != NULL)
*entry_out = entry;
- return GIT_SUCCESS;
+ return 0;
}
static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename)
@@ -538,16 +527,15 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
git_tree_entry *remove_ptr = treebuilder_get(bld, filename);
if (remove_ptr == NULL || remove_ptr->removed)
- return git__throw(GIT_ENOTFOUND, "Failed to remove entry. File isn't in the tree");
+ return tree_error("Failed to remove entry. File isn't in the tree");
remove_ptr->removed = 1;
- return GIT_SUCCESS;
+ return 0;
}
int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
{
unsigned int i;
- int error;
git_buf tree = GIT_BUF_INIT;
git_odb *odb;
@@ -569,21 +557,22 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
git_buf_put(&tree, (char *)entry->oid.id, GIT_OID_RAWSZ);
}
- if ((error = git_buf_lasterror(&tree)) < GIT_SUCCESS) {
- git_buf_free(&tree);
- return git__rethrow(error, "Not enough memory to build the tree data");
- }
+ if (git_buf_oom(&tree))
+ goto on_error;
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS) {
- git_buf_free(&tree);
- return error;
- }
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ goto on_error;
+
+
+ if (git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE) < 0)
+ goto on_error;
- error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
git_buf_free(&tree);
+ return 0;
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write tree");
+on_error:
+ git_buf_free(&tree);
+ return -1;
}
void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload)
@@ -624,16 +613,18 @@ static int tree_frompath(
git_tree **parent_out,
git_tree *root,
git_buf *treeentry_path,
- int offset)
+ size_t offset)
{
char *slash_pos = NULL;
const git_tree_entry* entry;
int error = GIT_SUCCESS;
git_tree *subtree;
- if (!*(treeentry_path->ptr + offset))
- return git__rethrow(GIT_EINVALIDPATH,
+ 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, '/');
@@ -644,9 +635,11 @@ static int tree_frompath(
git_object_id((const git_object *)root)
);
- if (slash_pos == treeentry_path->ptr + offset)
- return git__rethrow(GIT_EINVALIDPATH,
+ if (slash_pos == treeentry_path->ptr + offset) {
+ giterr_set(GITERR_INVALID,
"Invalid relative path to a tree entry '%s'.", treeentry_path->ptr);
+ return -1;
+ }
*slash_pos = '\0';
@@ -655,14 +648,15 @@ static int tree_frompath(
if (slash_pos != NULL)
*slash_pos = '/';
- if (entry == NULL)
- return git__rethrow(GIT_ENOTFOUND,
+ if (entry == NULL) {
+ giterr_set(GITERR_TREE,
"No tree entry can be found from "
"the given tree and relative path '%s'.", treeentry_path->ptr);
+ return GIT_ENOTFOUND;
+ }
- error = git_tree_lookup(&subtree, root->object.repo, &entry->oid);
- if (error < GIT_SUCCESS)
+ if (git_tree_lookup(&subtree, root->object.repo, &entry->oid) < 0)
return error;
error = tree_frompath(
@@ -686,7 +680,7 @@ int git_tree_get_subtree(
assert(subtree && root && subtree_path);
- if ((error = git_buf_sets(&buffer, subtree_path)) == GIT_SUCCESS)
+ if ((error = git_buf_sets(&buffer, subtree_path)) == 0)
error = tree_frompath(subtree, root, &buffer, 0);
git_buf_free(&buffer);
@@ -711,7 +705,7 @@ static int tree_walk_post(
if (entry_is_tree(entry)) {
git_tree *subtree;
- size_t path_len = path->size;
+ size_t path_len = git_buf_len(path);
if ((error = git_tree_lookup(
&subtree, tree->object.repo, &entry->oid)) < 0)
@@ -720,19 +714,19 @@ static int tree_walk_post(
/* append the next entry to the path */
git_buf_puts(path, entry->filename);
git_buf_putc(path, '/');
- if ((error = git_buf_lasterror(path)) < GIT_SUCCESS)
- break;
- error = tree_walk_post(subtree, callback, path, payload);
- if (error < GIT_SUCCESS)
- break;
+ if (git_buf_oom(path))
+ return -1;
+
+ if (tree_walk_post(subtree, callback, path, payload) < 0)
+ return -1;
git_buf_truncate(path, path_len);
git_tree_free(subtree);
}
}
- return error;
+ return 0;
}
int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload)
@@ -746,14 +740,12 @@ int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payl
break;
case GIT_TREEWALK_PRE:
- error = git__throw(GIT_ENOTIMPLEMENTED,
- "Preorder tree walking is still not implemented");
- break;
+ tree_error("Preorder tree walking is still not implemented");
+ return GIT_ENOTIMPLEMENTED;
default:
- error = git__throw(GIT_EINVALIDARGS,
- "Invalid walking mode for tree walk");
- break;
+ giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk");
+ return -1;
}
git_buf_free(&root_path);
@@ -792,7 +784,7 @@ static int signal_additions(git_tree *tree, int start, int end, git_tree_diff_cb
{
git_tree_diff_data diff;
git_tree_entry *entry;
- int i, error;
+ int i;
if (end < 0)
end = git_tree_entrycount(tree);
@@ -802,12 +794,11 @@ static int signal_additions(git_tree *tree, int start, int end, git_tree_diff_cb
entry = git_vector_get(&tree->entries, i);
mark_add(&diff, entry);
- error = cb(&diff, data);
- if (error < GIT_SUCCESS)
- return error;
+ if (cb(&diff, data) < 0)
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
static int signal_addition(git_tree_entry *entry, git_tree_diff_cb cb, void *data)
@@ -825,7 +816,7 @@ static int signal_deletions(git_tree *tree, int start, int end, git_tree_diff_cb
{
git_tree_diff_data diff;
git_tree_entry *entry;
- int i, error;
+ int i;
if (end < 0)
end = git_tree_entrycount(tree);
@@ -835,12 +826,11 @@ static int signal_deletions(git_tree *tree, int start, int end, git_tree_diff_cb
entry = git_vector_get(&tree->entries, i);
mark_del(&diff, entry);
- error = cb(&diff, data);
- if (error < GIT_SUCCESS)
- return error;
+ if (cb(&diff, data) < 0)
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
static int signal_deletion(git_tree_entry *entry, git_tree_diff_cb cb, void *data)
@@ -872,14 +862,14 @@ int git_tree_diff(git_tree *a, git_tree *b, git_tree_diff_cb cb, void *data)
unsigned int i_a = 0, i_b = 0; /* Counters for trees a and b */
git_tree_entry *entry_a = NULL, *entry_b = NULL;
git_tree_diff_data diff;
- int error = GIT_SUCCESS, cmp;
+ int cmp;
while (1) {
entry_a = a == NULL ? NULL : git_vector_get(&a->entries, i_a);
entry_b = b == NULL ? NULL : git_vector_get(&b->entries, i_b);
if (!entry_a && !entry_b)
- goto exit;
+ return 0;
memset(&diff, 0x0, sizeof(git_tree_diff_data));
@@ -910,29 +900,28 @@ int git_tree_diff(git_tree *a, git_tree *b, git_tree_diff_cb cb, void *data)
/* If they're not both dirs or both files, it's add + del */
if (S_ISDIR(entry_a->attr) != S_ISDIR(entry_b->attr)) {
- if ((error = signal_addition(entry_a, cb, data)) < 0)
- goto exit;
- if ((error = signal_deletion(entry_b, cb, data)) < 0)
- goto exit;
+ if (signal_addition(entry_a, cb, data) < 0)
+ return -1;
+ if (signal_deletion(entry_b, cb, data) < 0)
+ return -1;
}
/* Otherwise consider it a modification */
- if ((error = signal_modification(entry_a, entry_b, cb, data)) < 0)
- goto exit;
+ if (signal_modification(entry_a, entry_b, cb, data) < 0)
+ return -1;
} else if (cmp < 0) {
i_a++;
- if ((error = signal_deletion(entry_a, cb, data)) < 0)
- goto exit;
+ if (signal_deletion(entry_a, cb, data) < 0)
+ return -1;
} else if (cmp > 0) {
i_b++;
- if ((error = signal_addition(entry_b, cb, data)) < 0)
- goto exit;
+ if (signal_addition(entry_b, cb, data) < 0)
+ return -1;
}
}
-exit:
- return error;
+ return 0;
}
struct diff_index_cbdata {
@@ -977,53 +966,49 @@ static int diff_index_cb(const char *root, git_tree_entry *tentry, void *data)
git_index_entry *ientry = git_index_get(cbdata->index, cbdata->i);
git_tree_entry fake_entry;
git_buf fn_buf = GIT_BUF_INIT;
- int cmp, error = GIT_SUCCESS;
+ int cmp;
if (entry_is_tree(tentry))
- return GIT_SUCCESS;
+ return 0;
+
+ if (!ientry)
+ return signal_deletion(tentry, cbdata->cb, cbdata->data);
git_buf_puts(&fn_buf, root);
git_buf_puts(&fn_buf, tentry->filename);
- if (!ientry) {
- error = signal_deletion(tentry, cbdata->cb, cbdata->data);
- git_buf_free(&fn_buf);
- goto exit;
- }
-
/* Like with 'git diff-index', the index is the right side*/
cmp = strcmp(git_buf_cstr(&fn_buf), ientry->path);
git_buf_free(&fn_buf);
if (cmp == 0) {
cbdata->i++;
if (!cmp_tentry_ientry(tentry, ientry))
- goto exit;
+ return 0;
/* modification */
make_tentry(&fake_entry, ientry);
- if ((error = signal_modification(tentry, &fake_entry, cbdata->cb, cbdata->data)) < 0)
- goto exit;
+ if (signal_modification(tentry, &fake_entry, cbdata->cb, cbdata->data) < 0)
+ return -1;
} else if (cmp < 0) {
/* deletion */
memcpy(&fake_entry, tentry, sizeof(git_tree_entry));
- if ((error = signal_deletion(tentry, cbdata->cb, cbdata->data)) < 0)
- goto exit;
+ if (signal_deletion(tentry, cbdata->cb, cbdata->data) < 0)
+ return -1;
} else {
/* addition */
cbdata->i++;
make_tentry(&fake_entry, ientry);
- if ((error = signal_addition(&fake_entry, cbdata->cb, cbdata->data)) < 0)
- goto exit;
+ if (signal_addition(&fake_entry, cbdata->cb, cbdata->data) < 0)
+ return -1;
/*
* The index has an addition. This means that we need to use
* the next entry in the index without advancing the tree
* walker, so call ourselves with the same tree state.
*/
- if ((error = diff_index_cb(root, tentry, data)) < 0)
- goto exit;
+ if (diff_index_cb(root, tentry, data) < 0)
+ return -1;;
}
- exit:
- return error;
+ return 0;
}
int git_tree_diff_index_recursive(git_tree *tree, git_index *index, git_tree_diff_cb cb, void *data)
diff --git a/src/tree.h b/src/tree.h
index 0bff41312..fd00afde5 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -32,7 +32,7 @@ struct git_treebuilder {
GIT_INLINE(unsigned int) entry_is_tree(const struct git_tree_entry *e)
{
- return e->attr & 040000;
+ return (S_ISDIR(e->attr) && !S_ISGITLINK(e->attr));
}
void git_tree__free(git_tree *tree);
diff --git a/src/tsort.c b/src/tsort.c
index 6fbec5b2a..f54c21e50 100644
--- a/src/tsort.c
+++ b/src/tsort.c
@@ -30,8 +30,10 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
int l, c, r;
void *lx, *cx;
+ assert(size > 0);
+
l = 0;
- r = size - 1;
+ r = (int)size - 1;
c = r >> 1;
lx = dst[l];
@@ -84,7 +86,7 @@ static void bisort(void **dst, size_t start, size_t size, cmp_ptr_t cmp)
/* Else we need to find the right place, shift everything over, and squeeze in */
x = dst[i];
location = binsearch(dst, x, i, cmp);
- for (j = i - 1; j >= location; j--) {
+ for (j = (int)i - 1; j >= location; j--) {
dst[j + 1] = dst[j];
}
dst[location] = x;
@@ -104,7 +106,7 @@ struct tsort_store {
void **storage;
};
-static void reverse_elements(void **dst, int start, int end)
+static void reverse_elements(void **dst, ssize_t start, ssize_t end)
{
while (start < end) {
void *tmp = dst[start];
@@ -116,7 +118,7 @@ static void reverse_elements(void **dst, int start, int end)
}
}
-static int count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
+static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
{
ssize_t curr = start + 2;
@@ -148,7 +150,7 @@ static int count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store
}
}
-static int compute_minrun(size_t n)
+static size_t compute_minrun(size_t n)
{
int r = 0;
while (n >= 64) {
@@ -158,19 +160,19 @@ static int compute_minrun(size_t n)
return n + r;
}
-static int check_invariant(struct tsort_run *stack, int stack_curr)
+static int check_invariant(struct tsort_run *stack, ssize_t stack_curr)
{
if (stack_curr < 2)
return 1;
else if (stack_curr == 2) {
- const int A = stack[stack_curr - 2].length;
- const int B = stack[stack_curr - 1].length;
+ const ssize_t A = stack[stack_curr - 2].length;
+ const ssize_t B = stack[stack_curr - 1].length;
return (A > B);
} else {
- const int A = stack[stack_curr - 3].length;
- const int B = stack[stack_curr - 2].length;
- const int C = stack[stack_curr - 1].length;
+ const ssize_t A = stack[stack_curr - 3].length;
+ const ssize_t B = stack[stack_curr - 2].length;
+ const ssize_t C = stack[stack_curr - 1].length;
return !((A <= B + C) || (B <= C));
}
}
@@ -195,7 +197,7 @@ static int resize(struct tsort_store *store, size_t new_size)
return 0;
}
-static void merge(void **dst, const struct tsort_run *stack, int stack_curr, struct tsort_store *store)
+static void merge(void **dst, const struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store)
{
const ssize_t A = stack[stack_curr - 2].length;
const ssize_t B = stack[stack_curr - 1].length;
@@ -343,7 +345,7 @@ void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)
}
/* compute the minimum run length */
- minrun = compute_minrun(size);
+ minrun = (ssize_t)compute_minrun(size);
/* temporary storage for merges */
store->alloc = 0;
diff --git a/src/unix/map.c b/src/unix/map.c
index 67a73e43c..772f4e247 100644
--- a/src/unix/map.c
+++ b/src/unix/map.c
@@ -17,12 +17,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs
int mprot = 0;
int mflag = 0;
- assert((out != NULL) && (len > 0));
-
- if ((out == NULL) || (len == 0)) {
- errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. No map or zero length");
- }
+ GIT_MMAP_VALIDATE(out, len, prot, flags);
out->data = NULL;
out->len = 0;
@@ -31,39 +26,28 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs
mprot = PROT_WRITE;
else if (prot & GIT_PROT_READ)
mprot = PROT_READ;
- else {
- errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. Invalid protection parameters");
- }
if ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)
mflag = MAP_SHARED;
else if ((flags & GIT_MAP_TYPE) == GIT_MAP_PRIVATE)
mflag = MAP_PRIVATE;
- if (flags & GIT_MAP_FIXED) {
- errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. FIXED not set");
+ 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;
}
- out->data = mmap(NULL, len, mprot, mflag, fd, offset);
- if (!out->data || out->data == MAP_FAILED)
- return git__throw(GIT_EOSERR, "Failed to mmap. Could not write data");
out->len = len;
- return GIT_SUCCESS;
+ return 0;
}
int p_munmap(git_map *map)
{
assert(map != NULL);
-
- if (!map)
- return git__throw(GIT_ERROR, "Failed to munmap. Map does not exist");
-
munmap(map->data, map->len);
-
- return GIT_SUCCESS;
+ return 0;
}
#endif
diff --git a/src/util.c b/src/util.c
index 1fb01170b..2cf7b158b 100644
--- a/src/util.c
+++ b/src/util.c
@@ -31,6 +31,35 @@ void git_strarray_free(git_strarray *array)
git__free(array->strings);
}
+int git_strarray_copy(git_strarray *tgt, const git_strarray *src)
+{
+ size_t i;
+
+ assert(tgt && src);
+
+ memset(tgt, 0, sizeof(*tgt));
+
+ if (!src->count)
+ return 0;
+
+ tgt->strings = git__calloc(src->count, sizeof(char *));
+ GITERR_CHECK_ALLOC(tgt->strings);
+
+ for (i = 0; i < src->count; ++i) {
+ tgt->strings[tgt->count] = git__strdup(src->strings[i]);
+
+ if (!tgt->strings[tgt->count]) {
+ git_strarray_free(tgt);
+ memset(tgt, 0, sizeof(*tgt));
+ return -1;
+ }
+
+ tgt->count++;
+ }
+
+ return 0;
+}
+
int git__fnmatch(const char *pattern, const char *name, int flags)
{
int ret;
@@ -38,11 +67,12 @@ int git__fnmatch(const char *pattern, const char *name, int flags)
ret = p_fnmatch(pattern, name, flags);
switch (ret) {
case 0:
- return GIT_SUCCESS;
+ return 0;
case FNM_NOMATCH:
return GIT_ENOMATCH;
default:
- return git__throw(GIT_EOSERR, "Error trying to match path");
+ giterr_set(GITERR_OS, "Error trying to match path");
+ return -1;
}
}
@@ -111,34 +141,40 @@ int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int ba
}
Return:
- if (ndig == 0)
- return git__throw(GIT_ENOTNUM, "Failed to convert string to long. Not a number");
+ if (ndig == 0) {
+ giterr_set(GITERR_INVALID, "Failed to convert string to long. Not a number");
+ return -1;
+ }
if (endptr)
*endptr = p;
- if (ovfl)
- return git__throw(GIT_EOVERFLOW, "Failed to convert string to long. Overflow error");
+ if (ovfl) {
+ giterr_set(GITERR_INVALID, "Failed to convert string to long. Overflow error");
+ return -1;
+ }
*result = neg ? -n : n;
- return GIT_SUCCESS;
+ return 0;
}
int git__strtol32(int32_t *result, const char *nptr, const char **endptr, int base)
{
- int error = GIT_SUCCESS;
+ int error;
int32_t tmp_int;
int64_t tmp_long;
- if ((error = git__strtol64(&tmp_long, nptr, endptr, base)) < GIT_SUCCESS)
+ if ((error = git__strtol64(&tmp_long, nptr, endptr, base)) < 0)
return error;
tmp_int = tmp_long & 0xFFFFFFFF;
- if (tmp_int != tmp_long)
- return git__throw(GIT_EOVERFLOW, "Failed to convert. '%s' is too large", nptr);
+ if (tmp_int != tmp_long) {
+ giterr_set(GITERR_INVALID, "Failed to convert. '%s' is too large", nptr);
+ return -1;
+ }
*result = tmp_int;
-
+
return error;
}
@@ -355,23 +391,27 @@ int git__bsearch(
int (*compare)(const void *, const void *),
size_t *position)
{
- int lim, cmp;
+ unsigned int lim;
+ int cmp = -1;
void **part, **base = array;
- for (lim = array_len; lim != 0; lim >>= 1) {
+ for (lim = (unsigned int)array_len; lim != 0; lim >>= 1) {
part = base + (lim >> 1);
cmp = (*compare)(key, *part);
if (cmp == 0) {
- *position = (part - array);
- return GIT_SUCCESS;
- } else if (cmp > 0) { /* key > p; take right partition */
+ base = part;
+ break;
+ }
+ if (cmp > 0) { /* key > p; take right partition */
base = part + 1;
lim--;
} /* else take left partition */
}
- *position = (base - array);
- return GIT_ENOTFOUND;
+ if (position)
+ *position = (base - array);
+
+ return (cmp == 0) ? 0 : -1;
}
/**
diff --git a/src/util.h b/src/util.h
index f77c91dfd..1fee9a70c 100644
--- a/src/util.h
+++ b/src/util.h
@@ -22,24 +22,21 @@
GIT_INLINE(void *) git__malloc(size_t len)
{
void *ptr = malloc(len);
- if (!ptr)
- git__throw(GIT_ENOMEM, "Out of memory. Failed to allocate %d bytes.", (int)len);
+ if (!ptr) giterr_set_oom();
return ptr;
}
GIT_INLINE(void *) git__calloc(size_t nelem, size_t elsize)
{
void *ptr = calloc(nelem, elsize);
- if (!ptr)
- git__throw(GIT_ENOMEM, "Out of memory. Failed to allocate %d bytes.", (int)elsize);
+ if (!ptr) giterr_set_oom();
return ptr;
}
GIT_INLINE(char *) git__strdup(const char *str)
{
char *ptr = strdup(str);
- if (!ptr)
- git__throw(GIT_ENOMEM, "Out of memory. Failed to duplicate string");
+ if (!ptr) giterr_set_oom();
return ptr;
}
@@ -54,7 +51,7 @@ GIT_INLINE(char *) git__strndup(const char *str, size_t n)
ptr = (char*)malloc(length + 1);
if (!ptr) {
- git__throw(GIT_ENOMEM, "Out of memory. Failed to duplicate string");
+ giterr_set_oom();
return NULL;
}
@@ -67,8 +64,7 @@ GIT_INLINE(char *) git__strndup(const char *str, size_t n)
GIT_INLINE(void *) git__realloc(void *ptr, size_t size)
{
void *new_ptr = realloc(ptr, size);
- if (!new_ptr)
- git__throw(GIT_ENOMEM, "Out of memory. Failed to allocate %d bytes.", (int)size);
+ if (!new_ptr) giterr_set_oom();
return new_ptr;
}
@@ -113,6 +109,11 @@ extern int git__fnmatch(const char *pattern, const char *name, int flags);
extern void git__tsort(void **dst, size_t size, int (*cmp)(const void *, const void *));
+/**
+ * @param position If non-NULL, this will be set to the position where the
+ * element is or would be inserted if not found.
+ * @return pos (>=0) if found or -1 if not found
+ */
extern int git__bsearch(
void **array,
size_t array_len,
@@ -178,4 +179,21 @@ GIT_INLINE(int) git__ishex(const char *str)
return 1;
}
+GIT_INLINE(size_t) git__size_t_bitmask(size_t v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+
+ return v;
+}
+
+GIT_INLINE(size_t) git__size_t_powerof2(size_t v)
+{
+ return git__size_t_bitmask(v) + 1;
+}
+
#endif /* INCLUDE_util_h__ */
diff --git a/src/vector.c b/src/vector.c
index 7513ea3f0..304f324f0 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -10,7 +10,7 @@
#include "vector.h"
static const double resize_factor = 1.75;
-static const size_t minimum_size = 8;
+static const unsigned int minimum_size = 8;
static int resize_vector(git_vector *v)
{
@@ -19,10 +19,9 @@ static int resize_vector(git_vector *v)
v->_alloc_size = minimum_size;
v->contents = git__realloc(v->contents, v->_alloc_size * sizeof(void *));
- if (v->contents == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(v->contents);
- return GIT_SUCCESS;
+ return 0;
}
void git_vector_free(git_vector *v)
@@ -52,30 +51,29 @@ int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp
v->sorted = 1;
v->contents = git__malloc(v->_alloc_size * sizeof(void *));
- if (v->contents == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(v->contents);
- return GIT_SUCCESS;
+ return 0;
}
int git_vector_insert(git_vector *v, void *element)
{
assert(v);
- if (v->length >= v->_alloc_size) {
- if (resize_vector(v) < 0)
- return GIT_ENOMEM;
- }
+ if (v->length >= v->_alloc_size &&
+ resize_vector(v) < 0)
+ return -1;
v->contents[v->length++] = element;
v->sorted = 0;
- return GIT_SUCCESS;
+ return 0;
}
-int git_vector_insert_sorted(git_vector *v, void *element, int (*on_dup)(void **old, void *new))
+int git_vector_insert_sorted(
+ git_vector *v, void *element, int (*on_dup)(void **old, void *new))
{
- int error = GIT_SUCCESS;
+ int result;
size_t pos;
assert(v && v->_cmp);
@@ -83,22 +81,18 @@ int git_vector_insert_sorted(git_vector *v, void *element, int (*on_dup)(void **
if (!v->sorted)
git_vector_sort(v);
- if (v->length >= v->_alloc_size) {
- if (resize_vector(v) < 0)
- return GIT_ENOMEM;
- }
-
- error = git__bsearch(v->contents, v->length, element, v->_cmp, &pos);
+ if (v->length >= v->_alloc_size &&
+ resize_vector(v) < 0)
+ return -1;
- /* If we found the element and have a duplicate handler callback,
- * invoke it. If it returns an error, then cancel insert, otherwise
+ /* If we find the element and have a duplicate handler callback,
+ * invoke it. If it returns non-zero, then cancel insert, otherwise
* proceed with normal insert.
*/
- if (error == GIT_SUCCESS && on_dup != NULL) {
- error = on_dup(&v->contents[pos], element);
- if (error != GIT_SUCCESS)
- return error;
- }
+ if (git__bsearch(v->contents, v->length, element, v->_cmp, &pos) >= 0 &&
+ on_dup != NULL &&
+ (result = on_dup(&v->contents[pos], element)) < 0)
+ return result;
/* shift elements to the right */
if (pos < v->length) {
@@ -108,8 +102,7 @@ int git_vector_insert_sorted(git_vector *v, void *element, int (*on_dup)(void **
v->contents[pos] = element;
v->length++;
-
- return GIT_SUCCESS;
+ return 0;
}
void git_vector_sort(git_vector *v)
@@ -130,16 +123,14 @@ int git_vector_bsearch2(git_vector *v, git_vector_cmp key_lookup, const void *ke
assert(v && key && key_lookup);
/* need comparison function to sort the vector */
- if (v->_cmp == NULL)
- return git__throw(GIT_ENOTFOUND, "Can't sort vector. No comparison function set");
+ assert(v->_cmp != NULL);
git_vector_sort(v);
- if (git__bsearch(v->contents, v->length, key, key_lookup,
- &pos) == GIT_SUCCESS)
+ if (git__bsearch(v->contents, v->length, key, key_lookup, &pos) >= 0)
return (int)pos;
- return git__throw(GIT_ENOTFOUND, "Can't find element");
+ return GIT_ENOTFOUND;
}
int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key)
@@ -153,7 +144,7 @@ int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key
return i;
}
- return git__throw(GIT_ENOTFOUND, "Can't find element");
+ return GIT_ENOTFOUND;
}
static int strict_comparison(const void *a, const void *b)
@@ -178,13 +169,13 @@ int git_vector_remove(git_vector *v, unsigned int idx)
assert(v);
if (idx >= v->length || v->length == 0)
- return git__throw(GIT_ENOTFOUND, "Can't remove element. Index out of bounds");
+ return GIT_ENOTFOUND;
for (i = idx; i < v->length - 1; ++i)
v->contents[i] = v->contents[i + 1];
v->length--;
- return GIT_SUCCESS;
+ return 0;
}
void git_vector_pop(git_vector *v)
diff --git a/src/vector.h b/src/vector.h
index 180edbf7c..5bc27914a 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -44,6 +44,8 @@ GIT_INLINE(const void *) git_vector_get_const(const git_vector *v, unsigned int
return (position < v->length) ? v->contents[position] : NULL;
}
+#define GIT_VECTOR_GET(V,I) ((I) < (V)->length ? (V)->contents[(I)] : NULL)
+
GIT_INLINE(void *) git_vector_last(git_vector *v)
{
return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL;
diff --git a/src/win32/dir.c b/src/win32/dir.c
index 035e2b685..bc3d40fa5 100644
--- a/src/win32/dir.c
+++ b/src/win32/dir.c
@@ -27,8 +27,8 @@ static int init_filter(char *filter, size_t n, const char *dir)
git__DIR *git__opendir(const char *dir)
{
char filter[4096];
- wchar_t* filter_w;
- git__DIR *new;
+ wchar_t* filter_w = NULL;
+ git__DIR *new = NULL;
if (!dir || !init_filter(filter, sizeof(filter), dir))
return NULL;
@@ -37,25 +37,29 @@ git__DIR *git__opendir(const char *dir)
if (!new)
return NULL;
- new->dir = git__malloc(strlen(dir)+1);
- if (!new->dir) {
- git__free(new);
- return NULL;
- }
- strcpy(new->dir, dir);
+ new->dir = git__strdup(dir);
+ if (!new->dir)
+ goto fail;
filter_w = gitwin_to_utf16(filter);
+ if (!filter_w)
+ goto fail;
+
new->h = FindFirstFileW(filter_w, &new->f);
git__free(filter_w);
if (new->h == INVALID_HANDLE_VALUE) {
- git__free(new->dir);
- git__free(new);
- return NULL;
+ giterr_set(GITERR_OS, "Could not open directory '%s'", dir);
+ goto fail;
}
- new->first = 1;
+ new->first = 1;
return new;
+
+fail:
+ git__free(new->dir);
+ git__free(new);
+ return NULL;
}
int git__readdir_ext(
@@ -67,22 +71,32 @@ int git__readdir_ext(
if (!d || !entry || !result || d->h == INVALID_HANDLE_VALUE)
return -1;
+ *result = NULL;
+
if (d->first)
d->first = 0;
else if (!FindNextFileW(d->h, &d->f)) {
- *result = NULL;
- return 0;
+ if (GetLastError() == ERROR_NO_MORE_FILES)
+ return 0;
+ giterr_set(GITERR_OS, "Could not read from directory '%s'", d->dir);
+ return -1;
}
if (wcslen(d->f.cFileName) >= sizeof(entry->d_name))
return -1;
entry->d_ino = 0;
- WideCharToMultiByte(
+
+ if (WideCharToMultiByte(
gitwin_get_codepage(), 0, d->f.cFileName, -1,
- entry->d_name, GIT_PATH_MAX, NULL, NULL);
+ entry->d_name, GIT_PATH_MAX, NULL, NULL) == 0)
+ {
+ giterr_set(GITERR_OS, "Could not convert filename to UTF-8");
+ return -1;
+ }
*result = entry;
+
if (is_dir != NULL)
*is_dir = ((d->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
@@ -102,32 +116,40 @@ void git__rewinddir(git__DIR *d)
char filter[4096];
wchar_t* filter_w;
- if (d) {
- if (d->h != INVALID_HANDLE_VALUE)
- FindClose(d->h);
+ if (!d)
+ return;
+
+ if (d->h != INVALID_HANDLE_VALUE) {
+ FindClose(d->h);
d->h = INVALID_HANDLE_VALUE;
d->first = 0;
+ }
- if (init_filter(filter, sizeof(filter), d->dir)) {
- filter_w = gitwin_to_utf16(filter);
- d->h = FindFirstFileW(filter_w, &d->f);
- git__free(filter_w);
+ if (!init_filter(filter, sizeof(filter), d->dir) ||
+ (filter_w = gitwin_to_utf16(filter)) == NULL)
+ return;
- if (d->h != INVALID_HANDLE_VALUE)
- d->first = 1;
- }
- }
+ d->h = FindFirstFileW(filter_w, &d->f);
+ git__free(filter_w);
+
+ if (d->h == INVALID_HANDLE_VALUE)
+ giterr_set(GITERR_OS, "Could not open directory '%s'", d->dir);
+ else
+ d->first = 1;
}
int git__closedir(git__DIR *d)
{
- if (d) {
- if (d->h != INVALID_HANDLE_VALUE)
- FindClose(d->h);
- if (d->dir)
- git__free(d->dir);
- git__free(d);
+ if (!d)
+ return 0;
+
+ if (d->h != INVALID_HANDLE_VALUE) {
+ FindClose(d->h);
+ d->h = INVALID_HANDLE_VALUE;
}
+ git__free(d->dir);
+ d->dir = NULL;
+ git__free(d);
return 0;
}
diff --git a/src/win32/map.c b/src/win32/map.c
index 60adf0f94..f730120cc 100644
--- a/src/win32/map.c
+++ b/src/win32/map.c
@@ -33,12 +33,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs
git_off_t page_start;
git_off_t page_offset;
- assert((out != NULL) && (len > 0));
-
- if ((out == NULL) || (len == 0)) {
- errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. No map or zero length");
- }
+ GIT_MMAP_VALIDATE(out, len, prot, flags);
out->data = NULL;
out->len = 0;
@@ -46,86 +41,75 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs
if (fh == INVALID_HANDLE_VALUE) {
errno = EBADF;
- return git__throw(GIT_ERROR, "Failed to mmap. Invalid handle value");
+ giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value");
+ return -1;
}
if (prot & GIT_PROT_WRITE)
fmap_prot |= PAGE_READWRITE;
else if (prot & GIT_PROT_READ)
fmap_prot |= PAGE_READONLY;
- else {
- errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. Invalid protection parameters");
- }
if (prot & GIT_PROT_WRITE)
view_prot |= FILE_MAP_WRITE;
if (prot & GIT_PROT_READ)
view_prot |= FILE_MAP_READ;
- if (flags & GIT_MAP_FIXED) {
- errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. FIXED not set");
- }
-
page_start = (offset / page_size) * page_size;
page_offset = offset - page_start;
if (page_offset != 0) { /* offset must be multiple of page size */
errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. Offset must be multiple of page size");
+ giterr_set(GITERR_OS, "Failed to mmap. Offset must be multiple of page size");
+ return -1;
}
out->fmh = CreateFileMapping(fh, NULL, fmap_prot, 0, 0, NULL);
if (!out->fmh || out->fmh == INVALID_HANDLE_VALUE) {
- /* errno = ? */
+ giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value");
out->fmh = NULL;
- return git__throw(GIT_ERROR, "Failed to mmap. Invalid handle value");
+ return -1;
}
assert(sizeof(git_off_t) == 8);
+
off_low = (DWORD)(page_start);
off_hi = (DWORD)(page_start >> 32);
out->data = MapViewOfFile(out->fmh, view_prot, off_hi, off_low, len);
if (!out->data) {
- /* errno = ? */
+ giterr_set(GITERR_OS, "Failed to mmap. No data written");
CloseHandle(out->fmh);
out->fmh = NULL;
- return git__throw(GIT_ERROR, "Failed to mmap. No data written");
+ return -1;
}
out->len = len;
- return GIT_SUCCESS;
+ return 0;
}
int p_munmap(git_map *map)
{
- assert(map != NULL);
+ int error = 0;
- if (!map)
- return git__throw(GIT_ERROR, "Failed to munmap. Map does not exist");
+ assert(map != NULL);
if (map->data) {
if (!UnmapViewOfFile(map->data)) {
- /* errno = ? */
- CloseHandle(map->fmh);
- map->data = NULL;
- map->fmh = NULL;
- return git__throw(GIT_ERROR, "Failed to munmap. Could not unmap view of file");
+ giterr_set(GITERR_OS, "Failed to munmap. Could not unmap view of file");
+ error = -1;
}
map->data = NULL;
}
if (map->fmh) {
if (!CloseHandle(map->fmh)) {
- /* errno = ? */
- map->fmh = NULL;
- return git__throw(GIT_ERROR, "Failed to munmap. Could not close handle");
+ giterr_set(GITERR_OS, "Failed to munmap. Could not close handle");
+ error = -1;
}
map->fmh = NULL;
}
- return GIT_SUCCESS;
+ return error;
}
diff --git a/src/win32/posix.h b/src/win32/posix.h
index 60adc9666..d13d3e39b 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -49,5 +49,7 @@ extern int p_open(const char *path, int flags);
extern int p_creat(const char *path, mode_t mode);
extern int p_getcwd(char *buffer_out, size_t size);
extern int p_rename(const char *from, const char *to);
+extern int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags);
+extern int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags);
#endif
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 91585aeae..8af664165 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -4,7 +4,7 @@
* 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 "posix.h"
+#include "../posix.h"
#include "path.h"
#include "utf-conv.h"
#include <errno.h>
@@ -17,10 +17,11 @@ int p_unlink(const char *path)
int ret = 0;
wchar_t* buf;
- buf = gitwin_to_utf16(path);
- _wchmod(buf, 0666);
- ret = _wunlink(buf);
- git__free(buf);
+ if ((buf = gitwin_to_utf16(path)) != NULL) {
+ _wchmod(buf, 0666);
+ ret = _wunlink(buf);
+ git__free(buf);
+ }
return ret;
}
@@ -60,6 +61,8 @@ static int do_lstat(const char *file_name, struct stat *buf)
{
WIN32_FILE_ATTRIBUTE_DATA fdata;
wchar_t* fbuf = gitwin_to_utf16(file_name);
+ if (!fbuf)
+ return -1;
if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) {
int fMode = S_IREAD;
@@ -87,54 +90,43 @@ static int do_lstat(const char *file_name, struct stat *buf)
buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
git__free(fbuf);
- return GIT_SUCCESS;
+ return 0;
}
git__free(fbuf);
-
- switch (GetLastError()) {
- case ERROR_ACCESS_DENIED:
- case ERROR_SHARING_VIOLATION:
- case ERROR_LOCK_VIOLATION:
- case ERROR_SHARING_BUFFER_EXCEEDED:
- return GIT_EOSERR;
-
- case ERROR_BUFFER_OVERFLOW:
- case ERROR_NOT_ENOUGH_MEMORY:
- return GIT_ENOMEM;
-
- default:
- return GIT_EINVALIDPATH;
- }
+ return -1;
}
int p_lstat(const char *file_name, struct stat *buf)
{
- int namelen, error;
- char alt_name[GIT_PATH_MAX];
+ int error;
+ size_t namelen;
+ char *alt_name;
- if ((error = do_lstat(file_name, buf)) == GIT_SUCCESS)
- return GIT_SUCCESS;
+ if (do_lstat(file_name, buf) == 0)
+ return 0;
/* if file_name ended in a '/', Windows returned ENOENT;
* try again without trailing slashes
*/
- if (error != GIT_EINVALIDPATH)
- return git__throw(GIT_EOSERR, "Failed to lstat file");
-
namelen = strlen(file_name);
if (namelen && file_name[namelen-1] != '/')
- return git__throw(GIT_EOSERR, "Failed to lstat file");
+ return -1;
while (namelen && file_name[namelen-1] == '/')
--namelen;
- if (!namelen || namelen >= GIT_PATH_MAX)
- return git__throw(GIT_ENOMEM, "Failed to lstat file");
+ if (!namelen)
+ return -1;
+
+ alt_name = git__strndup(file_name, namelen);
+ if (!alt_name)
+ return -1;
- memcpy(alt_name, file_name, namelen);
- alt_name[namelen] = 0;
- return do_lstat(alt_name, buf);
+ error = do_lstat(alt_name, buf);
+
+ git__free(alt_name);
+ return error;
}
int p_readlink(const char *link, char *target, size_t target_len)
@@ -145,6 +137,9 @@ int p_readlink(const char *link, char *target, size_t target_len)
DWORD dwRet;
wchar_t* link_w;
wchar_t* target_w;
+ int error = 0;
+
+ assert(link && target && target_len > 0);
/*
* Try to load the pointer to pGetFinalPath dynamically, because
@@ -156,12 +151,15 @@ int p_readlink(const char *link, char *target, size_t target_len)
if (library != NULL)
pGetFinalPath = (fpath_func)GetProcAddress(library, "GetFinalPathNameByHandleW");
- if (pGetFinalPath == NULL)
- return git__throw(GIT_EOSERR,
+ if (pGetFinalPath == NULL) {
+ giterr_set(GITERR_OS,
"'GetFinalPathNameByHandleW' is not available in this platform");
+ return -1;
+ }
}
link_w = gitwin_to_utf16(link);
+ GITERR_CHECK_ALLOC(link_w);
hFile = CreateFileW(link_w, // file to open
GENERIC_READ, // open for reading
@@ -173,50 +171,49 @@ int p_readlink(const char *link, char *target, size_t target_len)
git__free(link_w);
- if (hFile == INVALID_HANDLE_VALUE)
- return GIT_EOSERR;
-
- if (target_len <= 0) {
- return GIT_EINVALIDARGS;
+ if (hFile == INVALID_HANDLE_VALUE) {
+ giterr_set(GITERR_OS, "Cannot open '%s' for reading", link);
+ return -1;
}
target_w = (wchar_t*)git__malloc(target_len * sizeof(wchar_t));
+ GITERR_CHECK_ALLOC(target_w);
- dwRet = pGetFinalPath(hFile, target_w, target_len, 0x0);
- if (dwRet >= target_len) {
- git__free(target_w);
- CloseHandle(hFile);
- return GIT_ENOMEM;
- }
-
- if (!WideCharToMultiByte(CP_UTF8, 0, target_w, -1, target, target_len * sizeof(char), NULL, NULL)) {
- git__free(target_w);
- return GIT_EOSERR;
- }
+ dwRet = pGetFinalPath(hFile, target_w, (DWORD)target_len, 0x0);
+ if (dwRet == 0 ||
+ dwRet >= target_len ||
+ !WideCharToMultiByte(CP_UTF8, 0, target_w, -1, target,
+ (int)(target_len * sizeof(char)), NULL, NULL))
+ error = -1;
git__free(target_w);
CloseHandle(hFile);
- if (dwRet > 4) {
- /* Skip first 4 characters if they are "\\?\" */
- if (target[0] == '\\' && target[1] == '\\' && target[2] == '?' && target[3] == '\\') {
- char tmp[GIT_PATH_MAX];
- unsigned int offset = 4;
- dwRet -= 4;
-
- /* \??\UNC\ */
- if (dwRet > 7 && target[4] == 'U' && target[5] == 'N' && target[6] == 'C') {
- offset += 2;
- dwRet -= 2;
- target[offset] = '\\';
- }
-
- memcpy(tmp, target + offset, dwRet);
- memcpy(target, tmp, dwRet);
+ if (error)
+ return error;
+
+ /* Skip first 4 characters if they are "\\?\" */
+ if (dwRet > 4 &&
+ target[0] == '\\' && target[1] == '\\' &&
+ target[2] == '?' && target[3] == '\\')
+ {
+ unsigned int offset = 4;
+ dwRet -= 4;
+
+ /* \??\UNC\ */
+ if (dwRet > 7 &&
+ target[4] == 'U' && target[5] == 'N' && target[6] == 'C')
+ {
+ offset += 2;
+ dwRet -= 2;
+ target[offset] = '\\';
}
+
+ memmove(target, target + offset, dwRet);
}
target[dwRet] = '\0';
+
return dwRet;
}
@@ -224,8 +221,9 @@ int p_open(const char *path, int flags)
{
int fd;
wchar_t* buf = gitwin_to_utf16(path);
+ if (!buf)
+ return -1;
fd = _wopen(buf, flags | _O_BINARY);
-
git__free(buf);
return fd;
}
@@ -234,24 +232,31 @@ int p_creat(const char *path, mode_t mode)
{
int fd;
wchar_t* buf = gitwin_to_utf16(path);
+ if (!buf)
+ return -1;
fd = _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode);
-
git__free(buf);
return fd;
}
int p_getcwd(char *buffer_out, size_t size)
{
- wchar_t* buf = (wchar_t*)git__malloc(sizeof(wchar_t) * (int)size);
+ int ret;
+ wchar_t* buf;
+
+ if ((size_t)((int)size) != size)
+ return -1;
+
+ buf = (wchar_t*)git__malloc(sizeof(wchar_t) * (int)size);
+ GITERR_CHECK_ALLOC(buf);
+
_wgetcwd(buf, (int)size);
- if (!WideCharToMultiByte(CP_UTF8, 0, buf, -1, buffer_out, size, NULL, NULL)) {
- git__free(buf);
- return GIT_EOSERR;
- }
+ ret = WideCharToMultiByte(
+ CP_UTF8, 0, buf, -1, buffer_out, (int)size, NULL, NULL);
git__free(buf);
- return GIT_SUCCESS;
+ return !ret ? -1 : 0;
}
int p_stat(const char* path, struct stat* buf)
@@ -262,8 +267,10 @@ int p_stat(const char* path, struct stat* buf)
int p_chdir(const char* path)
{
wchar_t* buf = gitwin_to_utf16(path);
- int ret = _wchdir(buf);
-
+ int ret;
+ if (!buf)
+ return -1;
+ ret = _wchdir(buf);
git__free(buf);
return ret;
}
@@ -271,8 +278,10 @@ int p_chdir(const char* path)
int p_chmod(const char* path, mode_t mode)
{
wchar_t* buf = gitwin_to_utf16(path);
- int ret = _wchmod(buf, mode);
-
+ int ret;
+ if (!buf)
+ return -1;
+ ret = _wchmod(buf, mode);
git__free(buf);
return ret;
}
@@ -280,26 +289,25 @@ int p_chmod(const char* path, mode_t mode)
int p_rmdir(const char* path)
{
wchar_t* buf = gitwin_to_utf16(path);
- int ret = _wrmdir(buf);
-
+ int ret;
+ if (!buf)
+ return -1;
+ ret = _wrmdir(buf);
git__free(buf);
return ret;
}
int p_hide_directory__w32(const char *path)
{
- int error;
+ int res;
wchar_t* buf = gitwin_to_utf16(path);
+ if (!buf)
+ return -1;
- error = SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN) != 0 ?
- GIT_SUCCESS : GIT_ERROR; /* MSDN states a "non zero" value indicates a success */
-
+ res = SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN);
git__free(buf);
- if (error < GIT_SUCCESS)
- error = git__throw(GIT_EOSERR, "Failed to hide directory '%s'", path);
-
- return error;
+ return (res != 0) ? 0 : -1; /* MSDN states a "non zero" value indicates a success */
}
char *p_realpath(const char *orig_path, char *buffer)
@@ -308,6 +316,9 @@ char *p_realpath(const char *orig_path, char *buffer)
wchar_t* orig_path_w = gitwin_to_utf16(orig_path);
wchar_t* buffer_w = (wchar_t*)git__malloc(GIT_PATH_MAX * sizeof(wchar_t));
+ if (!orig_path_w || !buffer_w)
+ return NULL;
+
ret = GetFullPathNameW(orig_path_w, GIT_PATH_MAX, buffer_w, NULL);
git__free(orig_path_w);
@@ -341,8 +352,12 @@ done:
int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
{
#ifdef _MSC_VER
- int len = _vsnprintf(buffer, count, format, argptr);
- return (len < 0) ? _vscprintf(format, argptr) : len;
+ int len;
+
+ if (count == 0 || (len = _vsnprintf(buffer, count, format, argptr)) < 0)
+ return _vscprintf(format, argptr);
+
+ return len;
#else /* MinGW */
return vsnprintf(buffer, count, format, argptr);
#endif
@@ -366,10 +381,10 @@ int p_mkstemp(char *tmp_path)
{
#if defined(_MSC_VER)
if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
- return GIT_EOSERR;
+ return -1;
#else
if (_mktemp(tmp_path) == NULL)
- return GIT_EOSERR;
+ return -1;
#endif
return p_creat(tmp_path, 0744);
@@ -378,15 +393,17 @@ int p_mkstemp(char *tmp_path)
int p_setenv(const char* name, const char* value, int overwrite)
{
if (overwrite != 1)
- return EINVAL;
+ return -1;
- return (SetEnvironmentVariableA(name, value) == 0 ? GIT_EOSERR : GIT_SUCCESS);
+ return (SetEnvironmentVariableA(name, value) == 0 ? -1 : 0);
}
int p_access(const char* path, mode_t mode)
{
wchar_t *buf = gitwin_to_utf16(path);
int ret;
+ if (!buf)
+ return -1;
ret = _waccess(buf, mode);
git__free(buf);
@@ -394,16 +411,35 @@ int p_access(const char* path, mode_t mode)
return ret;
}
-extern int p_rename(const char *from, const char *to)
+int p_rename(const char *from, const char *to)
{
wchar_t *wfrom = gitwin_to_utf16(from);
wchar_t *wto = gitwin_to_utf16(to);
int ret;
- ret = MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR;
+ if (!wfrom || !wto)
+ return -1;
+
+ ret = MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1;
git__free(wfrom);
git__free(wto);
return ret;
}
+
+int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
+{
+ if ((size_t)((int)length) != length)
+ return -1; /* giterr_set will be done by caller */
+
+ return recv(socket, buffer, (int)length, flags);
+}
+
+int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)
+{
+ if ((size_t)((int)length) != length)
+ return -1; /* giterr_set will be done by caller */
+
+ return send(socket, buffer, (int)length, flags);
+}
diff --git a/src/win32/pthread.c b/src/win32/pthread.c
index 3db536848..3a186c8d9 100644
--- a/src/win32/pthread.c
+++ b/src/win32/pthread.c
@@ -7,13 +7,16 @@
#include "pthread.h"
-int pthread_create(pthread_t *GIT_RESTRICT thread,
- const pthread_attr_t *GIT_RESTRICT attr,
- void *(*start_routine)(void*), void *GIT_RESTRICT arg)
+int pthread_create(
+ pthread_t *GIT_RESTRICT thread,
+ const pthread_attr_t *GIT_RESTRICT attr,
+ void *(*start_routine)(void*),
+ void *GIT_RESTRICT arg)
{
GIT_UNUSED(attr);
- *thread = (pthread_t) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL);
- return *thread ? GIT_SUCCESS : git__throw(GIT_EOSERR, "Failed to create pthread");
+ *thread = (pthread_t) CreateThread(
+ NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL);
+ return *thread ? 0 : -1;
}
int pthread_join(pthread_t thread, void **value_ptr)
diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c
index 3c8be81d1..fbcb69d0a 100644
--- a/src/win32/utf-conv.c
+++ b/src/win32/utf-conv.c
@@ -31,25 +31,24 @@ void gitwin_set_utf8(void)
wchar_t* gitwin_to_utf16(const char* str)
{
wchar_t* ret;
- int cb;
+ size_t cb;
- if (!str) {
+ if (!str)
return NULL;
- }
cb = strlen(str) * sizeof(wchar_t);
- if (cb == 0) {
- ret = (wchar_t*)git__malloc(sizeof(wchar_t));
- ret[0] = 0;
- return ret;
- }
+ if (cb == 0)
+ return (wchar_t *)git__calloc(1, sizeof(wchar_t));
/* Add space for null terminator */
cb += sizeof(wchar_t);
- ret = (wchar_t*)git__malloc(cb);
+ ret = (wchar_t *)git__malloc(cb);
+ if (!ret)
+ return NULL;
- if (MultiByteToWideChar(_active_codepage, 0, str, -1, ret, cb) == 0) {
+ if (MultiByteToWideChar(_active_codepage, 0, str, -1, ret, (int)cb) == 0) {
+ giterr_set(GITERR_OS, "Could not convert string to UTF-16");
git__free(ret);
ret = NULL;
}
@@ -59,31 +58,33 @@ wchar_t* gitwin_to_utf16(const char* str)
int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len)
{
- return MultiByteToWideChar(_active_codepage, 0, str, -1, buffer, len);
+ int result = MultiByteToWideChar(_active_codepage, 0, str, -1, buffer, (int)len);
+ if (result == 0)
+ giterr_set(GITERR_OS, "Could not convert string to UTF-16");
+ return result;
}
char* gitwin_from_utf16(const wchar_t* str)
{
char* ret;
- int cb;
+ size_t cb;
- if (!str) {
+ if (!str)
return NULL;
- }
cb = wcslen(str) * sizeof(char);
- if (cb == 0) {
- ret = (char*)git__malloc(sizeof(char));
- ret[0] = 0;
- return ret;
- }
+ if (cb == 0)
+ return (char *)git__calloc(1, sizeof(char));
/* Add space for null terminator */
cb += sizeof(char);
ret = (char*)git__malloc(cb);
+ if (!ret)
+ return NULL;
- if (WideCharToMultiByte(_active_codepage, 0, str, -1, ret, cb, NULL, NULL) == 0) {
+ if (WideCharToMultiByte(_active_codepage, 0, str, -1, ret, (int)cb, NULL, NULL) == 0) {
+ giterr_set(GITERR_OS, "Could not convert string to UTF-8");
git__free(ret);
ret = NULL;
}
diff --git a/src/xdiff/xemit.c b/src/xdiff/xemit.c
index 8b7d417a1..e3e63d902 100644
--- a/src/xdiff/xemit.c
+++ b/src/xdiff/xemit.c
@@ -40,7 +40,7 @@ static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {
static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {
- long size, psize = strlen(pre);
+ long size, psize = (long)strlen(pre);
char const *rec;
size = xdl_get_rec(xdf, ri, &rec);
diff --git a/src/xdiff/xmerge.c b/src/xdiff/xmerge.c
index 9e13b25ab..84e424672 100644
--- a/src/xdiff/xmerge.c
+++ b/src/xdiff/xmerge.c
@@ -149,9 +149,9 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
int size, int i, int style,
xdmerge_t *m, char *dest, int marker_size)
{
- int marker1_size = (name1 ? strlen(name1) + 1 : 0);
- int marker2_size = (name2 ? strlen(name2) + 1 : 0);
- int marker3_size = (name3 ? strlen(name3) + 1 : 0);
+ int marker1_size = (name1 ? (int)strlen(name1) + 1 : 0);
+ int marker2_size = (name2 ? (int)strlen(name2) + 1 : 0);
+ int marker3_size = (name3 ? (int)strlen(name3) + 1 : 0);
if (marker_size <= 0)
marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
diff --git a/src/xdiff/xutils.c b/src/xdiff/xutils.c
index 9dea04bba..bb7bdee49 100644
--- a/src/xdiff/xutils.c
+++ b/src/xdiff/xutils.c
@@ -62,14 +62,14 @@ int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
void *xdl_mmfile_first(mmfile_t *mmf, long *size)
{
- *size = mmf->size;
+ *size = (long)mmf->size;
return mmf->ptr;
}
long xdl_mmfile_size(mmfile_t *mmf)
{
- return mmf->size;
+ return (long)mmf->size;
}
@@ -321,7 +321,7 @@ int xdl_num_out(char *out, long val) {
*str++ = '0';
*str = '\0';
- return str - out;
+ return (int)(str - out);
}