From 9f77b3f6f5ce6944ec49dfc666ef6b8df0af0c6b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 25 Nov 2013 14:21:34 -0800 Subject: Add config read fns with controlled error behavior This adds `git_config__lookup_entry` which will look up a key in a config and return either the entry or NULL if the key was not present. Optionally, it can either suppress all errors or can return them (although not finding the key is not an error for this function). Unlike other accessors, this does not normalize the config key string, so it must only be used when the key is known to be in normalized form (i.e. all lower-case before the first dot and after the last dot, with no invalid characters). This also adds three high-level helper functions to look up config values with no errors and a fallback value. The three functions are for string, bool, and int values, and will resort to the fallback value for any error that arises. They are: * `git_config__get_string_force` * `git_config__get_bool_force` * `git_config__get_int_force` None of them normalize the config `key` either, so they can only be used for internal cases where the key is known to be in normal format. --- include/git2/diff.h | 8 +-- src/attr.c | 16 ++--- src/config.c | 181 +++++++++++++++++++++++++++++++------------------ src/config.h | 21 ++++++ src/config_cache.c | 22 +++--- src/config_file.c | 13 +--- src/diff.c | 39 +++-------- src/diff_driver.c | 39 ++++++----- src/diff_tform.c | 35 ++++------ src/merge.c | 17 ++--- src/notes.c | 15 +--- src/remote.c | 63 +++++++---------- src/repository.c | 40 ++++++----- src/submodule.c | 14 ++-- src/transports/local.c | 3 +- 15 files changed, 267 insertions(+), 259 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 315cc1215..d6919393a 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -468,7 +468,7 @@ typedef int (*git_diff_line_cb)( * Flags to control the behavior of diff rename/copy detection. */ typedef enum { - /** Obey `diff.renames`. This is overridden by any other GIT_DIFF_FIND_ALL flag. */ + /** Obey `diff.renames`. Overridden by any other GIT_DIFF_FIND_... flag. */ GIT_DIFF_FIND_BY_CONFIG = 0, /** Look for renames? (`--find-renames`) */ @@ -577,9 +577,9 @@ typedef struct { unsigned int version; /** - * Combination of git_diff_find_t values (default FIND_BY_CONFIG). - * Note that if you don't explicitly set this, `diff.renames` could be set - * to false, resulting in `git_diff_find_similar` doing nothing. + * Combination of git_diff_find_t values (default GIT_DIFF_FIND_BY_CONFIG). + * NOTE: if you don't explicitly set this, `diff.renames` could be set + * to false, resulting in `git_diff_find_similar` doing nothing. */ uint32_t flags; diff --git a/src/attr.c b/src/attr.c index 98a328a55..51895b7ac 100644 --- a/src/attr.c +++ b/src/attr.c @@ -603,11 +603,15 @@ static int attr_cache__lookup_path( { git_buf buf = GIT_BUF_INIT; int error; - const char *cfgval = NULL; + const git_config_entry *entry = NULL; *out = NULL; - if (!(error = git_config_get_string(&cfgval, cfg, key))) { + if ((error = git_config__lookup_entry(&entry, cfg, key, false)) < 0) + return error; + + if (entry) { + const char *cfgval = entry->value; /* expand leading ~/ as needed */ if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' && @@ -616,13 +620,9 @@ static int attr_cache__lookup_path( else if (cfgval) *out = git__strdup(cfgval); - } else if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = 0; - - if (!git_futils_find_xdg_file(&buf, fallback)) - *out = git_buf_detach(&buf); } + else if (!git_futils_find_xdg_file(&buf, fallback)) + *out = git_buf_detach(&buf); git_buf_free(&buf); diff --git a/src/config.c b/src/config.c index 0d9471383..227adbc9b 100644 --- a/src/config.c +++ b/src/config.c @@ -620,6 +620,78 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value) /*********** * Getters ***********/ + +static int config_error_notfound(const char *name) +{ + giterr_set(GITERR_CONFIG, "Config value '%s' was not found", name); + return GIT_ENOTFOUND; +} + +enum { + GET_ALL_ERRORS = 0, + GET_NO_MISSING = 1, + GET_NO_ERRORS = 2 +}; + +static int get_entry( + const git_config_entry **out, + const git_config *cfg, + const char *name, + bool normalize_name, + int want_errors) +{ + int res = GIT_ENOTFOUND; + const char *key = name; + char *normalized = NULL; + size_t i; + file_internal *internal; + + *out = NULL; + + if (normalize_name) { + if ((res = git_config__normalize_name(name, &normalized)) < 0) + goto cleanup; + key = normalized; + } + + git_vector_foreach(&cfg->files, i, internal) { + if (!internal || !internal->file) + continue; + + res = internal->file->get(internal->file, key, out); + if (res != GIT_ENOTFOUND) + break; + } + + git__free(normalized); + +cleanup: + if (res == GIT_ENOTFOUND) + res = (want_errors > GET_ALL_ERRORS) ? 0 : config_error_notfound(name); + else if (res && (want_errors == GET_NO_ERRORS)) { + giterr_clear(); + res = 0; + } + + return res; +} + +int git_config_get_entry( + const git_config_entry **out, const git_config *cfg, const char *name) +{ + return get_entry(out, cfg, name, true, GET_ALL_ERRORS); +} + +int git_config__lookup_entry( + const git_config_entry **out, + const git_config *cfg, + const char *key, + bool no_errors) +{ + return get_entry( + out, cfg, key, false, no_errors ? GET_NO_ERRORS : GET_NO_MISSING); +} + int git_config_get_mapped( int *out, const git_config *cfg, @@ -627,116 +699,91 @@ int git_config_get_mapped( const git_cvar_map *maps, size_t map_n) { - const char *value; + const git_config_entry *entry; int ret; - if ((ret = git_config_get_string(&value, cfg, name)) < 0) + if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return ret; - return git_config_lookup_map_value(out, maps, map_n, value); + return git_config_lookup_map_value(out, maps, map_n, entry->value); } int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name) { - const char *value; + const git_config_entry *entry; int ret; - if ((ret = git_config_get_string(&value, cfg, name)) < 0) + if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return ret; - return git_config_parse_int64(out, value); + return git_config_parse_int64(out, entry->value); } int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name) { - const char *value; + const git_config_entry *entry; int ret; - if ((ret = git_config_get_string(&value, cfg, name)) < 0) + if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return ret; - return git_config_parse_int32(out, value); + return git_config_parse_int32(out, entry->value); } -static int get_string_at_file(const char **out, const git_config_backend *file, const char *name) +int git_config_get_bool(int *out, const git_config *cfg, const char *name) { const git_config_entry *entry; - int res; + int ret; - res = file->get(file, name, &entry); - if (!res) - *out = entry->value; + if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) + return ret; - return res; + return git_config_parse_bool(out, entry->value); } -static int config_error_notfound(const char *name) +int git_config_get_string( + const char **out, const git_config *cfg, const char *name) { - giterr_set(GITERR_CONFIG, "Config value '%s' was not found", name); - return GIT_ENOTFOUND; + const git_config_entry *entry; + int ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS); + *out = !ret ? (entry->value ? entry->value : "") : NULL; + return ret; } -static int get_string(const char **out, const git_config *cfg, const char *name) +const char *git_config__get_string_force( + const git_config *cfg, const char *key, const char *fallback_value) { - file_internal *internal; - unsigned int i; - int res; - - git_vector_foreach(&cfg->files, i, internal) { - if (!internal || !internal->file) - continue; - - res = get_string_at_file(out, internal->file, name); - if (res != GIT_ENOTFOUND) - return res; - } - - return config_error_notfound(name); + const git_config_entry *entry; + get_entry(&entry, cfg, key, false, GET_NO_ERRORS); + return (entry && entry->value) ? entry->value : fallback_value; } -int git_config_get_bool(int *out, const git_config *cfg, const char *name) +int git_config__get_bool_force( + const git_config *cfg, const char *key, int fallback_value) { - const char *value = NULL; - int ret; - - if ((ret = get_string(&value, cfg, name)) < 0) - return ret; - - return git_config_parse_bool(out, value); -} + int val = fallback_value; + const git_config_entry *entry; -int git_config_get_string(const char **out, const git_config *cfg, const char *name) -{ - int ret; - const char *str = NULL; + get_entry(&entry, cfg, key, false, GET_NO_ERRORS); - if ((ret = get_string(&str, cfg, name)) < 0) - return ret; + if (entry && git_config_parse_bool(&val, entry->value) < 0) + giterr_clear(); - *out = str == NULL ? "" : str; - return 0; + return val; } -int git_config_get_entry(const git_config_entry **out, const git_config *cfg, const char *name) +int git_config__get_int_force( + const git_config *cfg, const char *key, int fallback_value) { - file_internal *internal; - unsigned int i; - git_config_backend *file; - int ret; - - *out = NULL; + int32_t val = (int32_t)fallback_value; + const git_config_entry *entry; - git_vector_foreach(&cfg->files, i, internal) { - if (!internal || !internal->file) - continue; - file = internal->file; + get_entry(&entry, cfg, key, false, GET_NO_ERRORS); - ret = file->get(file, name, out); - if (ret != GIT_ENOTFOUND) - return ret; - } + if (entry && git_config_parse_int32(&val, entry->value) < 0) + giterr_clear(); - return config_error_notfound(name); + return (int)val; } int git_config_get_multivar_foreach( @@ -1070,7 +1117,7 @@ int git_config_parse_int64(int64_t *out, const char *value) const char *num_end; int64_t num; - if (git__strtol64(&num, value, &num_end, 0) < 0) + if (!value || git__strtol64(&num, value, &num_end, 0) < 0) goto fail_parse; switch (*num_end) { diff --git a/src/config.h b/src/config.h index 01e8465cc..3cd888c88 100644 --- a/src/config.h +++ b/src/config.h @@ -51,5 +51,26 @@ extern int git_config_file__ondisk(git_config_backend **out, const char *path); extern int git_config__normalize_name(const char *in, char **out); +/* internal only: does not normalize key and sets out to NULL if not found */ +extern int git_config__lookup_entry( + const git_config_entry **out, + const git_config *cfg, + const char *key, + bool no_errors); + +/* + * Lookup functions that cannot fail. These functions look up a config + * value and return a fallback value if the value is missing or if any + * failures occur while trying to access the value. + */ + +extern const char *git_config__get_string_force( + const git_config *cfg, const char *key, const char *fallback_value); + +extern int git_config__get_bool_force( + const git_config *cfg, const char *key, int fallback_value); + +extern int git_config__get_int_force( + const git_config *cfg, const char *key, int fallback_value); #endif diff --git a/src/config_cache.c b/src/config_cache.c index 6808521a3..ec75d1501 100644 --- a/src/config_cache.c +++ b/src/config_cache.c @@ -78,22 +78,22 @@ int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar) struct map_data *data = &_cvar_maps[(int)cvar]; git_config *config; int error; + const git_config_entry *entry; - error = git_repository_config__weakptr(&config, repo); - if (error < 0) + if ((error = git_repository_config__weakptr(&config, repo)) < 0) return error; - if (data->maps) - error = git_config_get_mapped( - out, config, data->cvar_name, data->maps, data->map_count); - else - error = git_config_get_bool(out, config, data->cvar_name); + git_config__lookup_entry(&entry, config, data->cvar_name, false); - if (error == GIT_ENOTFOUND) { - giterr_clear(); + if (!entry) *out = data->default_value; - } - else if (error < 0) + else if (data->maps) + error = git_config_lookup_map_value( + out, data->maps, data->map_count, entry->value); + else + error = git_config_parse_bool(out, entry->value); + + if (error < 0) return error; repo->cvar_cache[(int)cvar] = *out; diff --git a/src/config_file.c b/src/config_file.c index 15c8de49c..0971aa7b0 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -404,20 +404,12 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val /* * Internal function that actually gets the value in string form */ -static int config_get(const git_config_backend *cfg, const char *name, const git_config_entry **out) +static int config_get(const git_config_backend *cfg, const char *key, const git_config_entry **out) { diskfile_backend *b = (diskfile_backend *)cfg; - char *key; - khiter_t pos; - int error; + khiter_t pos = git_strmap_lookup_index(b->values, key); cvar_t *var; - if ((error = git_config__normalize_name(name, &key)) < 0) - return error; - - pos = git_strmap_lookup_index(b->values, key); - git__free(key); - /* no error message; the config system will write one */ if (!git_strmap_valid_index(b->values, pos)) return GIT_ENOTFOUND; @@ -427,7 +419,6 @@ static int config_get(const git_config_backend *cfg, const char *name, const git var = var->next; *out = var->entry; - return 0; } diff --git a/src/diff.c b/src/diff.c index 4c33a0213..53a8f4638 100644 --- a/src/diff.c +++ b/src/diff.c @@ -304,26 +304,6 @@ bool git_diff_delta__should_skip( } -static int config_bool(git_config *cfg, const char *name, int defvalue) -{ - int val = defvalue; - - if (git_config_get_bool(&val, cfg, name) < 0) - giterr_clear(); - - return val; -} - -static int config_int(git_config *cfg, const char *name, int defvalue) -{ - int val = defvalue; - - if (git_config_get_int32(&val, cfg, name) < 0) - giterr_clear(); - - return val; -} - static const char *diff_mnemonic_prefix( git_iterator_type_t type, bool left_side) { @@ -422,8 +402,8 @@ static int diff_list_apply_options( diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; /* load config values that affect diff behavior */ - if (git_repository_config__weakptr(&cfg, repo) < 0) - return -1; + if ((val = git_repository_config__weakptr(&cfg, repo)) < 0) + return val; if (!git_repository__cvar(&val, repo, GIT_CVAR_SYMLINKS) && val) diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS; @@ -445,7 +425,7 @@ static int diff_list_apply_options( /* If not given explicit `opts`, check `diff.xyz` configs */ if (!opts) { - int context = config_int(cfg, "diff.context", 3); + int context = git_config__get_int_force(cfg, "diff.context", 3); diff->opts.context_lines = context >= 0 ? (uint16_t)context : 3; /* add other defaults here */ @@ -460,12 +440,11 @@ static int diff_list_apply_options( /* if ignore_submodules not explicitly set, check diff config */ if (diff->opts.ignore_submodules <= 0) { - const char *str; + const git_config_entry *entry; + git_config__lookup_entry(&entry, cfg, "diff.ignoresubmodules", true); - if (git_config_get_string(&str , cfg, "diff.ignoreSubmodules") < 0) - giterr_clear(); - else if (str != NULL && - git_submodule_parse_ignore(&diff->opts.ignore_submodules, str) < 0) + if (entry && git_submodule_parse_ignore( + &diff->opts.ignore_submodules, entry->value) < 0) giterr_clear(); } @@ -474,9 +453,9 @@ static int diff_list_apply_options( const char *use_old = DIFF_OLD_PREFIX_DEFAULT; const char *use_new = DIFF_NEW_PREFIX_DEFAULT; - if (config_bool(cfg, "diff.noprefix", 0)) { + if (git_config__get_bool_force(cfg, "diff.noprefix", 0)) use_old = use_new = ""; - } else if (config_bool(cfg, "diff.mnemonicprefix", 0)) { + else if (git_config__get_bool_force(cfg, "diff.mnemonicprefix", 0)) { use_old = diff_mnemonic_prefix(diff->old_src, true); use_new = diff_mnemonic_prefix(diff->new_src, false); } diff --git a/src/diff_driver.c b/src/diff_driver.c index bd5a8fbd9..167c0cc5a 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -14,6 +14,7 @@ #include "strmap.h" #include "map.h" #include "buf_text.h" +#include "config.h" #include "repository.h" GIT__USE_STRMAP; @@ -130,14 +131,14 @@ static git_diff_driver_registry *git_repository_driver_registry( static int git_diff_driver_load( git_diff_driver **out, git_repository *repo, const char *driver_name) { - int error = 0, bval; + int error = 0; git_diff_driver_registry *reg; git_diff_driver *drv; size_t namelen = strlen(driver_name); khiter_t pos; git_config *cfg; git_buf name = GIT_BUF_INIT; - const char *val; + const git_config_entry *ce; bool found_driver = false; reg = git_repository_driver_registry(repo); @@ -164,23 +165,21 @@ static int git_diff_driver_load( if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0) goto done; - if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { - if (error != GIT_ENOTFOUND) - goto done; - /* diff..binary unspecified, so just continue */ - giterr_clear(); - } else if (git_config_parse_bool(&bval, val) < 0) { - /* TODO: warn that diff..binary has invalid value */ - giterr_clear(); - } else if (bval) { + + switch (git_config__get_bool_force(cfg, name.ptr, -1)) { + case true: /* if diff..binary is true, just return the binary driver */ *out = &global_drivers[DIFF_DRIVER_BINARY]; goto done; - } else { + case false: /* if diff..binary is false, force binary checks off */ /* but still may have custom function context patterns, etc. */ drv->binary_flags = GIT_DIFF_FORCE_TEXT; found_driver = true; + break; + default: + /* diff..binary unspecified, so just continue */ + break; } /* TODO: warn if diff..command or diff..textconv are set */ @@ -211,16 +210,16 @@ static int git_diff_driver_load( git_buf_truncate(&name, namelen + strlen("diff..")); git_buf_put(&name, "wordregex", strlen("wordregex")); - if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { - if (error != GIT_ENOTFOUND) - goto done; - giterr_clear(); /* no diff..wordregex, so just continue */ - } else if ((error = regcomp(&drv->word_pattern, val, REG_EXTENDED)) != 0) { - /* TODO: warning about bad regex instead of failure */ - error = giterr_set_regex(&drv->word_pattern, error); + if ((error = git_config__lookup_entry(&ce, cfg, name.ptr, false)) < 0) goto done; - } else { + if (!ce || !ce->value) + /* no diff..wordregex, so just continue */; + else if (!(error = regcomp(&drv->word_pattern, ce->value, REG_EXTENDED))) found_driver = true; + else { + /* TODO: warn about bad regex instead of failure */ + error = giterr_set_regex(&drv->word_pattern, error); + goto done; } /* TODO: look up diff..algorithm to turn on minimal / patience diff --git a/src/diff_tform.c b/src/diff_tform.c index 702e43bd3..2f94b2e77 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -13,6 +13,7 @@ #include "hashsig.h" #include "path.h" #include "fileops.h" +#include "config.h" static git_diff_delta *diff_delta__dup( const git_diff_delta *d, git_pool *pool) @@ -290,19 +291,16 @@ static int normalize_find_opts( if (!given || (given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG) { - const char *val = NULL; - - if (git_config_get_string(&val, cfg, "diff.renames") < 0) - giterr_clear(); - else if (val) { - int boolval; - if (!git__parse_bool(&boolval, val) && !boolval) { - /* do nothing */ - } else if (!strcasecmp(val, "copies") || !strcasecmp(val, "copy")) - opts->flags |= (GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES); - else - opts->flags |= GIT_DIFF_FIND_RENAMES; - } + const char *rule = + git_config__get_string_force(cfg, "diff.renames", "true"); + int boolval; + + if (!git__parse_bool(&boolval, rule) && !boolval) + /* don't set FIND_RENAMES if bool value is false */; + else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy")) + opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES; + else + opts->flags |= GIT_DIFF_FIND_RENAMES; } /* some flags imply others */ @@ -343,14 +341,11 @@ static int normalize_find_opts( #undef USE_DEFAULT if (!opts->rename_limit) { - int32_t limit = 0; - - opts->rename_limit = DEFAULT_RENAME_LIMIT; + opts->rename_limit = git_config__get_int_force( + cfg, "diff.renamelimit", DEFAULT_RENAME_LIMIT); - if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0) - giterr_clear(); - else if (limit > 0) - opts->rename_limit = limit; + if (opts->rename_limit <= 0) + opts->rename_limit = DEFAULT_RENAME_LIMIT; } /* assign the internal metric with whitespace flag as payload */ diff --git a/src/merge.c b/src/merge.c index 45387d4ad..e552b037b 100644 --- a/src/merge.c +++ b/src/merge.c @@ -26,6 +26,7 @@ #include "oid.h" #include "index.h" #include "filebuf.h" +#include "config.h" #include "git2/types.h" #include "git2/repository.h" @@ -1396,19 +1397,13 @@ static int merge_tree_normalize_opts( } if (!opts->target_limit) { - int32_t limit = 0; + int limit = git_config__get_int_force(cfg, "merge.renamelimit", 0); - opts->target_limit = GIT_MERGE_TREE_TARGET_LIMIT; + if (!limit) + limit = git_config__get_int_force(cfg, "diff.renamelimit", 0); - if (git_config_get_int32(&limit, cfg, "merge.renameLimit") < 0) { - giterr_clear(); - - if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0) - giterr_clear(); - } - - if (limit > 0) - opts->target_limit = limit; + opts->target_limit = (limit <= 0) ? + GIT_MERGE_TREE_TARGET_LIMIT : (unsigned int)limit; } /* assign the internal metric with whitespace flag as payload */ diff --git a/src/notes.c b/src/notes.c index beace1b50..d8ed32f82 100644 --- a/src/notes.c +++ b/src/notes.c @@ -378,20 +378,11 @@ cleanup: static int note_get_default_ref(const char **out, git_repository *repo) { - int ret; git_config *cfg; + int ret = git_repository_config__weakptr(&cfg, repo); - *out = NULL; - - if (git_repository_config__weakptr(&cfg, repo) < 0) - return -1; - - ret = git_config_get_string(out, cfg, "core.notesRef"); - if (ret == GIT_ENOTFOUND) { - giterr_clear(); - *out = GIT_NOTES_DEFAULT_REF; - return 0; - } + *out = (ret != 0) ? NULL : git_config__get_string_force( + cfg, "core.notesref", GIT_NOTES_DEFAULT_REF); return ret; } diff --git a/src/remote.c b/src/remote.c index 3d890a5f1..6f86a4b57 100644 --- a/src/remote.c +++ b/src/remote.c @@ -45,7 +45,7 @@ static int add_refspec(git_remote *remote, const char *string, bool is_fetch) static int download_tags_value(git_remote *remote, git_config *cfg) { - const char *val; + const git_config_entry *ce; git_buf buf = GIT_BUF_INIT; int error; @@ -53,16 +53,14 @@ static int download_tags_value(git_remote *remote, git_config *cfg) if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) return -1; - error = git_config_get_string(&val, cfg, git_buf_cstr(&buf)); + error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); git_buf_free(&buf); - if (!error && !strcmp(val, "--no-tags")) - remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; - else if (!error && !strcmp(val, "--tags")) - remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; - if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = 0; + if (!error && ce && ce->value) { + if (!strcmp(ce->value, "--no-tags")) + remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; + else if (!strcmp(ce->value, "--tags")) + remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; } return error; @@ -104,12 +102,7 @@ static int get_check_cert(int *out, git_repository *repo) if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) return error; - if ((error = git_config_get_bool(out, cfg, "http.sslVerify")) == 0) - return 0; - else if (error != GIT_ENOTFOUND) - return error; - - giterr_clear(); + *out = git_config__get_bool_force(cfg, "http.sslverify", 1); return 0; } @@ -493,7 +486,7 @@ int git_remote_save(const git_remote *remote) } if (error < 0) { git_buf_free(&buf); - return -1; + return error; } } @@ -667,7 +660,8 @@ int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url) { git_config *cfg; - const char *val; + const git_config_entry *ce; + const char *val = NULL; int error; assert(remote); @@ -684,44 +678,39 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur * to least specific. */ /* remote..proxy config setting */ - if (remote->name && 0 != *(remote->name)) { + if (remote->name && remote->name[0]) { git_buf buf = GIT_BUF_INIT; if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0) return error; - if ((error = git_config_get_string(&val, cfg, git_buf_cstr(&buf))) == 0 && - val && ('\0' != *val)) { - git_buf_free(&buf); + error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); + git_buf_free(&buf); - *proxy_url = git__strdup(val); - GITERR_CHECK_ALLOC(*proxy_url); - return 0; - } else if (error != GIT_ENOTFOUND) + if (error < 0) return error; - giterr_clear(); - git_buf_free(&buf); + if (ce && ce->value) { + val = ce->value; + goto found; + } } /* http.proxy config setting */ - if ((error = git_config_get_string(&val, cfg, "http.proxy")) == 0 && - val && ('\0' != *val)) { - *proxy_url = git__strdup(val); - GITERR_CHECK_ALLOC(*proxy_url); - return 0; - } else if (error != GIT_ENOTFOUND) + if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0) return error; - - giterr_clear(); + if (ce && ce->value) { + val = ce->value; + goto found; + } /* HTTP_PROXY / HTTPS_PROXY environment variables */ val = use_ssl ? getenv("HTTPS_PROXY") : getenv("HTTP_PROXY"); - if (val && ('\0' != *val)) { +found: + if (val && val[0]) { *proxy_url = git__strdup(val); GITERR_CHECK_ALLOC(*proxy_url); - return 0; } return 0; diff --git a/src/repository.c b/src/repository.c index 278c0384e..443744504 100644 --- a/src/repository.c +++ b/src/repository.c @@ -186,39 +186,37 @@ 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; + const git_config_entry *ce; + git_buf worktree = GIT_BUF_INIT; if (repo->is_bare) return 0; - if (git_repository_config__weakptr(&config, repo) < 0) - return -1; + if ((error = git_repository_config__weakptr(&config, repo)) < 0) + return error; - error = git_config_get_string(&worktree, config, "core.worktree"); - if (!error && worktree != NULL) { - error = git_path_prettify_dir( - &worktree_buf, worktree, repo->path_repository); - if (error < 0) + if ((error = git_config__lookup_entry( + &ce, config, "core.worktree", false)) < 0) + return error; + + if (ce && ce->value) { + if ((error = git_path_prettify_dir( + &worktree, ce->value, repo->path_repository)) < 0) return error; - repo->workdir = git_buf_detach(&worktree_buf); + + repo->workdir = git_buf_detach(&worktree); } - else if (error != GIT_ENOTFOUND) - return error; + else if (parent_path && git_path_isdir(parent_path->ptr)) + repo->workdir = git_buf_detach(parent_path); else { - giterr_clear(); + if (git_path_dirname_r(&worktree, repo->path_repository) < 0 || + git_path_to_dir(&worktree) < 0) + return -1; - 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); - } + repo->workdir = git_buf_detach(&worktree); } GITERR_CHECK_ALLOC(repo->workdir); - return 0; } diff --git a/src/submodule.c b/src/submodule.c index 586494fed..1e3d07911 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -1468,7 +1468,7 @@ static int submodule_update_config( int error; git_config *config; git_buf key = GIT_BUF_INIT; - const char *old = NULL; + const git_config_entry *ce = NULL; assert(submodule); @@ -1480,14 +1480,16 @@ static int submodule_update_config( if (error < 0) goto cleanup; - if (git_config_get_string(&old, config, key.ptr) < 0) - giterr_clear(); + if ((error = git_config__lookup_entry(&ce, config, key.ptr, false)) < 0) + goto cleanup; - if (!old && only_existing) + if (!ce && only_existing) + goto cleanup; + if (ce && !overwrite) goto cleanup; - if (old && !overwrite) + if (value && ce && ce->value && !strcmp(ce->value, value)) goto cleanup; - if ((!old && !value) || (old && value && strcmp(old, value) == 0)) + if (!value && (!ce || !ce->value)) goto cleanup; if (!value) diff --git a/src/transports/local.c b/src/transports/local.c index 4502f0202..f09e797ce 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -250,8 +250,9 @@ static int local_negotiate_fetch( git_oid_cpy(&rhead->loid, git_object_id(obj)); else if (error != GIT_ENOTFOUND) return error; + else + giterr_clear(); git_object_free(obj); - giterr_clear(); } return 0; -- cgit v1.2.1 From 96869a4edb2872934e0e167a726ab240f4270fea Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 3 Dec 2013 16:45:39 -0800 Subject: Improve GIT_EUSER handling This adds giterr_user_cancel to return GIT_EUSER and clear any error message that is sitting around. As a result of using that in places, we need to be more thorough with capturing errors that happen inside a callback when used internally. To help with that, this also adds giterr_capture and giterr_restore so that when we internally use a foreach-type function that clears errors and converts them to GIT_EUSER, it is easier to restore not just the return value, but the actual error message text. --- include/git2/errors.h | 3 +- src/attr.c | 3 +- src/blame.c | 24 +++++---- src/blame.h | 2 +- src/branch.c | 41 ++++++++------- src/checkout.c | 17 +++--- src/common.h | 42 ++++++++++----- src/config.c | 29 +++++------ src/diff.c | 23 ++++---- src/diff_patch.c | 68 ++++++++++++------------ src/diff_print.c | 46 +++++++++------- src/diff_xdiff.c | 25 ++++----- src/errors.c | 45 ++++++++++++++-- src/fileops.c | 75 +++++++++++++-------------- src/iterator.c | 12 ++--- src/odb_loose.c | 20 ++++--- src/odb_pack.c | 58 +++++++++++++-------- src/path.c | 11 ++-- src/pool.c | 9 +++- src/refdb_fs.c | 27 +++++++--- src/refs.c | 2 +- src/remote.c | 124 +++++++++++++++++++++++--------------------- src/status.c | 32 +++++++++--- src/submodule.c | 122 ++++++++++++++++++++++++------------------- tests/config/rename.c | 82 +++++++++++++++++++++++++++++ tests/config/validkeyname.c | 20 ------- tests/config/write.c | 1 + tests/refs/branches/move.c | 44 +++++++++++++++- 28 files changed, 624 insertions(+), 383 deletions(-) create mode 100644 tests/config/rename.c diff --git a/include/git2/errors.h b/include/git2/errors.h index f1a8ea1ae..c6076f3ab 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -8,7 +8,6 @@ #define INCLUDE_git_errors_h__ #include "common.h" -#include "buffer.h" /** * @file git2/errors.h @@ -91,7 +90,7 @@ GIT_EXTERN(void) giterr_clear(void); * Get the last error data and clear it. * * This copies the last error into the given `git_error` struct - * and returns 0 if the copy was successful, leaving the error + * and returns 0 if the copy was successful, leaving the error * cleared as if `giterr_clear` had been called. * * If there was no existing error in the library, -1 will be returned diff --git a/src/attr.c b/src/attr.c index 51895b7ac..2fccf21f8 100644 --- a/src/attr.c +++ b/src/attr.c @@ -193,8 +193,7 @@ int git_attr_foreach( error = callback(assign->name, assign->value, payload); if (error) { - giterr_clear(); - error = GIT_EUSER; + error = giterr_user_cancel(); goto cleanup; } } diff --git a/src/blame.c b/src/blame.c index ea9f77af5..f732338e6 100644 --- a/src/blame.c +++ b/src/blame.c @@ -108,17 +108,23 @@ git_blame* git_blame__alloc( git_blame_options opts, const char *path) { - git_blame *gbr = (git_blame*)git__calloc(1, sizeof(git_blame)); - if (!gbr) { - giterr_set_oom(); + git_blame *gbr = git__calloc(1, sizeof(git_blame)); + if (!gbr) return NULL; - } - git_vector_init(&gbr->hunks, 8, hunk_cmp); - git_vector_init(&gbr->paths, 8, paths_cmp); + gbr->repository = repo; gbr->options = opts; - gbr->path = git__strdup(path); - git_vector_insert(&gbr->paths, git__strdup(path)); + + if (git_vector_init(&gbr->hunks, 8, hunk_cmp) < 0 || + git_vector_init(&gbr->paths, 8, paths_cmp) < 0 || + (gbr->path = git__strdup(path)) == NULL || + git_vector_insert(&gbr->paths, git__strdup(path)) < 0) + { + git_blame_free(gbr); + git__free(gbr); + return NULL; + } + return gbr; } @@ -140,7 +146,7 @@ void git_blame_free(git_blame *blame) git_array_clear(blame->line_index); - git__free((void*)blame->path); + git__free(blame->path); git_blob_free(blame->final_blob); git__free(blame); } diff --git a/src/blame.h b/src/blame.h index 637e43985..7e23de808 100644 --- a/src/blame.h +++ b/src/blame.h @@ -64,7 +64,7 @@ typedef struct git_blame__entry { } git_blame__entry; struct git_blame { - const char *path; + char *path; git_repository *repository; git_blame_options options; diff --git a/src/branch.c b/src/branch.c index 95b3fd980..ef71c2cd1 100644 --- a/src/branch.c +++ b/src/branch.c @@ -90,29 +90,28 @@ int git_branch_delete(git_reference *branch) assert(branch); - if (!git_reference_is_branch(branch) && - !git_reference_is_remote(branch)) { - giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.", git_reference_name(branch)); - return -1; + if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) { + giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.", + git_reference_name(branch)); + return GIT_ENOTFOUND; } if ((is_head = git_branch_is_head(branch)) < 0) return is_head; if (is_head) { - giterr_set(GITERR_REFERENCE, - "Cannot delete branch '%s' as it is the current HEAD of the repository.", git_reference_name(branch)); + giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is " + "the current HEAD of the repository.", git_reference_name(branch)); return -1; } - if (git_buf_printf(&config_section, "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0) + if (git_buf_join(&config_section, '.', "branch", + git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0) goto on_error; if (git_config_rename_section( - git_reference_owner(branch), - git_buf_cstr(&config_section), - NULL) < 0) - goto on_error; + git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0) + goto on_error; if (git_reference_delete(branch) < 0) goto on_error; @@ -206,17 +205,21 @@ int git_branch_move( if (error < 0) goto done; - git_buf_printf(&old_config_section, - "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)); - - git_buf_printf(&new_config_section, "branch.%s", new_branch_name); + /* first update ref then config so failure won't trash config */ - if ((error = git_config_rename_section(git_reference_owner(branch), - git_buf_cstr(&old_config_section), - git_buf_cstr(&new_config_section))) < 0) + error = git_reference_rename( + out, branch, git_buf_cstr(&new_reference_name), force); + if (error < 0) goto done; - error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force); + git_buf_join(&old_config_section, '.', "branch", + git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)); + git_buf_join(&new_config_section, '.', "branch", new_branch_name); + + error = git_config_rename_section( + git_reference_owner(branch), + git_buf_cstr(&old_config_section), + git_buf_cstr(&new_config_section)); done: git_buf_free(&new_reference_name); diff --git a/src/checkout.c b/src/checkout.c index 6d7e3cfd4..4305d3e9a 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -216,7 +216,7 @@ static int checkout_action_common( if (notify != GIT_CHECKOUT_NOTIFY_NONE && checkout_notify(data, notify, delta, wd) != 0) - return GIT_EUSER; + return giterr_user_cancel(); return action; } @@ -230,7 +230,7 @@ static int checkout_action_no_wd( switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* case 12 */ if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL)) - return GIT_EUSER; + return giterr_user_cancel(); action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE); break; case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */ @@ -302,7 +302,7 @@ static int checkout_action_wd_only( } if (checkout_notify(data, notify, NULL, wd)) - return GIT_EUSER; + return giterr_user_cancel(); if (remove) { char *path = git_pool_strdup(&data->pool, wd->path); @@ -342,7 +342,7 @@ static int checkout_action_with_wd( if (checkout_is_workdir_modified(data, &delta->old_file, wd)) { if (checkout_notify( data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd)) - return GIT_EUSER; + return giterr_user_cancel(); action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE); } break; @@ -406,7 +406,7 @@ static int checkout_action_with_wd_blocker( case GIT_DELTA_UNMODIFIED: /* should show delta as dirty / deleted */ if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd)) - return GIT_EUSER; + return giterr_user_cancel(); action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE); break; case GIT_DELTA_ADDED: @@ -439,7 +439,7 @@ static int checkout_action_with_wd_dir( if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL) || checkout_notify( data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)) - return GIT_EUSER; + return giterr_user_cancel(); break; case GIT_DELTA_ADDED:/* case 4 (and 7 for dir) */ case GIT_DELTA_MODIFIED: /* case 20 (or 37 but not really) */ @@ -452,7 +452,7 @@ static int checkout_action_with_wd_dir( if (delta->old_file.mode != GIT_FILEMODE_TREE && checkout_notify( data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)) - return GIT_EUSER; + return giterr_user_cancel(); break; case GIT_DELTA_TYPECHANGE: /* case 24 or 31 */ if (delta->old_file.mode == GIT_FILEMODE_TREE) { @@ -1998,9 +1998,6 @@ int git_checkout_iterator( assert(data.completed_steps == data.total_steps); cleanup: - if (error == GIT_EUSER) - giterr_clear(); - if (!error && data.index != NULL && (data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0) error = git_index_write(data.index); diff --git a/src/common.h b/src/common.h index a1888785e..0ad4130aa 100644 --- a/src/common.h +++ b/src/common.h @@ -78,27 +78,41 @@ int giterr_set_regex(const regex_t *regex, int error_code); /** * Gets the system error code for this thread. */ -GIT_INLINE(int) giterr_system_last(void) -{ -#ifdef GIT_WIN32 - return GetLastError(); -#else - return errno; -#endif -} +int giterr_system_last(void); /** * Sets the system error code for this thread. */ -GIT_INLINE(void) giterr_system_set(int code) +void giterr_system_set(int code); + +/** + * Note that a user cancelled an operation with GIT_EUSER + */ +GIT_INLINE(int) giterr_user_cancel(void) { -#ifdef GIT_WIN32 - SetLastError(code); -#else - errno = code; -#endif + giterr_clear(); + return GIT_EUSER; } +/** + * Structure to preserve libgit2 error state + */ +typedef struct { + int error_code; + git_error error_msg; +} git_error_state; + +/** + * Capture current error state to restore later, returning error code. + * If `error_code` is zero, this does nothing and returns zero. + */ +int giterr_capture(git_error_state *state, int error_code); + +/** + * Restore error state to a previous value, returning saved error code. + */ +int giterr_restore(git_error_state *state); + /** * Check a versioned structure for validity */ diff --git a/src/config.c b/src/config.c index 227adbc9b..3af9d58de 100644 --- a/src/config.c +++ b/src/config.c @@ -501,20 +501,18 @@ int git_config_backend_foreach_match( return -1; } - while(!(iter->next(&entry, iter) < 0)) { + while (!(iter->next(&entry, iter) < 0)) { /* skip non-matching keys if regexp was provided */ if (regexp && regexec(®ex, entry->name, 0, NULL, 0) != 0) continue; /* abort iterator on non-zero return value */ if (fn(entry, data)) { - giterr_clear(); - result = GIT_EUSER; - goto cleanup; + result = giterr_user_cancel(); + break; } } -cleanup: if (regexp != NULL) regfree(®ex); @@ -537,9 +535,8 @@ int git_config_foreach_match( return error; while ((error = git_config_next(&entry, iter)) == 0) { - if(cb(entry, payload)) { - giterr_clear(); - error = GIT_EUSER; + if (cb(entry, payload)) { + error = giterr_user_cancel(); break; } } @@ -800,9 +797,10 @@ int git_config_get_multivar_foreach( found = 0; while ((err = iter->next(&entry, iter)) == 0) { found = 1; - if(cb(entry, payload)) { + + if (cb(entry, payload)) { iter->free(iter); - return GIT_EUSER; + return giterr_user_cancel(); } } @@ -1214,7 +1212,7 @@ struct rename_data { git_config *config; git_buf *name; size_t old_len; - int actual_error; + git_error_state error; }; static int rename_config_entries_cb( @@ -1237,9 +1235,8 @@ static int rename_config_entries_cb( if (!error) error = git_config_delete_entry(data->config, entry->name); - data->actual_error = error; /* preserve actual error code */ - - return error; + /* capture error message as needed, since it will become EUSER */ + return giterr_capture(&data->error, error); } int git_config_rename_section( @@ -1260,10 +1257,10 @@ int git_config_rename_section( if ((error = git_repository_config__weakptr(&config, repo)) < 0) goto cleanup; + memset(&data, 0, sizeof(data)); data.config = config; data.name = &replace; data.old_len = strlen(old_section_name) + 1; - data.actual_error = 0; if ((error = git_buf_join(&replace, '.', new_section_name, "")) < 0) goto cleanup; @@ -1281,7 +1278,7 @@ int git_config_rename_section( config, git_buf_cstr(&pattern), rename_config_entries_cb, &data); if (error == GIT_EUSER) - error = data.actual_error; + error = giterr_restore(&data.error); cleanup: git_buf_free(&pattern); diff --git a/src/diff.c b/src/diff.c index 53a8f4638..ad058af61 100644 --- a/src/diff.c +++ b/src/diff.c @@ -120,7 +120,7 @@ static int diff_delta__from_one( return -1; } - return notify_res < 0 ? GIT_EUSER : 0; + return notify_res < 0 ? giterr_user_cancel() : 0; } static int diff_delta__from_two( @@ -182,7 +182,7 @@ static int diff_delta__from_two( return -1; } - return notify_res < 0 ? GIT_EUSER : 0; + return notify_res < 0 ? giterr_user_cancel() : 0; } static git_diff_delta *diff_delta__last_for_item( @@ -1343,7 +1343,7 @@ int git_diff__paired_foreach( int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload), void *payload) { - int cmp; + int cmp, error = 0; git_diff_delta *h2i, *i2w; size_t i, j, i_max, j_max; int (*strcomp)(const char *, const char *) = git__strcmp; @@ -1399,18 +1399,17 @@ int git_diff__paired_foreach( strcomp(h2i->new_file.path, i2w->old_file.path); if (cmp < 0) { - if (cb(h2i, NULL, payload)) - return GIT_EUSER; - i++; + i++; i2w = NULL; } else if (cmp > 0) { - if (cb(NULL, i2w, payload)) - return GIT_EUSER; - j++; + j++; h2i = NULL; } else { - if (cb(h2i, i2w, payload)) - return GIT_EUSER; i++; j++; } + + if (cb(h2i, i2w, payload)) { + error = giterr_user_cancel(); + break; + } } /* restore case-insensitive delta sort */ @@ -1426,5 +1425,5 @@ int git_diff__paired_foreach( git_vector_sort(&idx2wd->deltas); } - return 0; + return error; } diff --git a/src/diff_patch.c b/src/diff_patch.c index cc49d68eb..c0910558e 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -32,6 +32,7 @@ struct git_patch { git_array_t(git_diff_line) lines; size_t content_size, context_size, header_size; git_pool flattened; + git_error_state error; }; enum { @@ -193,21 +194,17 @@ cleanup: return error; } -static int diff_patch_file_callback( +static int diff_patch_invoke_file_callback( git_patch *patch, git_diff_output *output) { - float progress; - - if (!output->file_cb) - return 0; - - progress = patch->diff ? + float progress = patch->diff ? ((float)patch->delta_index / patch->diff->deltas.length) : 1.0f; - if (output->file_cb(patch->delta, progress, output->payload) != 0) - output->error = GIT_EUSER; + if (output->file_cb && + output->file_cb(patch->delta, progress, output->payload) != 0) + return giterr_user_cancel(); - return output->error; + return 0; } static int diff_patch_generate(git_patch *patch, git_diff_output *output) @@ -229,7 +226,7 @@ static int diff_patch_generate(git_patch *patch, git_diff_output *output) return 0; if (output->diff_cb != NULL && - !(error = output->diff_cb(output, patch))) + (error = output->diff_cb(output, patch)) < 0) patch->flags |= GIT_DIFF_PATCH_DIFFED; return error; @@ -272,9 +269,10 @@ int git_diff_foreach( size_t idx; git_patch patch; - if (diff_required(diff, "git_diff_foreach") < 0) - return -1; + if ((error = diff_required(diff, "git_diff_foreach")) < 0) + return error; + memset(&xo, 0, sizeof(xo)); diff_output_init( &xo.output, &diff->opts, file_cb, hunk_cb, data_cb, payload); git_xdiff_init(&xo, &diff->opts); @@ -285,22 +283,18 @@ int git_diff_foreach( if (git_diff_delta__should_skip(&diff->opts, patch.delta)) continue; - if (!(error = diff_patch_init_from_diff(&patch, diff, idx))) { - - error = diff_patch_file_callback(&patch, &xo.output); + if ((error = diff_patch_init_from_diff(&patch, diff, idx)) < 0) + break; - if (!error) - error = diff_patch_generate(&patch, &xo.output); + if (!(error = diff_patch_invoke_file_callback(&patch, &xo.output))) + error = diff_patch_generate(&patch, &xo.output); - git_patch_free(&patch); - } + git_patch_free(&patch); if (error < 0) break; } - if (error == GIT_EUSER) - giterr_clear(); /* don't leave error message set invalidly */ return error; } @@ -332,7 +326,7 @@ static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo) !(patch->ofile.opts_flags & GIT_DIFF_INCLUDE_UNMODIFIED)) return error; - error = diff_patch_file_callback(patch, (git_diff_output *)xo); + error = diff_patch_invoke_file_callback(patch, (git_diff_output *)xo); if (!error) error = diff_patch_generate(patch, (git_diff_output *)xo); @@ -424,9 +418,7 @@ int git_diff_blobs( diff_patch_with_delta pd; git_xdiff_output xo; - memset(&pd, 0, sizeof(pd)); memset(&xo, 0, sizeof(xo)); - diff_output_init( &xo.output, opts, file_cb, hunk_cb, data_cb, payload); git_xdiff_init(&xo, opts); @@ -436,6 +428,7 @@ int git_diff_blobs( else if (!new_path && old_path) new_path = old_path; + memset(&pd, 0, sizeof(pd)); error = diff_patch_from_blobs( &pd, &xo, old_blob, old_path, new_blob, new_path, opts); @@ -463,13 +456,15 @@ int git_patch_from_blobs( return -1; memset(&xo, 0, sizeof(xo)); - diff_output_to_patch(&xo.output, &pd->patch); git_xdiff_init(&xo, opts); error = diff_patch_from_blobs( pd, &xo, old_blob, old_path, new_blob, new_path, opts); + if (error == GIT_EUSER) + error = giterr_restore(&pd->patch.error); + if (!error) *out = (git_patch *)pd; else @@ -536,9 +531,7 @@ int git_diff_blob_to_buffer( diff_patch_with_delta pd; git_xdiff_output xo; - memset(&pd, 0, sizeof(pd)); memset(&xo, 0, sizeof(xo)); - diff_output_init( &xo.output, opts, file_cb, hunk_cb, data_cb, payload); git_xdiff_init(&xo, opts); @@ -548,6 +541,7 @@ int git_diff_blob_to_buffer( else if (!buf_path && old_path) buf_path = old_path; + memset(&pd, 0, sizeof(pd)); error = diff_patch_from_blob_and_buffer( &pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts); @@ -576,13 +570,15 @@ int git_patch_from_blob_and_buffer( return -1; memset(&xo, 0, sizeof(xo)); - diff_output_to_patch(&xo.output, &pd->patch); git_xdiff_init(&xo, opts); error = diff_patch_from_blob_and_buffer( pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts); + if (error == GIT_EUSER) + error = giterr_restore(&pd->patch.error); + if (!error) *out = (git_patch *)pd; else @@ -622,14 +618,18 @@ int git_patch_from_diff( if ((error = diff_patch_alloc_from_diff(&patch, diff, idx)) < 0) return error; + memset(&xo, 0, sizeof(xo)); diff_output_to_patch(&xo.output, patch); git_xdiff_init(&xo, &diff->opts); - error = diff_patch_file_callback(patch, &xo.output); + error = diff_patch_invoke_file_callback(patch, &xo.output); if (!error) error = diff_patch_generate(patch, &xo.output); + if (error == GIT_EUSER) + error = giterr_restore(&patch->error); + if (!error) { /* if cumulative diff size is < 0.5 total size, flatten the patch */ /* unload the file content */ @@ -879,7 +879,8 @@ static int diff_patch_hunk_cb( GIT_UNUSED(delta); hunk = git_array_alloc(patch->hunks); - GITERR_CHECK_ALLOC(hunk); + if (!hunk) + return giterr_capture(&patch->error, -1); memcpy(&hunk->hunk, hunk_, sizeof(hunk->hunk)); @@ -905,10 +906,11 @@ static int diff_patch_line_cb( GIT_UNUSED(hunk_); hunk = git_array_last(patch->hunks); - GITERR_CHECK_ALLOC(hunk); + assert(hunk); /* programmer error if no hunk is available */ line = git_array_alloc(patch->lines); - GITERR_CHECK_ALLOC(line); + if (!line) + return giterr_capture(&patch->error, -1); memcpy(line, line_, sizeof(*line)); diff --git a/src/diff_print.c b/src/diff_print.c index b04b11515..712402864 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -18,6 +18,7 @@ typedef struct { uint32_t flags; int oid_strlen; git_diff_line line; + git_error_state error; } diff_print_info; static int diff_print_info_init( @@ -33,6 +34,7 @@ static int diff_print_info_init( pi->print_cb = cb; pi->payload = payload; pi->buf = out; + memset(&pi->error, 0, sizeof(pi->error)); if (diff) pi->flags = diff->opts.flags; @@ -89,12 +91,6 @@ char git_diff_status_char(git_delta_t status) return code; } -static int callback_error(void) -{ - giterr_clear(); - return GIT_EUSER; -} - static int diff_print_one_name_only( const git_diff_delta *delta, float progress, void *data) { @@ -111,14 +107,14 @@ static int diff_print_one_name_only( if (git_buf_puts(out, delta->new_file.path) < 0 || git_buf_putc(out, '\n')) - return -1; + return giterr_capture(&pi->error, -1); pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); + return giterr_user_cancel(); return 0; } @@ -156,14 +152,14 @@ static int diff_print_one_name_status( git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path); if (git_buf_oom(out)) - return -1; + return giterr_capture(&pi->error, -1); pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); + return giterr_user_cancel(); return 0; } @@ -202,14 +198,14 @@ static int diff_print_one_raw( delta->old_file.path : delta->new_file.path); if (git_buf_oom(out)) - return -1; + return giterr_capture(&pi->error, -1); pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); + return giterr_user_cancel(); return 0; } @@ -315,14 +311,14 @@ static int diff_print_patch_file( if (git_diff_delta__format_file_header( pi->buf, delta, oldpfx, newpfx, pi->oid_strlen) < 0) - return -1; + return giterr_capture(&pi->error, -1); pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(pi->buf); pi->line.content_len = git_buf_len(pi->buf); if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); + return giterr_user_cancel(); if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) return 0; @@ -332,7 +328,7 @@ static int diff_print_patch_file( if (diff_delta_format_with_paths( pi->buf, delta, oldpfx, newpfx, "Binary files %s%s and %s%s differ\n") < 0) - return -1; + return giterr_capture(&pi->error, -1); pi->line.origin = GIT_DIFF_LINE_BINARY; pi->line.content = git_buf_cstr(pi->buf); @@ -340,7 +336,7 @@ static int diff_print_patch_file( pi->line.num_lines = 1; if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); + return giterr_user_cancel(); return 0; } @@ -360,7 +356,7 @@ static int diff_print_patch_hunk( pi->line.content_len = h->header_len; if (pi->print_cb(d, h, &pi->line, pi->payload)) - return callback_error(); + return giterr_user_cancel(); return 0; } @@ -377,7 +373,7 @@ static int diff_print_patch_line( return 0; if (pi->print_cb(delta, hunk, line, pi->payload)) - return callback_error(); + return giterr_user_cancel(); return 0; } @@ -421,9 +417,14 @@ int git_diff_print( if (!(error = diff_print_info_init( &pi, &buf, diff, format, print_cb, payload))) + { error = git_diff_foreach( diff, print_file, print_hunk, print_line, &pi); + if (error == GIT_EUSER && pi.error.error_code) + error = giterr_restore(&pi.error); + } + git_buf_free(&buf); return error; @@ -444,10 +445,15 @@ int git_patch_print( if (!(error = diff_print_info_init( &pi, &temp, git_patch__diff(patch), GIT_DIFF_FORMAT_PATCH, print_cb, payload))) + { error = git_patch__invoke_callbacks( patch, diff_print_patch_file, diff_print_patch_hunk, diff_print_patch_line, &pi); + if (error && error != GIT_EUSER) + error = giterr_restore(&pi.error); + } + git_buf_free(&temp); return error; @@ -483,8 +489,10 @@ int git_patch_to_str( /* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1, * meaning a memory allocation failure, so just map to -1... */ - if (error == GIT_EUSER) + if (error == GIT_EUSER) { + giterr_set_oom(); error = -1; + } *string = git_buf_detach(&output); diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c index e0bc11f7f..c6ca48882 100644 --- a/src/diff_xdiff.c +++ b/src/diff_xdiff.c @@ -28,25 +28,29 @@ static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header) { /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */ if (*header != '@') - return -1; + goto fail; if (git_xdiff_scan_int(&header, &hunk->old_start) < 0) - return -1; + goto fail; if (*header == ',') { if (git_xdiff_scan_int(&header, &hunk->old_lines) < 0) - return -1; + goto fail; } else hunk->old_lines = 1; if (git_xdiff_scan_int(&header, &hunk->new_start) < 0) - return -1; + goto fail; if (*header == ',') { if (git_xdiff_scan_int(&header, &hunk->new_lines) < 0) - return -1; + goto fail; } else hunk->new_lines = 1; if (hunk->old_start < 0 || hunk->new_start < 0) - return -1; + goto fail; return 0; + +fail: + giterr_set(GITERR_INVALID, "Malformed hunk header from xdiff"); + return -1; } typedef struct { @@ -123,7 +127,7 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) if (output->hunk_cb != NULL && output->hunk_cb(delta, &info->hunk, output->payload)) - output->error = GIT_EUSER; + return (output->error = giterr_user_cancel()); info->old_lineno = info->hunk.old_start; info->new_lineno = info->hunk.new_start; @@ -149,7 +153,7 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) if (!output->error && output->data_cb != NULL && output->data_cb(delta, &info->hunk, &line, output->payload)) - output->error = GIT_EUSER; + output->error = giterr_user_cancel(); } if (len == 3 && !output->error) { @@ -171,7 +175,7 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) if (!output->error && output->data_cb != NULL && output->data_cb(delta, &info->hunk, &line, output->payload)) - output->error = GIT_EUSER; + output->error = giterr_user_cancel(); } return output->error; @@ -219,11 +223,9 @@ void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts) xo->output.diff_cb = git_xdiff; - memset(&xo->config, 0, sizeof(xo->config)); xo->config.ctxlen = opts ? opts->context_lines : 3; xo->config.interhunkctxlen = opts ? opts->interhunk_lines : 0; - memset(&xo->params, 0, sizeof(xo->params)); if (flags & GIT_DIFF_IGNORE_WHITESPACE) xo->params.flags |= XDF_WHITESPACE_FLAGS; if (flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE) @@ -236,6 +238,5 @@ void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts) if (flags & GIT_DIFF_MINIMAL) xo->params.flags |= XDF_NEED_MINIMAL; - memset(&xo->callback, 0, sizeof(xo->callback)); xo->callback.outf = git_xdiff_cb; } diff --git a/src/errors.c b/src/errors.c index d04da4ca9..a0b085923 100644 --- a/src/errors.c +++ b/src/errors.c @@ -23,7 +23,8 @@ static void set_error(int error_class, char *string) { git_error *error = &GIT_GLOBAL->error_t; - git__free(error->message); + if (error->message != string) + git__free(error->message); error->message = string; error->klass = error_class; @@ -103,8 +104,10 @@ int giterr_set_regex(const regex_t *regex, int error_code) void giterr_clear(void) { - set_error(0, NULL); - GIT_GLOBAL->last_error = NULL; + if (GIT_GLOBAL->last_error != NULL) { + set_error(0, NULL); + GIT_GLOBAL->last_error = NULL; + } errno = 0; #ifdef GIT_WIN32 @@ -134,3 +137,39 @@ const git_error *giterr_last(void) { return GIT_GLOBAL->last_error; } + +int giterr_capture(git_error_state *state, int error_code) +{ + state->error_code = error_code; + if (error_code) + giterr_detach(&state->error_msg); + return error_code; +} + +int giterr_restore(git_error_state *state) +{ + if (state && state->error_code && state->error_msg.message) + set_error(state->error_msg.klass, state->error_msg.message); + else + giterr_clear(); + + return state ? state->error_code : 0; +} + +int giterr_system_last(void) +{ +#ifdef GIT_WIN32 + return GetLastError(); +#else + return errno; +#endif +} + +void giterr_system_set(int code) +{ +#ifdef GIT_WIN32 + SetLastError(code); +#else + errno = code; +#endif +} diff --git a/src/fileops.c b/src/fileops.c index 5763b370b..0418e9e52 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -403,8 +403,8 @@ typedef struct { const char *base; size_t baselen; uint32_t flags; - int error; int depth; + git_error_state error; } futils__rmdir_data; #define FUTILS_MAX_DEPTH 100 @@ -447,8 +447,8 @@ static int futils__rm_first_parent(git_buf *path, const char *ceiling) static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) { + int error = 0; futils__rmdir_data *data = opaque; - int error = data->error; struct stat st; if (data->depth > FUTILS_MAX_DEPTH) @@ -474,13 +474,16 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) data->depth++; error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data); - if (error < 0) - return (error == GIT_EUSER) ? data->error : error; + if (error == GIT_EUSER) + return error; data->depth--; + if (error < 0) + goto done; + if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0) - return data->error; + goto done; if ((error = p_rmdir(path->ptr)) < 0) { if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 && @@ -499,35 +502,31 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0) error = futils__error_cannot_rmdir(path->ptr, "still present"); - data->error = error; - return error; +done: + return giterr_capture(&data->error, error); } static int futils__rmdir_empty_parent(void *opaque, git_buf *path) { futils__rmdir_data *data = opaque; - int error; + int error = 0; if (git_buf_len(path) <= data->baselen) - return GIT_ITEROVER; - - error = p_rmdir(git_buf_cstr(path)); + return giterr_capture(&data->error, GIT_ITEROVER); - if (error) { + if (p_rmdir(git_buf_cstr(path)) < 0) { int en = errno; if (en == ENOENT || en == ENOTDIR) { - giterr_clear(); - error = 0; + /* do nothing */ } else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) { - giterr_clear(); error = GIT_ITEROVER; } else { error = git_path_set_error(errno, git_buf_cstr(path), "rmdir"); } } - return error; + return giterr_capture(&data->error, error); } int git_futils_rmdir_r( @@ -535,12 +534,13 @@ int git_futils_rmdir_r( { int error; git_buf fullpath = GIT_BUF_INIT; - futils__rmdir_data data = { 0 }; + futils__rmdir_data data; /* build path and find "root" where we should start calling mkdir */ if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0) return -1; + memset(&data, 0, sizeof(data)); data.base = base ? base : ""; data.baselen = base ? strlen(base) : 0; data.flags = flags; @@ -548,13 +548,14 @@ int git_futils_rmdir_r( error = futils__rmdir_recurs_foreach(&data, &fullpath); /* remove now-empty parents if requested */ - if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) { + if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) error = git_path_walk_up( &fullpath, base, futils__rmdir_empty_parent, &data); - if (error == GIT_ITEROVER) - error = 0; - } + if (error == GIT_EUSER) + error = giterr_restore(&data.error); + if (error == GIT_ITEROVER) + error = 0; git_buf_free(&fullpath); @@ -858,7 +859,7 @@ typedef struct { uint32_t flags; uint32_t mkdir_flags; mode_t dirmode; - int error; + git_error_state error; } cp_r_info; #define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10) @@ -896,23 +897,21 @@ static int _cp_r_callback(void *ref, git_buf *from) from->ptr[git_path_basename_offset(from)] == '.') return 0; - if (git_buf_joinpath( - &info->to, info->to_root, from->ptr + info->from_prefix) < 0) { - error = -1; - goto exit; - } + if ((error = git_buf_joinpath( + &info->to, info->to_root, from->ptr + info->from_prefix)) < 0) + goto done; if (!(error = git_path_lstat(info->to.ptr, &to_st))) exists = true; else if (error != GIT_ENOTFOUND) - goto exit; + goto done; else { giterr_clear(); error = 0; } if ((error = git_path_lstat(from->ptr, &from_st)) < 0) - goto exit; + goto done; if (S_ISDIR(from_st.st_mode)) { mode_t oldmode = info->dirmode; @@ -928,15 +927,14 @@ static int _cp_r_callback(void *ref, git_buf *from) /* recurse onto target directory */ if (!error && (!exists || S_ISDIR(to_st.st_mode))) { error = git_path_direach(from, 0, _cp_r_callback, info); - if (error == GIT_EUSER) - error = info->error; + return error; } if (oldmode != 0) info->dirmode = oldmode; - goto exit; + goto done; } if (exists) { @@ -947,7 +945,7 @@ static int _cp_r_callback(void *ref, git_buf *from) giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'", info->to.ptr); error = -1; - goto exit; + goto done; } } @@ -960,7 +958,7 @@ static int _cp_r_callback(void *ref, git_buf *from) /* Make container directory on demand if needed */ if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 && (error = _cp_r_mkdir(info, from)) < 0) - goto exit; + goto done; /* make symlink or regular file */ if (S_ISLNK(from_st.st_mode)) @@ -974,9 +972,8 @@ static int _cp_r_callback(void *ref, git_buf *from) error = git_futils_cp(from->ptr, info->to.ptr, usemode); } -exit: - info->error = error; - return error; +done: + return giterr_capture(&info->error, error); } int git_futils_cp_r( @@ -992,11 +989,11 @@ int git_futils_cp_r( if (git_buf_joinpath(&path, from, "") < 0) /* ensure trailing slash */ return -1; + memset(&info, 0, sizeof(info)); info.to_root = to; info.flags = flags; info.dirmode = dirmode; info.from_prefix = path.size; - info.error = 0; git_buf_init(&info.to, 0); /* precalculate mkdir flags */ @@ -1019,7 +1016,7 @@ int git_futils_cp_r( git_buf_free(&info.to); if (error == GIT_EUSER) - error = info.error; + error = giterr_restore(&info.error); return error; } diff --git a/src/iterator.c b/src/iterator.c index 8646399ab..c1292227c 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -991,9 +991,8 @@ static int fs_iterator__expand_dir(fs_iterator *fi) fi->base.start, fi->base.end, &ff->entries); if (error < 0) { - git_error last_error = {0}; - - giterr_detach(&last_error); + git_error_state last_error = { 0 }; + giterr_capture(&last_error, error); /* these callbacks may clear the error message */ fs_iterator__free_frame(ff); @@ -1001,12 +1000,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi) /* next time return value we skipped to */ fi->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS; - if (last_error.message) { - giterr_set_str(last_error.klass, last_error.message); - free(last_error.message); - } - - return error; + return giterr_restore(&last_error); } if (ff->entries.length == 0) { diff --git a/src/odb_loose.c b/src/odb_loose.c index ced272b33..ae772b425 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -696,7 +696,7 @@ struct foreach_state { size_t dir_len; git_odb_foreach_cb cb; void *data; - int cb_error; + git_error_state cb_error; }; GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr) @@ -735,10 +735,8 @@ static int foreach_object_dir_cb(void *_state, git_buf *path) if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0) return 0; - if (state->cb(&oid, state->data)) { - state->cb_error = GIT_EUSER; - return -1; - } + if (state->cb(&oid, state->data)) + return giterr_user_cancel(); return 0; } @@ -747,7 +745,9 @@ static int foreach_cb(void *_state, git_buf *path) { struct foreach_state *state = (struct foreach_state *) _state; - return git_path_direach(path, 0, foreach_object_dir_cb, state); + return giterr_capture( + &state->cb_error, + git_path_direach(path, 0, foreach_object_dir_cb, state)); } static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data) @@ -762,7 +762,8 @@ static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb objects_dir = backend->objects_dir; - git_buf_sets(&buf, objects_dir); + if (git_buf_sets(&buf, objects_dir) < 0) + return -1; git_path_to_dir(&buf); memset(&state, 0, sizeof(state)); @@ -772,9 +773,12 @@ static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb error = git_path_direach(&buf, 0, foreach_cb, &state); + if (error == GIT_EUSER) + error = giterr_restore(&state.cb_error); + git_buf_free(&buf); - return state.cb_error ? state.cb_error : error; + return error; } static int loose_backend__stream_fwrite(git_odb_stream *_stream, const git_oid *oid) diff --git a/src/odb_pack.c b/src/odb_pack.c index fd2ca0fd8..2c0319fb6 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -190,31 +190,45 @@ static int packfile_sort__cb(const void *a_, const void *b_) } +struct packfile_load_data { + struct pack_backend *backend; + git_error_state error; +}; static int packfile_load__cb(void *_data, git_buf *path) { - struct pack_backend *backend = (struct pack_backend *)_data; + struct packfile_load_data *data = _data; + struct pack_backend *backend = data->backend; struct git_pack_file *pack; + const char *path_str = git_buf_cstr(path); + size_t i, cmp_len = git_buf_len(path); int error; - size_t i; - if (git__suffixcmp(path->ptr, ".idx") != 0) + if (cmp_len <= strlen(".idx") || git__suffixcmp(path_str, ".idx") != 0) return 0; /* not an index */ + cmp_len -= strlen(".idx"); + for (i = 0; i < backend->packs.length; ++i) { struct git_pack_file *p = git_vector_get(&backend->packs, i); - if (memcmp(p->pack_name, git_buf_cstr(path), git_buf_len(path) - strlen(".idx")) == 0) + + if (memcmp(p->pack_name, path_str, cmp_len) == 0) return 0; } error = git_packfile_alloc(&pack, path->ptr); - if (error == GIT_ENOTFOUND) - /* ignore missing .pack file as git does */ + + /* ignore missing .pack file as git does */ + if (error == GIT_ENOTFOUND) { + giterr_clear(); return 0; - else if (error < 0) - return error; + } + + if (!error) + error = git_vector_insert(&backend->packs, pack); + + return giterr_capture(&data->error, error); - return git_vector_insert(&backend->packs, pack); } static int pack_entry_find_inner( @@ -314,32 +328,34 @@ static int pack_entry_find_prefix( * Implement the git_odb_backend API calls * ***********************************************************/ -static int pack_backend__refresh(git_odb_backend *_backend) +static int pack_backend__refresh(git_odb_backend *backend) { - struct pack_backend *backend = (struct pack_backend *)_backend; - + struct packfile_load_data data; int error; struct stat st; git_buf path = GIT_BUF_INIT; - if (backend->pack_folder == NULL) + memset(&data, 0, sizeof(data)); + data.backend = (struct pack_backend *)backend; + + if (data.backend->pack_folder == NULL) return 0; - if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode)) + if (p_stat(data.backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode)) return git_odb__error_notfound("failed to refresh packfiles", NULL); - git_buf_sets(&path, backend->pack_folder); + git_buf_sets(&path, data.backend->pack_folder); /* reload all packs */ - error = git_path_direach(&path, 0, packfile_load__cb, backend); + error = git_path_direach(&path, 0, packfile_load__cb, &data); - git_buf_free(&path); + if (error == GIT_EUSER) + error = giterr_restore(&data.error); - if (error < 0) - return -1; + git_buf_free(&path); + git_vector_sort(&data.backend->packs); - git_vector_sort(&backend->packs); - return 0; + return error; } static int pack_backend__read_header_internal( diff --git a/src/path.c b/src/path.c index 750dd3ef7..8be41c17e 100644 --- a/src/path.c +++ b/src/path.c @@ -434,7 +434,8 @@ int git_path_walk_up( iter.asize = path->asize; while (scan >= stop) { - error = cb(data, &iter); + if (cb(data, &iter)) + error = giterr_user_cancel(); iter.ptr[scan] = oldc; if (error < 0) break; @@ -528,7 +529,9 @@ bool git_path_is_empty_dir(const char *path) if (!git_path_isdir(path)) return false; - if (!(error = git_buf_sets(&dir, path))) + if ((error = git_buf_sets(&dir, path)) != 0) + giterr_clear(); + else error = git_path_direach(&dir, 0, path_found_entry, NULL); git_buf_free(&dir); @@ -867,7 +870,7 @@ int git_path_direach( if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0) break; #endif - + if ((error = git_buf_put(path, de_path, de_len)) < 0) break; @@ -876,7 +879,7 @@ int git_path_direach( git_buf_truncate(path, wd_len); /* restore path */ if (error) { - error = GIT_EUSER; + error = giterr_user_cancel(); break; } } diff --git a/src/pool.c b/src/pool.c index d484769e9..4796d0a81 100644 --- a/src/pool.c +++ b/src/pool.c @@ -217,7 +217,14 @@ char *git_pool_strdup(git_pool *pool, const char *str) char *git_pool_strdup_safe(git_pool *pool, const char *str) { - return str ? git_pool_strdup(pool, str) : NULL; + if (!str) + return NULL; + else { + char *result = git_pool_strdup(pool, str); + if (!result) + giterr_clear(); + return result; + } } char *git_pool_strcat(git_pool *pool, const char *a, const char *b) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 62d5c1047..938e02a78 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -264,9 +264,14 @@ done: return error; } -static int _dirent_loose_load(void *data, git_buf *full_path) +struct packed_loadloose_data { + refdb_fs_backend *backend; + git_error_state error; +}; + +static int _dirent_loose_load(void *data_, git_buf *full_path) { - refdb_fs_backend *backend = (refdb_fs_backend *)data; + struct packed_loadloose_data *data = data_; const char *file_path; if (git__suffixcmp(full_path->ptr, ".lock") == 0) @@ -274,11 +279,12 @@ static int _dirent_loose_load(void *data, git_buf *full_path) if (git_path_isdir(full_path->ptr)) return git_path_direach( - full_path, backend->direach_flags, _dirent_loose_load, backend); + full_path, data->backend->direach_flags, _dirent_loose_load, data); - file_path = full_path->ptr + strlen(backend->path); + file_path = full_path->ptr + strlen(data->backend->path); - return loose_lookup_to_packfile(backend, file_path); + return giterr_capture( + &data->error, loose_lookup_to_packfile(data->backend, file_path)); } /* @@ -291,21 +297,28 @@ static int packed_loadloose(refdb_fs_backend *backend) { int error; git_buf refs_path = GIT_BUF_INIT; + struct packed_loadloose_data data; if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0) return -1; + memset(&data, 0, sizeof(data)); + data.backend = backend; + /* * 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, backend->direach_flags, _dirent_loose_load, backend); + &refs_path, backend->direach_flags, _dirent_loose_load, &data); git_buf_free(&refs_path); - return (error == GIT_EUSER) ? -1 : error; + if (error == GIT_EUSER) + error = giterr_restore(&data.error); + + return error; } static int refdb_fs_backend__exists( diff --git a/src/refs.c b/src/refs.c index 472a79890..bae62158b 100644 --- a/src/refs.c +++ b/src/refs.c @@ -471,7 +471,7 @@ int git_reference_rename( if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force)) < 0) return error; - /* Update HEAD it was poiting to the reference being renamed. */ + /* Update HEAD it was pointing to the reference being renamed */ if (should_head_be_updated && (error = git_repository_set_head(ref->db->repo, new_name)) < 0) { giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference"); diff --git a/src/remote.c b/src/remote.c index 6f86a4b57..e4bebe1c6 100644 --- a/src/remote.c +++ b/src/remote.c @@ -251,13 +251,14 @@ int git_remote_create_inmemory(git_remote **out, git_repository *repo, const cha struct refspec_cb_data { git_remote *remote; int fetch; + git_error_state error; }; static int refspec_cb(const git_config_entry *entry, void *payload) { - const struct refspec_cb_data *data = (struct refspec_cb_data *)payload; - - return add_refspec(data->remote, entry->value, data->fetch); + struct refspec_cb_data *data = (struct refspec_cb_data *)payload; + return giterr_capture( + &data->error, add_refspec(data->remote, entry->value, data->fetch)); } static int get_optional_config( @@ -283,9 +284,6 @@ static int get_optional_config( error = 0; } - if (error < 0) - error = -1; - return error; } @@ -296,7 +294,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) const char *val; int error = 0; git_config *config; - struct refspec_cb_data data; + struct refspec_cb_data data = { NULL }; bool optional_setting_found = false, found; assert(out && repo && name); @@ -363,6 +361,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) data.remote = remote; data.fetch = true; + git_buf_clear(&buf); git_buf_printf(&buf, "remote.%s.fetch", name); @@ -388,6 +387,9 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) cleanup: git_buf_free(&buf); + if (error == GIT_EUSER) + error = giterr_restore(&data.error); + if (error < 0) git_remote_free(remote); @@ -1110,9 +1112,14 @@ void git_remote_free(git_remote *remote) git__free(remote); } +struct remote_list_data { + git_vector list; + git_error_state error; +}; + static int remote_list_cb(const git_config_entry *entry, void *payload) { - git_vector *list = payload; + struct remote_list_data *data = payload; const char *name = entry->name + strlen("remote."); size_t namelen = strlen(name); char *remote_name; @@ -1123,47 +1130,50 @@ static int remote_list_cb(const git_config_entry *entry, void *payload) remote_name = git__strndup(name, namelen - 4); /* strip ".url" */ else remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */ - GITERR_CHECK_ALLOC(remote_name); + if (!remote_name) + return giterr_capture(&data->error, -1); - return git_vector_insert(list, remote_name); + return giterr_capture( + &data->error, git_vector_insert(&data->list, remote_name)); } int git_remote_list(git_strarray *remotes_list, git_repository *repo) { - git_config *cfg; - git_vector list; int error; + git_config *cfg; + struct remote_list_data data; - if (git_repository_config__weakptr(&cfg, repo) < 0) - return -1; + if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) + return error; - if (git_vector_init(&list, 4, git__strcmp_cb) < 0) - return -1; + memset(&data, 0, sizeof(data)); + if ((error = git_vector_init(&data.list, 4, git__strcmp_cb)) < 0) + return error; error = git_config_foreach_match( - cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list); + cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &data); + + /* cb error is converted to GIT_EUSER by git_config_foreach */ + if (error == GIT_EUSER) + error = giterr_restore(&data.error); if (error < 0) { size_t i; char *elem; - git_vector_foreach(&list, i, elem) { + git_vector_foreach(&data.list, i, elem) { git__free(elem); } - git_vector_free(&list); - - /* cb error is converted to GIT_EUSER by git_config_foreach */ - if (error == GIT_EUSER) - error = -1; + git_vector_free(&data.list); return error; } - git_vector_uniq(&list, git__free); + git_vector_uniq(&data.list, git__free); - remotes_list->strings = (char **)list.contents; - remotes_list->count = list.length; + remotes_list->strings = (char **)data.list.contents; + remotes_list->count = data.list.length; return 0; } @@ -1250,11 +1260,11 @@ cleanup: return error; } -struct update_data -{ +struct update_data { git_config *config; const char *old_remote_name; const char *new_remote_name; + git_error_state error; }; static int update_config_entries_cb( @@ -1266,10 +1276,9 @@ static int update_config_entries_cb( if (strcmp(entry->value, data->old_remote_name)) return 0; - return git_config_set_string( - data->config, - entry->name, - data->new_remote_name); + return giterr_capture( + &data->error, git_config_set_string( + data->config, entry->name, data->new_remote_name)); } static int update_branch_remote_config_entry( @@ -1277,20 +1286,22 @@ static int update_branch_remote_config_entry( const char *old_name, const char *new_name) { - git_config *config; - struct update_data data; + int error; + struct update_data data = { NULL }; - if (git_repository_config__weakptr(&config, repo) < 0) - return -1; + if ((error = git_repository_config__weakptr(&data.config, repo)) < 0) + return error; - data.config = config; data.old_remote_name = old_name; data.new_remote_name = new_name; - return git_config_foreach_match( - config, - "branch\\..+\\.remote", - update_config_entries_cb, &data); + error = git_config_foreach_match( + data.config, "branch\\..+\\.remote", update_config_entries_cb, &data); + + if (error == GIT_EUSER) + error = giterr_restore(&data.error); + + return error; } static int rename_one_remote_reference( @@ -1298,18 +1309,20 @@ static int rename_one_remote_reference( const char *old_remote_name, const char *new_remote_name) { - int error = -1; + int error; git_buf new_name = GIT_BUF_INIT; - if (git_buf_printf( + error = git_buf_printf( &new_name, GIT_REFS_REMOTES_DIR "%s%s", new_remote_name, - reference->name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)) < 0) - return -1; + reference->name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)); - error = git_reference_rename(NULL, reference, git_buf_cstr(&new_name), 0); - git_reference_free(reference); + if (!error) { + error = git_reference_rename( + NULL, reference, git_buf_cstr(&new_name), 0); + git_reference_free(reference); + } git_buf_free(&new_name); return error; @@ -1320,12 +1333,12 @@ static int rename_remote_references( const char *old_name, const char *new_name) { - int error = -1; + int error; git_reference *ref; git_reference_iterator *iter; - if (git_reference_iterator_new(&iter, repo) < 0) - return -1; + if ((error = git_reference_iterator_new(&iter, repo)) < 0) + return error; while ((error = git_reference_next(&ref, iter)) == 0) { if (git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) { @@ -1333,18 +1346,13 @@ static int rename_remote_references( continue; } - if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) { - git_reference_iterator_free(iter); - return error; - } + if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) + break; } git_reference_iterator_free(iter); - if (error == GIT_ITEROVER) - return 0; - - return error; + return (error == GIT_ITEROVER) ? 0 : error; } static int rename_fetch_refspecs( diff --git a/src/status.c b/src/status.c index 07fdcb5c3..fb99fb4e4 100644 --- a/src/status.c +++ b/src/status.c @@ -152,25 +152,32 @@ static git_status_t status_compute( return st; } +struct status_data { + git_status_list *status; + git_error_state err; +}; + static int status_collect( git_diff_delta *head2idx, git_diff_delta *idx2wd, void *payload) { - git_status_list *status = payload; + struct status_data *data = payload; git_status_entry *status_entry; - if (!status_is_included(status, head2idx, idx2wd)) + if (!status_is_included(data->status, head2idx, idx2wd)) return 0; status_entry = git__malloc(sizeof(git_status_entry)); - GITERR_CHECK_ALLOC(status_entry); + if (!status_entry) + return giterr_capture(&data->err, -1); - status_entry->status = status_compute(status, head2idx, idx2wd); + status_entry->status = status_compute(data->status, head2idx, idx2wd); status_entry->head_to_index = head2idx; status_entry->index_to_workdir = idx2wd; - return git_vector_insert(&status->paired, status_entry); + return giterr_capture( + &data->err, git_vector_insert(&data->status->paired, status_entry)); } GIT_INLINE(int) status_entry_cmp_base( @@ -314,9 +321,18 @@ int git_status_list_new( goto done; } - if ((error = git_diff__paired_foreach( - status->head2idx, status->idx2wd, status_collect, status)) < 0) - goto done; + { + struct status_data data = { 0 }; + data.status = status; + + error = git_diff__paired_foreach( + status->head2idx, status->idx2wd, status_collect, &data); + + if (error == GIT_EUSER) + error = giterr_restore(&data.err); + if (error < 0) + goto done; + } if (flags & GIT_STATUS_OPT_SORT_CASE_SENSITIVELY) git_vector_set_cmp(&status->paired, status_entry_cmp); diff --git a/src/submodule.c b/src/submodule.c index 1e3d07911..15c87c0b4 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -71,6 +71,11 @@ __KHASH_IMPL( str, static kh_inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash); +struct submodule_callback_payload { + git_repository *repo; + git_error_state error; +}; + static int load_submodule_config(git_repository *repo); static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *); static int lookup_head_remote(git_buf *url, git_repository *repo); @@ -169,8 +174,7 @@ int git_submodule_foreach( } if (callback(sm, sm->name, payload)) { - giterr_clear(); - error = GIT_EUSER; + error = giterr_user_cancel(); break; } }); @@ -821,20 +825,21 @@ int git_submodule_reload(git_submodule *submodule) { int error = 0; git_config_backend *mods; + struct submodule_callback_payload p; assert(submodule); /* refresh index data */ - - if (submodule_update_index(submodule) < 0) - return -1; + if ((error = submodule_update_index(submodule)) < 0) + return error; /* refresh HEAD tree data */ - - if (submodule_update_head(submodule) < 0) - return -1; + if ((error = submodule_update_head(submodule)) < 0) + return error; /* refresh config data */ + memset(&p, 0, sizeof(p)); + p.repo = submodule->repo; mods = open_gitmodules(submodule->repo, false, NULL); if (mods != NULL) { @@ -846,23 +851,29 @@ int git_submodule_reload(git_submodule *submodule) if (git_buf_oom(&path)) error = -1; - else + else { error = git_config_file_foreach_match( - mods, path.ptr, submodule_load_from_config, submodule->repo); + mods, path.ptr, submodule_load_from_config, &p); + + if (error == GIT_EUSER) + error = giterr_restore(&p.error); + } git_buf_free(&path); git_config_file_free(mods); - } - if (error < 0) - return error; + if (error < 0) + return error; + } /* refresh wd data */ submodule->flags = submodule->flags & ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID); - error = submodule_load_from_wd_lite(submodule, submodule->path, NULL); + error = submodule_load_from_wd_lite(submodule, submodule->path, &p); + if (error) + error = giterr_restore(&p.error); return error; } @@ -1087,15 +1098,14 @@ int git_submodule_parse_update(git_submodule_update_t *out, const char *value) } static int submodule_load_from_config( - const git_config_entry *entry, void *data) + const git_config_entry *entry, void *payload) { - git_repository *repo = data; - git_strmap *smcfg = repo->submodules; + struct submodule_callback_payload *p = payload; + git_strmap *smcfg = p->repo->submodules; const char *namestart, *property, *alternate = NULL; - const char *key = entry->name, *value = entry->value; + const char *key = entry->name, *value = entry->value, *path; git_buf name = GIT_BUF_INIT; git_submodule *sm; - bool is_path; int error = 0; if (git__prefixcmp(key, "submodule.") != 0) @@ -1108,15 +1118,11 @@ static int submodule_load_from_config( return 0; property++; - is_path = (strcasecmp(property, "path") == 0); + path = !strcasecmp(property, "path") ? value : NULL; - if (git_buf_set(&name, namestart, property - namestart - 1) < 0) - return -1; - - if (submodule_get(&sm, repo, name.ptr, is_path ? value : NULL) < 0) { - git_buf_free(&name); - return -1; - } + if ((error = git_buf_set(&name, namestart, property - namestart - 1)) < 0 || + (error = submodule_get(&sm, p->repo, name.ptr, path)) < 0) + goto done; sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; @@ -1130,15 +1136,20 @@ static int submodule_load_from_config( if (strcmp(sm->name, name.ptr) != 0) { alternate = sm->name = git_buf_detach(&name); - } else if (is_path && value && strcmp(sm->path, value) != 0) { + } else if (path && strcmp(path, sm->path) != 0) { alternate = sm->path = git__strdup(value); - if (!sm->path) + if (!sm->path) { error = -1; + goto done; + } } + if (alternate) { void *old_sm = NULL; git_strmap_insert2(smcfg, alternate, sm, old_sm, error); + if (error < 0) + goto done; if (error >= 0) GIT_REFCOUNT_INC(sm); /* inserted under a new key */ @@ -1149,15 +1160,11 @@ static int submodule_load_from_config( } } - git_buf_free(&name); - if (error < 0) - return error; - /* TODO: Look up path in index and if it is present but not a GITLINK * then this should be deleted (at least to match git's behavior) */ - if (is_path) + if (path) return 0; /* copy other properties into submodule entry */ @@ -1165,41 +1172,47 @@ static int submodule_load_from_config( git__free(sm->url); sm->url = NULL; - if (value != NULL && (sm->url = git__strdup(value)) == NULL) - return -1; + if (value != NULL && (sm->url = git__strdup(value)) == NULL) { + error = -1; + goto done; + } } else if (strcasecmp(property, "update") == 0) { - if (git_submodule_parse_update(&sm->update, value) < 0) - return -1; + if ((error = git_submodule_parse_update(&sm->update, value)) < 0) + goto done; sm->update_default = sm->update; } else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) { - if (git__parse_bool(&sm->fetch_recurse, value) < 0) - return submodule_config_error("fetchRecurseSubmodules", value); + if (git__parse_bool(&sm->fetch_recurse, value) < 0) { + error = submodule_config_error("fetchRecurseSubmodules", value); + goto done; + } } else if (strcasecmp(property, "ignore") == 0) { - if (git_submodule_parse_ignore(&sm->ignore, value) < 0) - return -1; + if ((error = git_submodule_parse_ignore(&sm->ignore, value)) < 0) + goto done; sm->ignore_default = sm->ignore; } /* ignore other unknown submodule properties */ - return 0; +done: + git_buf_free(&name); + return giterr_capture(&p->error, error); } static int submodule_load_from_wd_lite( git_submodule *sm, const char *name, void *payload) { + struct submodule_callback_payload *p = payload; git_buf path = GIT_BUF_INIT; GIT_UNUSED(name); - GIT_UNUSED(payload); if (git_repository_is_bare(sm->repo)) return 0; if (git_buf_joinpath(&path, git_repository_workdir(sm->repo), sm->path) < 0) - return -1; + return giterr_capture(&p->error, -1); if (git_path_isdir(path.ptr)) sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED; @@ -1208,7 +1221,6 @@ static int submodule_load_from_wd_lite( sm->flags |= GIT_SUBMODULE_STATUS_IN_WD; git_buf_free(&path); - return 0; } @@ -1342,13 +1354,15 @@ static int load_submodule_config(git_repository *repo) { int error; git_oid gitmodules_oid; - git_buf path = GIT_BUF_INIT; git_config_backend *mods = NULL; + struct submodule_callback_payload p; if (repo->submodules) return 0; memset(&gitmodules_oid, 0, sizeof(gitmodules_oid)); + memset(&p, 0, sizeof(p)); + p.repo = repo; /* Submodule data is kept in a hashtable keyed by both name and path. * These are usually the same, but that is not guaranteed. @@ -1370,23 +1384,23 @@ static int load_submodule_config(git_repository *repo) /* add submodule information from .gitmodules */ - if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL) - error = git_config_file_foreach(mods, submodule_load_from_config, repo); - - if (error != 0) + if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL && + (error = git_config_file_foreach( + mods, submodule_load_from_config, &p)) < 0) goto cleanup; /* shallow scan submodules in work tree */ if (!git_repository_is_bare(repo)) - error = git_submodule_foreach(repo, submodule_load_from_wd_lite, NULL); + error = git_submodule_foreach(repo, submodule_load_from_wd_lite, &p); cleanup: - git_buf_free(&path); - if (mods != NULL) git_config_file_free(mods); + if (error == GIT_EUSER) + error = giterr_restore(&p.error); + if (error) git_submodule_config_free(repo); diff --git a/tests/config/rename.c b/tests/config/rename.c new file mode 100644 index 000000000..29ade7b00 --- /dev/null +++ b/tests/config/rename.c @@ -0,0 +1,82 @@ +#include "clar_libgit2.h" +#include "config.h" + +static git_repository *g_repo = NULL; +static git_config *g_config = NULL; + +void test_config_rename__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_repository_config(&g_config, g_repo)); +} + +void test_config_rename__cleanup(void) +{ + git_config_free(g_config); + g_config = NULL; + + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +void test_config_rename__can_rename(void) +{ + const git_config_entry *ce; + + cl_git_pass(git_config_get_entry( + &ce, g_config, "branch.track-local.remote")); + cl_assert_equal_s(".", ce->value); + + cl_git_fail(git_config_get_entry( + &ce, g_config, "branch.local-track.remote")); + + cl_git_pass(git_config_rename_section( + g_repo, "branch.track-local", "branch.local-track")); + + cl_git_pass(git_config_get_entry( + &ce, g_config, "branch.local-track.remote")); + cl_assert_equal_s(".", ce->value); + + cl_git_fail(git_config_get_entry( + &ce, g_config, "branch.track-local.remote")); +} + +void test_config_rename__prevent_overwrite(void) +{ + const git_config_entry *ce; + const git_error *err; + + cl_git_pass(git_config_set_string( + g_config, "branch.local-track.remote", "yellow")); + + cl_git_pass(git_config_get_entry( + &ce, g_config, "branch.local-track.remote")); + cl_assert_equal_s("yellow", ce->value); + + cl_git_pass(git_config_rename_section( + g_repo, "branch.track-local", "branch.local-track")); + + cl_git_pass(git_config_get_entry( + &ce, g_config, "branch.local-track.remote")); + cl_assert_equal_s(".", ce->value); + +// cl_assert((err = giterr_last()) != NULL); +// cl_assert(err->message != NULL); +} + +static void assert_invalid_config_section_name( + git_repository *repo, const char *name) +{ + cl_git_fail_with( + git_config_rename_section(repo, "branch.remoteless", name), + GIT_EINVALIDSPEC); +} + +void test_config_rename__require_a_valid_new_name(void) +{ + assert_invalid_config_section_name(g_repo, ""); + assert_invalid_config_section_name(g_repo, "bra\nch"); + assert_invalid_config_section_name(g_repo, "branc#"); + assert_invalid_config_section_name(g_repo, "bra\nch.duh"); + assert_invalid_config_section_name(g_repo, "branc#.duh"); +} diff --git a/tests/config/validkeyname.c b/tests/config/validkeyname.c index 33699737b..0ef4a9ae3 100644 --- a/tests/config/validkeyname.c +++ b/tests/config/validkeyname.c @@ -46,23 +46,3 @@ void test_config_validkeyname__accessing_requires_a_valid_name(void) assert_invalid_config_key_name("dif.dir\nstat.lines"); assert_invalid_config_key_name("dif.dirstat.li\nes"); } - -static void assert_invalid_config_section_name(git_repository *repo, const char *name) -{ - cl_git_fail_with(git_config_rename_section(repo, "branch.remoteless", name), GIT_EINVALIDSPEC); -} - -void test_config_validkeyname__renaming_a_section_requires_a_valid_name(void) -{ - git_repository *repo; - - cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); - - assert_invalid_config_section_name(repo, ""); - assert_invalid_config_section_name(repo, "bra\nch"); - assert_invalid_config_section_name(repo, "branc#"); - assert_invalid_config_section_name(repo, "bra\nch.duh"); - assert_invalid_config_section_name(repo, "branc#.duh"); - - git_repository_free(repo); -} diff --git a/tests/config/write.c b/tests/config/write.c index 15f750dc0..922d75557 100644 --- a/tests/config/write.c +++ b/tests/config/write.c @@ -303,3 +303,4 @@ void test_config_write__updating_a_locked_config_file_returns_ELOCKED(void) git_config_free(cfg); } + diff --git a/tests/refs/branches/move.c b/tests/refs/branches/move.c index ecf14e006..9d233de1a 100644 --- a/tests/refs/branches/move.c +++ b/tests/refs/branches/move.c @@ -66,12 +66,54 @@ void test_refs_branches_move__can_move_a_local_branch_to_a_partially_colliding_n void test_refs_branches_move__can_not_move_a_branch_if_its_destination_name_collide_with_an_existing_one(void) { git_reference *original_ref, *new_ref; + git_config *config; + const git_config_entry *ce; + char *original_remote, *original_merge; + + cl_git_pass(git_repository_config(&config, repo)); + + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.remote")); + original_remote = strdup(ce->value); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.merge")); + original_merge = strdup(ce->value); + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); - cl_assert_equal_i(GIT_EEXISTS, git_branch_move(&new_ref, original_ref, "master", 0)); + cl_assert_equal_i(GIT_EEXISTS, + git_branch_move(&new_ref, original_ref, "master", 0)); + + cl_assert(giterr_last()->message != NULL); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.remote")); + cl_assert_equal_s(original_remote, ce->value); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.merge")); + cl_assert_equal_s(original_merge, ce->value); + + + cl_assert_equal_i(GIT_EEXISTS, + git_branch_move(&new_ref, original_ref, "cannot-fetch", 0)); + + cl_assert(giterr_last()->message != NULL); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.remote")); + cl_assert_equal_s(original_remote, ce->value); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.merge")); + cl_assert_equal_s(original_merge, ce->value); + + git_reference_free(original_ref); + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/track-local")); + + cl_assert_equal_i(GIT_EEXISTS, + git_branch_move(&new_ref, original_ref, "master", 0)); + + cl_assert(giterr_last()->message != NULL); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.remote")); + cl_assert_equal_s(original_remote, ce->value); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.merge")); + cl_assert_equal_s(original_merge, ce->value); + free(original_remote); free(original_merge); git_reference_free(original_ref); + git_config_free(config); } void test_refs_branches_move__moving_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void) -- cgit v1.2.1 From dab89f9b6821b67dd07c8bd4dbb53e25a3e687c7 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 4 Dec 2013 21:22:57 -0800 Subject: Further EUSER and error propagation fixes This continues auditing all the places where GIT_EUSER is being returned and making sure to clear any existing error using the new giterr_user_cancel helper. As a result, places that relied on intercepting GIT_EUSER but having the old error preserved also needed to be cleaned up to correctly stash and then retrieve the actual error. Additionally, as I encountered places where error codes were not being propagated correctly, I tried to fix them up. A number of those fixes are included in the this commit as well. --- src/attr.c | 7 ++- src/clone.c | 120 +++++++++++++++++++++++-------------------------- src/fetchhead.c | 4 +- src/ignore.c | 21 +++++++-- src/index.c | 12 +++-- src/indexer.c | 6 +-- src/merge.c | 5 ++- src/notes.c | 2 +- src/pack-objects.c | 47 ++++++++++--------- src/pack.c | 6 +-- src/push.c | 2 +- src/refs.c | 27 +++++------ src/remote.c | 47 ++++++++++--------- src/revwalk.c | 21 +++++---- src/stash.c | 4 +- src/status.c | 3 +- src/tag.c | 42 +++++++++++++---- src/transports/git.c | 27 ++++++----- src/transports/smart.c | 3 +- src/tree.c | 13 ++---- tests/core/path.c | 46 +++++++++++++++++++ 21 files changed, 273 insertions(+), 192 deletions(-) diff --git a/src/attr.c b/src/attr.c index 2fccf21f8..1a0f1f97f 100644 --- a/src/attr.c +++ b/src/attr.c @@ -480,6 +480,7 @@ typedef struct { const char *workdir; git_index *index; git_vector *files; + git_error_state error; } attr_walk_up_info; int git_attr_cache__decide_sources( @@ -523,7 +524,7 @@ static int push_one_attr(void *ref, git_buf *path) info->repo, path->ptr, GIT_ATTR_FILE, src[i], git_attr_file__parse_buffer, NULL, info->files); - return error; + return giterr_capture(&info->error, error); } static int collect_attr_files( @@ -535,7 +536,7 @@ static int collect_attr_files( int error; git_buf dir = GIT_BUF_INIT; const char *workdir = git_repository_workdir(repo); - attr_walk_up_info info; + attr_walk_up_info info = { NULL }; if (git_attr_cache__init(repo) < 0 || git_vector_init(files, 4, NULL) < 0) @@ -569,6 +570,8 @@ static int collect_attr_files( info.files = files; error = git_path_walk_up(&dir, workdir, push_one_attr, &info); + if (error == GIT_EUSER) + error = giterr_restore(&info.error); if (error < 0) goto cleanup; diff --git a/src/clone.c b/src/clone.c index 23aacd478..415efabba 100644 --- a/src/clone.c +++ b/src/clone.c @@ -107,6 +107,7 @@ struct head_info { git_buf branchname; const git_refspec *refspec; bool found; + git_error_state error; }; static int reference_matches_remote_head( @@ -115,43 +116,38 @@ static int reference_matches_remote_head( { struct head_info *head_info = (struct head_info *)payload; git_oid oid; + int error; /* TODO: Should we guard against references * which name doesn't start with refs/heads/ ? */ - /* Stop looking if we've already found a match */ - if (head_info->found) + error = git_reference_name_to_id(&oid, head_info->repo, reference_name); + if (error == GIT_ENOTFOUND) { + /* If the reference doesn't exists, it obviously cannot match the + * expected oid. */ + giterr_clear(); return 0; - - if (git_reference_name_to_id( - &oid, - head_info->repo, - reference_name) < 0) { - /* If the reference doesn't exists, it obviously cannot match the expected oid. */ - giterr_clear(); - return 0; } - if (git_oid__cmp(&head_info->remote_head_oid, &oid) == 0) { + if (!error && !git_oid__cmp(&head_info->remote_head_oid, &oid)) { /* Determine the local reference name from the remote tracking one */ - if (git_refspec_transform_l( - &head_info->branchname, - head_info->refspec, - reference_name) < 0) - return -1; - - if (git_buf_len(&head_info->branchname) > 0) { - if (git_buf_sets( - &head_info->branchname, - git_buf_cstr(&head_info->branchname) + strlen(GIT_REFS_HEADS_DIR)) < 0) - return -1; + error = git_refspec_transform_l( + &head_info->branchname, head_info->refspec, reference_name); - head_info->found = 1; + if (!error && + git_buf_len(&head_info->branchname) > 0 && + !(error = git_buf_sets( + &head_info->branchname, + git_buf_cstr(&head_info->branchname) + + strlen(GIT_REFS_HEADS_DIR)))) + { + head_info->found = true; + error = GIT_ITEROVER; } } - return 0; + return giterr_capture(&head_info->error, error); } static int update_head_to_new_branch( @@ -160,16 +156,11 @@ static int update_head_to_new_branch( const char *name) { git_reference *tracking_branch = NULL; - int error; - - if ((error = create_tracking_branch( - &tracking_branch, - repo, - target, - name)) < 0) - return error; + int error = create_tracking_branch(&tracking_branch, repo, target, name); - error = git_repository_set_head(repo, git_reference_name(tracking_branch)); + if (!error) + error = git_repository_set_head( + repo, git_reference_name(tracking_branch)); git_reference_free(tracking_branch); @@ -178,34 +169,30 @@ static int update_head_to_new_branch( static int update_head_to_remote(git_repository *repo, git_remote *remote) { - int retcode = -1; + int error = 0; size_t refs_len; git_refspec dummy_spec; const git_remote_head *remote_head, **refs; struct head_info head_info; git_buf remote_master_name = GIT_BUF_INIT; - if (git_remote_ls(&refs, &refs_len, remote) < 0) - return -1; + if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0) + return error; /* Did we just clone an empty repository? */ - if (refs_len == 0) { + if (refs_len == 0) return setup_tracking_config( - repo, - "master", - GIT_REMOTE_ORIGIN, - GIT_REFS_HEADS_MASTER_FILE); - } + repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE); /* Get the remote's HEAD. This is always the first ref in the list. */ remote_head = refs[0]; assert(remote_head); + memset(&head_info, 0, sizeof(head_info)); git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid); - git_buf_init(&head_info.branchname, 16); head_info.repo = repo; - head_info.refspec = git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE); - head_info.found = 0; + head_info.refspec = + git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE); if (head_info.refspec == NULL) { memset(&dummy_spec, 0, sizeof(git_refspec)); @@ -213,50 +200,53 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote) } /* Determine the remote tracking reference name from the local master */ - if (git_refspec_transform_r( + if ((error = git_refspec_transform_r( &remote_master_name, head_info.refspec, - GIT_REFS_HEADS_MASTER_FILE) < 0) - return -1; + GIT_REFS_HEADS_MASTER_FILE)) < 0) + return error; /* Check to see if the remote HEAD points to the remote master */ - if (reference_matches_remote_head(git_buf_cstr(&remote_master_name), &head_info) < 0) - goto cleanup; + error = reference_matches_remote_head( + git_buf_cstr(&remote_master_name), &head_info); + + if (error < 0) { + error = giterr_restore(&head_info.error); + if (error < 0 && error != GIT_ITEROVER) + goto cleanup; + } if (head_info.found) { - retcode = update_head_to_new_branch( + error = update_head_to_new_branch( repo, &head_info.remote_head_oid, git_buf_cstr(&head_info.branchname)); - goto cleanup; } /* Not master. Check all the other refs. */ - if (git_reference_foreach_name( - repo, - reference_matches_remote_head, - &head_info) < 0) - goto cleanup; + error = git_reference_foreach_name( + repo, reference_matches_remote_head, &head_info); + + if (error == GIT_EUSER) + error = giterr_restore(&head_info.error); + if (error < 0 && error != GIT_ITEROVER) + goto cleanup; if (head_info.found) { - retcode = update_head_to_new_branch( + error = update_head_to_new_branch( repo, &head_info.remote_head_oid, git_buf_cstr(&head_info.branchname)); - - goto cleanup; } else { - retcode = git_repository_set_head_detached( - repo, - &head_info.remote_head_oid); - goto cleanup; + error = git_repository_set_head_detached( + repo, &head_info.remote_head_oid); } cleanup: git_buf_free(&remote_master_name); git_buf_free(&head_info.branchname); - return retcode; + return error; } static int update_head_to_branch( diff --git a/src/fetchhead.c b/src/fetchhead.c index 67089d13d..ee1492211 100644 --- a/src/fetchhead.c +++ b/src/fetchhead.c @@ -269,8 +269,8 @@ int git_repository_fetchhead_foreach(git_repository *repo, else ref_name = NULL; - if ((cb(ref_name, remote_url, &oid, is_merge, payload)) != 0) { - error = GIT_EUSER; + if (cb(ref_name, remote_url, &oid, is_merge, payload) != 0) { + error = giterr_user_cancel(); goto done; } } diff --git a/src/ignore.c b/src/ignore.c index 27d7c7ec4..aa53d409d 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -74,10 +74,20 @@ static int parse_ignore_file( #define push_ignore_file(R,IGN,S,B,F) \ git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(IGN),(S)) +struct ignores_walk_up_data { + git_ignores *ign; + git_error_state error; +}; + static int push_one_ignore(void *ref, git_buf *path) { - git_ignores *ign = (git_ignores *)ref; - return push_ignore_file(ign->repo, ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE); + struct ignores_walk_up_data *data = ref; + + return giterr_capture( + &data->error, + push_ignore_file( + data->ign->repo, data->ign, &data->ign->ign_path, + path->ptr, GIT_IGNORE_FILE) ); } static int get_internal_ignores(git_attr_file **ign, git_repository *repo) @@ -132,8 +142,13 @@ int git_ignore__for_path( /* load .gitignore up the path */ if (workdir != NULL) { + struct ignores_walk_up_data data = { ignores }; + error = git_path_walk_up( - &ignores->dir, workdir, push_one_ignore, ignores); + &ignores->dir, workdir, push_one_ignore, &data); + + if (error == GIT_EUSER) + error = giterr_restore(&data.error); if (error < 0) goto cleanup; } diff --git a/src/index.c b/src/index.c index 09e7b2346..d0d2cf187 100644 --- a/src/index.c +++ b/src/index.c @@ -2036,6 +2036,12 @@ int git_index_read_tree(git_index *index, const git_tree *tree) error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data); + if (error == GIT_EUSER) { + giterr_set_oom(); + git_vector_free(&entries); + return -1; + } + git_vector_sort(&entries); git_index_clear(index); @@ -2116,8 +2122,7 @@ int git_index_add_all( if (error > 0) /* return > 0 means skip this one */ continue; if (error < 0) { /* return < 0 means abort */ - giterr_clear(); - error = GIT_EUSER; + error = giterr_user_cancel(); break; } } @@ -2205,8 +2210,7 @@ static int index_apply_to_all( continue; } if (error < 0) { /* return < 0 means abort */ - giterr_clear(); - error = GIT_EUSER; + error = giterr_user_cancel(); break; } } diff --git a/src/indexer.c b/src/indexer.c index 852a04120..7312809bf 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -387,10 +387,8 @@ on_error: static int do_progress_callback(git_indexer *idx, git_transfer_progress *stats) { if (idx->progress_cb && - idx->progress_cb(stats, idx->progress_payload)) { - giterr_clear(); - return GIT_EUSER; - } + idx->progress_cb(stats, idx->progress_payload)) + return giterr_user_cancel(); return 0; } diff --git a/src/merge.c b/src/merge.c index e552b037b..0d89da5a0 100644 --- a/src/merge.c +++ b/src/merge.c @@ -254,7 +254,8 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l return 0; } -int git_repository_mergehead_foreach(git_repository *repo, +int git_repository_mergehead_foreach( + git_repository *repo, git_repository_mergehead_foreach_cb cb, void *payload) { @@ -287,7 +288,7 @@ int git_repository_mergehead_foreach(git_repository *repo, goto cleanup; if (cb(&oid, payload) != 0) { - error = GIT_EUSER; + error = giterr_user_cancel(); goto cleanup; } diff --git a/src/notes.c b/src/notes.c index d8ed32f82..7e8aecbae 100644 --- a/src/notes.c +++ b/src/notes.c @@ -584,7 +584,7 @@ int git_note_foreach( while (!(error = git_note_next(¬e_id, &annotated_id, iter))) { if (note_cb(¬e_id, &annotated_id, payload)) { - error = GIT_EUSER; + error = giterr_user_cancel(); break; } } diff --git a/src/pack-objects.c b/src/pack-objects.c index 2d62507f2..ac0615064 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -32,6 +32,7 @@ struct unpacked { struct tree_walk_context { git_packbuilder *pb; git_buf buf; + git_error_state error; }; struct pack_write_context { @@ -220,12 +221,15 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, if (pb->progress_cb) { double current_time = git__timer(); - if ((current_time - pb->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) { + double elapsed = current_time - pb->last_progress_report_time; + + if (elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) { pb->last_progress_report_time = current_time; - if (pb->progress_cb(GIT_PACKBUILDER_ADDING_OBJECTS, pb->nr_objects, 0, pb->progress_cb_payload)) { - giterr_clear(); - return GIT_EUSER; - } + + if (pb->progress_cb( + GIT_PACKBUILDER_ADDING_OBJECTS, + pb->nr_objects, 0, pb->progress_cb_payload)) + return giterr_user_cancel(); } } @@ -1284,21 +1288,22 @@ const git_oid *git_packbuilder_hash(git_packbuilder *pb) return &pb->pack_oid; } -static int cb_tree_walk(const char *root, const git_tree_entry *entry, void *payload) +static int cb_tree_walk( + const char *root, const git_tree_entry *entry, void *payload) { + int error; struct tree_walk_context *ctx = payload; /* A commit inside a tree represents a submodule commit and should be skipped. */ if (git_tree_entry_type(entry) == GIT_OBJ_COMMIT) return 0; - if (git_buf_sets(&ctx->buf, root) < 0 || - git_buf_puts(&ctx->buf, git_tree_entry_name(entry)) < 0) - return -1; + if (!(error = git_buf_sets(&ctx->buf, root)) && + !(error = git_buf_puts(&ctx->buf, git_tree_entry_name(entry)))) + error = git_packbuilder_insert( + ctx->pb, git_tree_entry_id(entry), git_buf_cstr(&ctx->buf)); - return git_packbuilder_insert(ctx->pb, - git_tree_entry_id(entry), - git_buf_cstr(&ctx->buf)); + return giterr_capture(&ctx->error, error); } int git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *oid) @@ -1318,22 +1323,20 @@ int git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *oid) int git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *oid) { - git_tree *tree; + int error; + git_tree *tree = NULL; struct tree_walk_context context = { pb, GIT_BUF_INIT }; - if (git_tree_lookup(&tree, pb->repo, oid) < 0 || - git_packbuilder_insert(pb, oid, NULL) < 0) - return -1; + if (!(error = git_tree_lookup(&tree, pb->repo, oid)) && + !(error = git_packbuilder_insert(pb, oid, NULL))) + error = git_tree_walk(tree, GIT_TREEWALK_PRE, cb_tree_walk, &context); - if (git_tree_walk(tree, GIT_TREEWALK_PRE, cb_tree_walk, &context) < 0) { - git_tree_free(tree); - git_buf_free(&context.buf); - return -1; - } + if (error == GIT_EUSER) + error = giterr_restore(&context.error); git_tree_free(tree); git_buf_free(&context.buf); - return 0; + return error; } uint32_t git_packbuilder_object_count(git_packbuilder *pb) diff --git a/src/pack.c b/src/pack.c index 644b2d465..f69fe85e8 100644 --- a/src/pack.c +++ b/src/pack.c @@ -1042,10 +1042,9 @@ int git_pack_foreach_entry( { const unsigned char *index = p->index_map.data, *current; uint32_t i; + int error = 0; if (index == NULL) { - int error; - if ((error = pack_index_open(p)) < 0) return error; @@ -1062,7 +1061,6 @@ int git_pack_foreach_entry( if (p->oids == NULL) { git_vector offsets, oids; - int error; if ((error = git_vector_init(&oids, p->num_objects, NULL))) return error; @@ -1090,7 +1088,7 @@ int git_pack_foreach_entry( for (i = 0; i < p->num_objects; i++) if (cb(p->oids[i], data)) - return GIT_EUSER; + return giterr_user_cancel(); return 0; } diff --git a/src/push.c b/src/push.c index 3c9d5bb35..e592249d9 100644 --- a/src/push.c +++ b/src/push.c @@ -663,7 +663,7 @@ int git_push_status_foreach(git_push *push, git_vector_foreach(&push->status, i, status) { if (cb(status->ref, status->msg, data) < 0) - return GIT_EUSER; + return giterr_user_cancel(); } return 0; diff --git a/src/refs.c b/src/refs.c index bae62158b..60ed9ffb1 100644 --- a/src/refs.c +++ b/src/refs.c @@ -513,20 +513,19 @@ int git_reference_foreach( git_reference *ref; int error; - if (git_reference_iterator_new(&iter, repo) < 0) - return -1; + if ((error = git_reference_iterator_new(&iter, repo)) < 0) + return error; while ((error = git_reference_next(&ref, iter)) == 0) { if (callback(ref, payload)) { - error = GIT_EUSER; - goto out; + error = giterr_user_cancel(); + break; } } if (error == GIT_ITEROVER) error = 0; -out: git_reference_iterator_free(iter); return error; } @@ -540,20 +539,19 @@ int git_reference_foreach_name( const char *refname; int error; - if (git_reference_iterator_new(&iter, repo) < 0) - return -1; + if ((error = git_reference_iterator_new(&iter, repo)) < 0) + return error; while ((error = git_reference_next_name(&refname, iter)) == 0) { if (callback(refname, payload)) { - error = GIT_EUSER; - goto out; + error = giterr_user_cancel(); + break; } } if (error == GIT_ITEROVER) error = 0; -out: git_reference_iterator_free(iter); return error; } @@ -568,20 +566,19 @@ int git_reference_foreach_glob( const char *refname; int error; - if (git_reference_iterator_glob_new(&iter, repo, glob) < 0) - return -1; + if ((error = git_reference_iterator_glob_new(&iter, repo, glob)) < 0) + return error; while ((error = git_reference_next_name(&refname, iter)) == 0) { if (callback(refname, payload)) { - error = GIT_EUSER; - goto out; + error = giterr_user_cancel(); + break; } } if (error == GIT_ITEROVER) error = 0; -out: git_reference_iterator_free(iter); return error; } diff --git a/src/remote.c b/src/remote.c index e4bebe1c6..e9d079db5 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1370,46 +1370,43 @@ static int rename_fetch_refspecs( if (git_buf_printf(&base, "+refs/heads/*:refs/remotes/%s/*", remote->name) < 0) goto cleanup; + if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0) + goto cleanup; + git_vector_foreach(&remote->refspecs, i, spec) { if (spec->push) continue; - /* Every refspec is a problem refspec for an in-memory remote */ - if (!remote->name) { - if (callback(spec->string, payload) < 0) { - error = GIT_EUSER; - goto cleanup; - } - - continue; - } + /* Every refspec is a problem refspec for an in-memory remote, OR */ + /* Does the dst part of the refspec follow the expected format? */ + if (!remote->name || + strcmp(git_buf_cstr(&base), spec->string)) { - /* Does the dst part of the refspec follow the extected standard format? */ - if (strcmp(git_buf_cstr(&base), spec->string)) { if (callback(spec->string, payload) < 0) { - error = GIT_EUSER; + error = giterr_user_cancel(); goto cleanup; } - continue; } /* If we do want to move it to the new section */ - if (git_buf_printf(&val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0) - goto cleanup; - if (git_buf_printf(&var, "remote.%s.fetch", new_name) < 0) - goto cleanup; + git_buf_clear(&val); + git_buf_clear(&var); - if (git_repository_config__weakptr(&config, remote->repo) < 0) + if (git_buf_printf( + &val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0 || + git_buf_printf(&var, "remote.%s.fetch", new_name) < 0) + { + error = -1; goto cleanup; + } - if (git_config_set_string(config, git_buf_cstr(&var), git_buf_cstr(&val)) < 0) + if ((error = git_config_set_string( + config, git_buf_cstr(&var), git_buf_cstr(&val))) < 0) goto cleanup; } - error = 0; - cleanup: git_buf_free(&base); git_buf_free(&var); @@ -1445,11 +1442,11 @@ int git_remote_rename( new_name, callback, payload)) < 0) - return error; + return error; remote->name = git__strdup(new_name); + GITERR_CHECK_ALLOC(remote->name); - if (!remote->name) return 0; return git_remote_save(remote); } @@ -1476,11 +1473,13 @@ int git_remote_rename( new_name, callback, payload)) < 0) - return error; + return error; } git__free(remote->name); + remote->name = git__strdup(new_name); + GITERR_CHECK_ALLOC(remote->name); return 0; } diff --git a/src/revwalk.c b/src/revwalk.c index 3dd14b419..e8c7f23ec 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -112,12 +112,13 @@ static int process_commit_parents(git_revwalk *walk, git_commit_list_node *commi static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting) { + int error; git_object *obj; git_otype type; git_commit_list_node *commit; - if (git_object_lookup(&obj, walk->repo, oid, GIT_OBJ_ANY) < 0) - return -1; + if ((error = git_object_lookup(&obj, walk->repo, oid, GIT_OBJ_ANY)) < 0) + return error; type = git_object_type(obj); git_object_free(obj); @@ -168,13 +169,15 @@ static int push_ref(git_revwalk *walk, const char *refname, int hide) struct push_cb_data { git_revwalk *walk; int hide; + git_error_state error; }; static int push_glob_cb(const char *refname, void *data_) { struct push_cb_data *data = (struct push_cb_data *)data_; - return push_ref(data->walk, refname, data->hide); + return giterr_capture( + &data->error, push_ref(data->walk, refname, data->hide) ); } static int push_glob(git_revwalk *walk, const char *glob, int hide) @@ -191,6 +194,8 @@ static int push_glob(git_revwalk *walk, const char *glob, int hide) git_buf_joinpath(&buf, GIT_REFS_DIR, glob); else git_buf_puts(&buf, glob); + if (git_buf_oom(&buf)) + return -1; /* If no '?', '*' or '[' exist, we append '/ *' to the glob */ wildcard = strcspn(glob, "?*["); @@ -199,12 +204,12 @@ static int push_glob(git_revwalk *walk, const char *glob, int hide) data.walk = walk; data.hide = hide; + memset(&data.error, 0, sizeof(data.error)); - if (git_buf_oom(&buf)) - error = -1; - else - error = git_reference_foreach_glob( - walk->repo, git_buf_cstr(&buf), push_glob_cb, &data); + error = git_reference_foreach_glob( + walk->repo, git_buf_cstr(&buf), push_glob_cb, &data); + if (error == GIT_EUSER) + error = giterr_restore(&data.error); git_buf_free(&buf); return error; diff --git a/src/stash.c b/src/stash.c index 083c2a4cd..06a7b9a33 100644 --- a/src/stash.c +++ b/src/stash.c @@ -586,8 +586,8 @@ int git_stash_foreach( git_reflog_entry_message(entry), git_reflog_entry_id_new(entry), payload)) { - error = GIT_EUSER; - break; + error = giterr_user_cancel(); + break; } } diff --git a/src/status.c b/src/status.c index fb99fb4e4..777b7964a 100644 --- a/src/status.c +++ b/src/status.c @@ -414,8 +414,7 @@ int git_status_foreach_ext( status_entry->index_to_workdir->old_file.path; if (cb(path, status_entry->status, payload) != 0) { - error = GIT_EUSER; - giterr_clear(); + error = giterr_user_cancel(); break; } } diff --git a/src/tag.c b/src/tag.c index 31a3c8b80..5d4e45e5d 100644 --- a/src/tag.c +++ b/src/tag.c @@ -414,24 +414,29 @@ typedef struct { git_repository *repo; git_tag_foreach_cb cb; void *cb_data; + git_error_state error; } tag_cb_data; static int tags_cb(const char *ref, void *data) { + int error; git_oid oid; tag_cb_data *d = (tag_cb_data *)data; if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0) return 0; /* no tag */ - if (git_reference_name_to_id(&oid, d->repo, ref) < 0) - return -1; + if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) { + if (d->cb(ref, &oid, d->cb_data)) + error = giterr_user_cancel(); + } - return d->cb(ref, &oid, d->cb_data); + return giterr_capture(&d->error, error); } int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data) { + int error; tag_cb_data data; assert(repo && cb); @@ -439,8 +444,14 @@ int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data) data.cb = cb; data.cb_data = cb_data; data.repo = repo; + memset(&data.error, 0, sizeof(data.error)); - return git_reference_foreach_name(repo, &tags_cb, &data); + error = git_reference_foreach_name(repo, &tags_cb, &data); + + if (error == GIT_EUSER) + error = giterr_restore(&data.error); + + return error; } typedef struct { @@ -455,8 +466,14 @@ static int tag_list_cb(const char *tag_name, git_oid *oid, void *data) tag_filter_data *filter = (tag_filter_data *)data; GIT_UNUSED(oid); - if (!*filter->pattern || p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0) - return git_vector_insert(filter->taglist, git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN)); + if (!*filter->pattern || + p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0) + { + char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN); + if (!matched) + return -1; + return git_vector_insert(filter->taglist, matched); + } return 0; } @@ -469,16 +486,23 @@ int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_reposit assert(tag_names && repo && pattern); - if (git_vector_init(&taglist, 8, NULL) < 0) - return -1; + if ((error = git_vector_init(&taglist, 8, NULL)) < 0) + return error; filter.taglist = &taglist; filter.pattern = pattern; error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter); + + /* the only case where callback will return an error is oom */ + if (error == GIT_EUSER) { + giterr_set_oom(); + error = -1; + } + if (error < 0) { git_vector_free(&taglist); - return -1; + return error; } tag_names->strings = (char **)taglist.contents; diff --git a/src/transports/git.c b/src/transports/git.c index 5dcd4eff7..21507c1c7 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -93,18 +93,19 @@ static int git_stream_read( size_t buf_size, size_t *bytes_read) { + int error; git_stream *s = (git_stream *)stream; gitno_buffer buf; *bytes_read = 0; - if (!s->sent_command && send_command(s) < 0) - return -1; + if (!s->sent_command && (error = send_command(s)) < 0) + return error; gitno_buffer_setup(&s->socket, &buf, buffer, buf_size); - if (gitno_recv(&buf) < 0) - return -1; + if ((error = gitno_recv(&buf)) < 0) + return error; *bytes_read = buf.offset; @@ -116,10 +117,11 @@ static int git_stream_write( const char *buffer, size_t len) { + int error; git_stream *s = (git_stream *)stream; - if (!s->sent_command && send_command(s) < 0) - return -1; + if (!s->sent_command && (error = send_command(s)) < 0) + return error; return gitno_send(&s->socket, buffer, len, 0); } @@ -140,7 +142,7 @@ static void git_stream_free(git_smart_subtransport_stream *stream) } git__free(s->url); - git__free(s); + git__free(s); } static int git_stream_alloc( @@ -182,18 +184,21 @@ static int _git_uploadpack_ls( char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; const char *stream_url = url; git_stream *s; - int error = -1; + int error; *stream = NULL; + if (!git__prefixcmp(url, prefix_git)) stream_url += strlen(prefix_git); - if (git_stream_alloc(t, stream_url, cmd_uploadpack, stream) < 0) - return -1; + if ((error = git_stream_alloc(t, stream_url, cmd_uploadpack, stream)) < 0) + return error; s = (git_stream *)*stream; - if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) { + if (!(error = gitno_extract_url_parts( + &host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) { + if (!(error = gitno_connect(&s->socket, host, port, 0))) t->current_stream = s; diff --git a/src/transports/smart.c b/src/transports/smart.c index 5242beb65..e298f3510 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -27,8 +27,7 @@ static int git_smart__recv_cb(gitno_buffer *buf) if (t->packetsize_cb(bytes_read, t->packetsize_payload)) { git_atomic_set(&t->cancelled, 1); - giterr_clear(); - return GIT_EUSER; + return giterr_user_cancel(); } return (int)(buf->offset - old_len); diff --git a/src/tree.c b/src/tree.c index bb59ff82b..8ded007eb 100644 --- a/src/tree.c +++ b/src/tree.c @@ -884,14 +884,12 @@ static int tree_walk( git_vector_foreach(&tree->entries, i, entry) { if (preorder) { error = callback(path->ptr, entry, payload); + if (error < 0) + return giterr_user_cancel(); if (error > 0) { error = 0; continue; } - if (error < 0) { - giterr_clear(); - return GIT_EUSER; - } } if (git_tree_entry__is_tree(entry)) { @@ -918,11 +916,8 @@ static int tree_walk( git_buf_truncate(path, path_len); } - if (!preorder && callback(path->ptr, entry, payload) < 0) { - giterr_clear(); - error = GIT_EUSER; - break; - } + if (!preorder && callback(path->ptr, entry, payload) < 0) + return giterr_user_cancel(); } return error; diff --git a/tests/core/path.c b/tests/core/path.c index cf2d5e944..3858e30dd 100644 --- a/tests/core/path.c +++ b/tests/core/path.c @@ -350,15 +350,24 @@ void test_core_path__10_fromurl(void) typedef struct { int expect_idx; + int cancel_after; char **expect; } check_walkup_info; static int check_one_walkup_step(void *ref, git_buf *path) { check_walkup_info *info = (check_walkup_info *)ref; + + if (!info->cancel_after) { + cl_assert_equal_s(info->expect[info->expect_idx], "[CANCEL]"); + return -1; + } + info->cancel_after--; + cl_assert(info->expect[info->expect_idx] != NULL); cl_assert_equal_s(info->expect[info->expect_idx], path->ptr); info->expect_idx++; + return 0; } @@ -381,6 +390,7 @@ void test_core_path__11_walkup(void) check_walkup_info info; info.expect = expect; + info.cancel_after = -1; for (i = 0, j = 0; expect[i] != NULL; i++, j++) { @@ -400,6 +410,42 @@ void test_core_path__11_walkup(void) git_buf_free(&p); } +void test_core_path__11a_walkup_cancel(void) +{ + git_buf p = GIT_BUF_INIT; + int cancel[] = { 3, 2, 1, 0 }; + char *expect[] = { + "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "[CANCEL]", NULL, + "/a/b/c/d/e", "/a/b/c/d/", "[CANCEL]", NULL, + "/a/b/c/d/e", "[CANCEL]", NULL, + "[CANCEL]", NULL, + NULL + }; + char *root[] = { NULL, NULL, "/", "", NULL }; + int i, j; + check_walkup_info info; + + info.expect = expect; + + for (i = 0, j = 0; expect[i] != NULL; i++, j++) { + + git_buf_sets(&p, expect[i]); + + info.cancel_after = cancel[j]; + info.expect_idx = i; + + cl_assert_equal_i( + GIT_EUSER, + git_path_walk_up(&p, root[j], check_one_walkup_step, &info) + ); + + /* skip to next run of expectations */ + while (expect[i] != NULL) i++; + } + + git_buf_free(&p); +} + void test_core_path__12_offset_to_path_root(void) { cl_assert(git_path_root("non/rooted/path") == -1); -- cgit v1.2.1 From fcd324c625d8be3f368c924d787e945e5812d8dd Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 6 Dec 2013 15:04:31 -0800 Subject: Add git_vector_free_all There are a lot of places that we call git__free on each item in a vector and then call git_vector_free on the vector itself. This just wraps that up into one convenient helper function. --- src/blame.c | 5 +---- src/checkout.c | 10 ++-------- src/diff.c | 9 +-------- src/diff_tform.c | 8 ++------ src/indexer.c | 20 +++++--------------- src/iterator.c | 7 +------ src/merge.c | 6 +----- src/pathspec.c | 10 +--------- src/push.c | 5 +---- src/remote.c | 14 ++------------ src/status.c | 8 +------- src/vector.c | 14 ++++++++++++++ src/vector.h | 1 + 13 files changed, 33 insertions(+), 84 deletions(-) diff --git a/src/blame.c b/src/blame.c index f732338e6..f10ed409a 100644 --- a/src/blame.c +++ b/src/blame.c @@ -132,7 +132,6 @@ void git_blame_free(git_blame *blame) { size_t i; git_blame_hunk *hunk; - char *path; if (!blame) return; @@ -140,9 +139,7 @@ void git_blame_free(git_blame *blame) free_hunk(hunk); git_vector_free(&blame->hunks); - git_vector_foreach(&blame->paths, i, path) - git__free(path); - git_vector_free(&blame->paths); + git_vector_free_all(&blame->paths); git_array_clear(blame->line_index); diff --git a/src/checkout.c b/src/checkout.c index 4305d3e9a..e33ac2ed6 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -846,7 +846,7 @@ static int checkout_conflicts_coalesce_renames( /* Juggle entries based on renames */ names = git_index_name_entrycount(data->index); - + for (i = 0; i < names; i++) { name_entry = git_index_name_get_byindex(data->index, i); @@ -1760,9 +1760,6 @@ static int checkout_create_conflicts(checkout_data *data) static void checkout_data_clear(checkout_data *data) { - checkout_conflictdata *conflict; - size_t i; - if (data->opts_free_baseline) { git_tree_free(data->opts.baseline); data->opts.baseline = NULL; @@ -1771,10 +1768,7 @@ static void checkout_data_clear(checkout_data *data) git_vector_free(&data->removes); git_pool_clear(&data->pool); - git_vector_foreach(&data->conflicts, i, conflict) - git__free(conflict); - - git_vector_free(&data->conflicts); + git_vector_free_all(&data->conflicts); git__free(data->pfx); data->pfx = NULL; diff --git a/src/diff.c b/src/diff.c index ad058af61..af47e86aa 100644 --- a/src/diff.c +++ b/src/diff.c @@ -483,14 +483,7 @@ static int diff_list_apply_options( static void diff_list_free(git_diff *diff) { - git_diff_delta *delta; - unsigned int i; - - git_vector_foreach(&diff->deltas, i, delta) { - git__free(delta); - diff->deltas.contents[i] = NULL; - } - git_vector_free(&diff->deltas); + git_vector_free_all(&diff->deltas); git_pathspec__vfree(&diff->pathspec); git_pool_clear(&diff->pool); diff --git a/src/diff_tform.c b/src/diff_tform.c index 2f94b2e77..16184910a 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -209,9 +209,7 @@ int git_diff_merge(git_diff *onto, const git_diff *from) git_pool_strdup_safe(&onto->pool, onto->opts.new_prefix); } - git_vector_foreach(&onto_new, i, delta) - git__free(delta); - git_vector_free(&onto_new); + git_vector_free_all(&onto_new); git_pool_clear(&onto_pool); return error; @@ -445,9 +443,7 @@ static int apply_splits_and_deletes( return 0; on_error: - git_vector_foreach(&onto, i, delta) - git__free(delta); - git_vector_free(&onto); + git_vector_free_all(&onto); return -1; } diff --git a/src/indexer.c b/src/indexer.c index 7312809bf..d3253cb57 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -1005,30 +1005,20 @@ on_error: void git_indexer_free(git_indexer *idx) { - khiter_t k; - unsigned int i; - struct entry *e; - struct delta_info *delta; - if (idx == NULL) return; - git_vector_foreach(&idx->objects, i, e) - git__free(e); - git_vector_free(&idx->objects); + git_vector_free_all(&idx->objects); if (idx->pack) { - for (k = kh_begin(idx->pack->idx_cache); k != kh_end(idx->pack->idx_cache); k++) { - if (kh_exist(idx->pack->idx_cache, k)) - git__free(kh_value(idx->pack->idx_cache, k)); - } + struct git_pack_entry *pentry; + kh_foreach_value( + idx->pack->idx_cache, pentry, { git__free(pentry); }); git_oidmap_free(idx->pack->idx_cache); } - git_vector_foreach(&idx->deltas, i, delta) - git__free(delta); - git_vector_free(&idx->deltas); + git_vector_free_all(&idx->deltas); git_packfile_free(idx->pack); git_filebuf_cleanup(&idx->pack_file); git__free(idx); diff --git a/src/iterator.c b/src/iterator.c index c1292227c..118bbb880 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -920,12 +920,7 @@ static fs_iterator_frame *fs_iterator__alloc_frame(fs_iterator *fi) static void fs_iterator__free_frame(fs_iterator_frame *ff) { - size_t i; - git_path_with_stat *path; - - git_vector_foreach(&ff->entries, i, path) - git__free(path); - git_vector_free(&ff->entries); + git_vector_free_all(&ff->entries); git__free(ff); } diff --git a/src/merge.c b/src/merge.c index 0d89da5a0..d5bc6a39c 100644 --- a/src/merge.c +++ b/src/merge.c @@ -2383,11 +2383,7 @@ done: git_index_set_caps(index_repo, index_repo_caps); git_index_free(index_repo); - - git_vector_foreach(&paths, i, path) - git__free(path); - - git_vector_free(&paths); + git_vector_free_all(&paths); return error; } diff --git a/src/pathspec.c b/src/pathspec.c index 1e7e65e90..f16e19f47 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -102,15 +102,7 @@ int git_pathspec__vinit( /* free data from the pathspec vector */ void git_pathspec__vfree(git_vector *vspec) { - git_attr_fnmatch *match; - unsigned int i; - - git_vector_foreach(vspec, i, match) { - git__free(match); - vspec->contents[i] = NULL; - } - - git_vector_free(vspec); + git_vector_free_all(vspec); } struct pathspec_match_context { diff --git a/src/push.c b/src/push.c index e592249d9..a314922c1 100644 --- a/src/push.c +++ b/src/push.c @@ -541,10 +541,7 @@ static int queue_objects(git_push *push) error = 0; on_error: - git_vector_foreach(&commits, i, oid) - git__free(oid); - - git_vector_free(&commits); + git_vector_free_all(&commits); return error; } diff --git a/src/remote.c b/src/remote.c index e9d079db5..93d243001 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1158,15 +1158,7 @@ int git_remote_list(git_strarray *remotes_list, git_repository *repo) error = giterr_restore(&data.error); if (error < 0) { - size_t i; - char *elem; - - git_vector_foreach(&data.list, i, elem) { - git__free(elem); - } - - git_vector_free(&data.list); - + git_vector_free_all(&data.list); return error; } @@ -1651,9 +1643,7 @@ static int copy_refspecs(git_strarray *array, git_remote *remote, unsigned int p return 0; on_error: - git_vector_foreach(&refspecs, i, dup) - git__free(dup); - git_vector_free(&refspecs); + git_vector_free_all(&refspecs); return -1; } diff --git a/src/status.c b/src/status.c index 777b7964a..ce571a2d8 100644 --- a/src/status.c +++ b/src/status.c @@ -376,19 +376,13 @@ const git_status_entry *git_status_byindex(git_status_list *status, size_t i) void git_status_list_free(git_status_list *status) { - git_status_entry *status_entry; - size_t i; - if (status == NULL) return; git_diff_free(status->head2idx); git_diff_free(status->idx2wd); - git_vector_foreach(&status->paired, i, status_entry) - git__free(status_entry); - - git_vector_free(&status->paired); + git_vector_free_all(&status->paired); git__memzero(status, sizeof(*status)); git__free(status); diff --git a/src/vector.c b/src/vector.c index 362e7b0c0..9e217b831 100644 --- a/src/vector.c +++ b/src/vector.c @@ -77,6 +77,20 @@ void git_vector_free(git_vector *v) v->_alloc_size = 0; } +void git_vector_free_all(git_vector *v) +{ + size_t i; + + assert(v); + + for (i = 0; i < v->length; ++i) { + git__free(v->contents[i]); + v->contents[i] = NULL; + } + + git_vector_free(v); +} + int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp) { assert(v); diff --git a/src/vector.h b/src/vector.h index 279f5c6ee..c6d3e9d55 100644 --- a/src/vector.h +++ b/src/vector.h @@ -23,6 +23,7 @@ typedef struct git_vector { int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp); void git_vector_free(git_vector *v); +void git_vector_free_all(git_vector *v); /* free each entry and self */ void git_vector_clear(git_vector *v); int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp); void git_vector_swap(git_vector *a, git_vector *b); -- cgit v1.2.1 From 25e0b1576d5f9e5248603f81d3198a65bfccf0ed Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 6 Dec 2013 15:07:57 -0800 Subject: Remove converting user error to GIT_EUSER This changes the behavior of callbacks so that the callback error code is not converted into GIT_EUSER and instead we propagate the return value through to the caller. Instead of using the giterr_capture and giterr_restore functions, we now rely on all functions to pass back the return value from a callback. To avoid having a return value with no error message, the user can call the public giterr_set_str or some such function to set an error message. There is a new helper 'giterr_set_callback' that functions can invoke after making a callback which ensures that some error message was set in case the callback did not set one. In places where the sign of the callback return value is meaningful (e.g. positive to skip, negative to abort), only the negative values are returned back to the caller, obviously, since the other values allow for continuing the loop. The hardest parts of this were in the checkout code where positive return values were overloaded as meaningful values for checkout. I fixed this by adding an output parameter to many of the internal checkout functions and removing the overload. This added some code, but it is probably a better implementation. There is some funkiness in the network code where user provided callbacks could be returning a positive or a negative value and we want to rely on that to cancel the loop. There are still a couple places where an user error might get turned into GIT_EUSER there, I think, though none exercised by the tests. --- include/git2/config.h | 4 +- include/git2/errors.h | 1 + src/attr.c | 12 +- src/checkout.c | 244 ++++++++++++++++++++-------------------- src/clone.c | 24 ++-- src/common.h | 32 ++++-- src/config.c | 42 +++---- src/diff.c | 79 ++++++------- src/diff_patch.c | 36 ++---- src/diff_print.c | 93 ++++++--------- src/diff_xdiff.c | 19 ++-- src/fetch.c | 6 +- src/fetchhead.c | 6 +- src/fileops.c | 47 +++----- src/ignore.c | 23 +--- src/index.c | 17 +-- src/indexer.c | 16 +-- src/merge.c | 4 +- src/notes.c | 10 +- src/odb_loose.c | 22 ++-- src/odb_pack.c | 32 ++---- src/pack-objects.c | 21 ++-- src/pack.c | 9 +- src/path.c | 12 +- src/path.h | 7 +- src/push.c | 4 +- src/refdb_fs.c | 25 +--- src/refs.c | 35 +++--- src/remote.c | 94 ++++++---------- src/repository.c | 5 +- src/revwalk.c | 8 +- src/stash.c | 16 +-- src/status.c | 38 ++----- src/submodule.c | 52 +++------ src/tag.c | 35 ++---- src/transports/http.c | 8 +- src/transports/local.c | 61 +++++----- src/transports/smart.c | 13 ++- src/transports/smart_protocol.c | 35 +++--- src/transports/winhttp.c | 8 +- src/tree.c | 22 ++-- src/vector.c | 16 +++ src/vector.h | 2 + tests/attr/repo.c | 6 +- tests/config/read.c | 2 +- tests/config/rename.c | 9 +- tests/core/path.c | 6 +- tests/diff/index.c | 4 +- tests/diff/notify.c | 2 +- tests/diff/rename.c | 22 ++++ tests/notes/notes.c | 2 +- tests/object/tree/walk.c | 14 +-- tests/odb/foreach.c | 28 ++--- tests/online/clone.c | 5 +- tests/online/fetch.c | 4 +- tests/refs/foreachglob.c | 4 +- tests/status/worktree.c | 4 +- 57 files changed, 603 insertions(+), 804 deletions(-) diff --git a/include/git2/config.h b/include/git2/config.h index 95da4bc03..3ab58f1a7 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -612,8 +612,8 @@ GIT_EXTERN(int) git_config_parse_int64(int64_t *out, const char *value); GIT_EXTERN(int) git_config_backend_foreach_match( git_config_backend *backend, const char *regexp, - int (*fn)(const git_config_entry *, void *), - void *data); + git_config_foreach_cb callback, + void *payload); /** @} */ diff --git a/include/git2/errors.h b/include/git2/errors.h index c6076f3ab..26f9a747c 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -71,6 +71,7 @@ typedef enum { GITERR_SSH, GITERR_FILTER, GITERR_REVERT, + GITERR_CALLBACK, } git_error_t; /** diff --git a/src/attr.c b/src/attr.c index 1a0f1f97f..08d7ee99d 100644 --- a/src/attr.c +++ b/src/attr.c @@ -191,11 +191,10 @@ int git_attr_foreach( if (error < 0) goto cleanup; - error = callback(assign->name, assign->value, payload); - if (error) { - error = giterr_user_cancel(); + error = GITERR_CALLBACK( + callback(assign->name, assign->value, payload) ); + if (error) goto cleanup; - } } } } @@ -480,7 +479,6 @@ typedef struct { const char *workdir; git_index *index; git_vector *files; - git_error_state error; } attr_walk_up_info; int git_attr_cache__decide_sources( @@ -524,7 +522,7 @@ static int push_one_attr(void *ref, git_buf *path) info->repo, path->ptr, GIT_ATTR_FILE, src[i], git_attr_file__parse_buffer, NULL, info->files); - return giterr_capture(&info->error, error); + return error; } static int collect_attr_files( @@ -570,8 +568,6 @@ static int collect_attr_files( info.files = files; error = git_path_walk_up(&dir, workdir, push_one_attr, &info); - if (error == GIT_EUSER) - error = giterr_restore(&info.error); if (error < 0) goto cleanup; diff --git a/src/checkout.c b/src/checkout.c index e33ac2ed6..4c64252e4 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -83,10 +83,8 @@ static int checkout_notify( const git_diff_file *baseline = NULL, *target = NULL, *workdir = NULL; const char *path = NULL; - if (!data->opts.notify_cb) - return 0; - - if ((why & data->opts.notify_flags) == 0) + if (!data->opts.notify_cb || + (why & data->opts.notify_flags) == 0) return 0; if (wditem) { @@ -125,8 +123,10 @@ static int checkout_notify( path = delta->old_file.path; } - return data->opts.notify_cb( - why, path, baseline, target, workdir, data->opts.notify_payload); + return giterr_set_callback( + data->opts.notify_cb( + why, path, baseline, target, workdir, data->opts.notify_payload), + "git_checkout notification"); } static bool checkout_is_workdir_modified( @@ -186,69 +186,66 @@ static bool checkout_is_workdir_modified( ((data->strategy & GIT_CHECKOUT_##FLAG) ? CHECKOUT_ACTION__##YES : CHECKOUT_ACTION__##NO) static int checkout_action_common( + int *action, checkout_data *data, - int action, const git_diff_delta *delta, const git_index_entry *wd) { git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE; - if (action <= 0) - return action; - if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) - action = (action & ~CHECKOUT_ACTION__REMOVE); + *action = (*action & ~CHECKOUT_ACTION__REMOVE); - if ((action & CHECKOUT_ACTION__UPDATE_BLOB) != 0) { + if ((*action & CHECKOUT_ACTION__UPDATE_BLOB) != 0) { if (S_ISGITLINK(delta->new_file.mode)) - action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB) | + *action = (*action & ~CHECKOUT_ACTION__UPDATE_BLOB) | CHECKOUT_ACTION__UPDATE_SUBMODULE; /* to "update" a symlink, we must remove the old one first */ if (delta->new_file.mode == GIT_FILEMODE_LINK && wd != NULL) - action |= CHECKOUT_ACTION__REMOVE; + *action |= CHECKOUT_ACTION__REMOVE; notify = GIT_CHECKOUT_NOTIFY_UPDATED; } - if ((action & CHECKOUT_ACTION__CONFLICT) != 0) + if ((*action & CHECKOUT_ACTION__CONFLICT) != 0) notify = GIT_CHECKOUT_NOTIFY_CONFLICT; - if (notify != GIT_CHECKOUT_NOTIFY_NONE && - checkout_notify(data, notify, delta, wd) != 0) - return giterr_user_cancel(); - - return action; + return checkout_notify(data, notify, delta, wd); } static int checkout_action_no_wd( + int *action, checkout_data *data, const git_diff_delta *delta) { - int action = CHECKOUT_ACTION__NONE; + int error = 0; + + *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* case 12 */ - if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL)) - return giterr_user_cancel(); - action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE); + error = checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL); + if (error) + return error; + *action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE); break; case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */ - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); break; case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */ - action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, CONFLICT); + *action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, CONFLICT); break; case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/ if (delta->new_file.mode == GIT_FILEMODE_TREE) - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); break; case GIT_DELTA_DELETED: /* case 8 or 25 */ default: /* impossible */ break; } - return checkout_action_common(data, action, delta, NULL); + return checkout_action_common(action, data, delta, NULL); } static int checkout_action_wd_only( @@ -257,6 +254,7 @@ static int checkout_action_wd_only( const git_index_entry *wd, git_vector *pathspec) { + int error = 0; bool remove = false; git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE; @@ -269,13 +267,13 @@ static int checkout_action_wd_only( /* check if item is tracked in the index but not in the checkout diff */ if (data->index != NULL) { if (wd->mode != GIT_FILEMODE_TREE) { - int error; - - if ((error = git_index_find(NULL, data->index, wd->path)) == 0) { + if (!(error = git_index_find(NULL, data->index, wd->path))) { notify = GIT_CHECKOUT_NOTIFY_DIRTY; remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0); } else if (error != GIT_ENOTFOUND) return error; + else + giterr_clear(); } else { /* for tree entries, we have to see if there are any index * entries that are contained inside that tree @@ -301,18 +299,16 @@ static int checkout_action_wd_only( remove = ((data->strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0); } - if (checkout_notify(data, notify, NULL, wd)) - return giterr_user_cancel(); + error = checkout_notify(data, notify, NULL, wd); - if (remove) { + if (!error && remove) { char *path = git_pool_strdup(&data->pool, wd->path); GITERR_CHECK_ALLOC(path); - if (git_vector_insert(&data->removes, path) < 0) - return -1; + error = git_vector_insert(&data->removes, path); } - return 0; + return error; } static bool submodule_is_config_only( @@ -331,35 +327,35 @@ static bool submodule_is_config_only( } static int checkout_action_with_wd( + int *action, checkout_data *data, const git_diff_delta *delta, const git_index_entry *wd) { - int action = CHECKOUT_ACTION__NONE; + *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* case 14/15 or 33 */ if (checkout_is_workdir_modified(data, &delta->old_file, wd)) { - if (checkout_notify( - data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd)) - return giterr_user_cancel(); - action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE); + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) ); + *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE); } break; case GIT_DELTA_ADDED: /* case 3, 4 or 6 */ - action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); break; case GIT_DELTA_DELETED: /* case 9 or 10 (or 26 but not really) */ if (checkout_is_workdir_modified(data, &delta->old_file, wd)) - action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); else - action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE); break; case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */ if (checkout_is_workdir_modified(data, &delta->old_file, wd)) - action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); else - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); break; case GIT_DELTA_TYPECHANGE: /* case 22, 23, 29, 30 */ if (delta->old_file.mode == GIT_FILEMODE_TREE) { @@ -367,92 +363,93 @@ static int checkout_action_with_wd( /* either deleting items in old tree will delete the wd dir, * or we'll get a conflict when we attempt blob update... */ - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); else if (wd->mode == GIT_FILEMODE_COMMIT) { /* workdir is possibly a "phantom" submodule - treat as a * tree if the only submodule info came from the config */ if (submodule_is_config_only(data, wd->path)) - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); else - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); } else - action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); } else if (checkout_is_workdir_modified(data, &delta->old_file, wd)) - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); else - action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE); /* don't update if the typechange is to a tree */ if (delta->new_file.mode == GIT_FILEMODE_TREE) - action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB); + *action = (*action & ~CHECKOUT_ACTION__UPDATE_BLOB); break; default: /* impossible */ break; } - return checkout_action_common(data, action, delta, wd); + return checkout_action_common(action, data, delta, wd); } static int checkout_action_with_wd_blocker( + int *action, checkout_data *data, const git_diff_delta *delta, const git_index_entry *wd) { - int action = CHECKOUT_ACTION__NONE; + *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* should show delta as dirty / deleted */ - if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd)) - return giterr_user_cancel(); - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE); + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) ); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE); break; case GIT_DELTA_ADDED: case GIT_DELTA_MODIFIED: - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; case GIT_DELTA_DELETED: - action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); break; case GIT_DELTA_TYPECHANGE: /* not 100% certain about this... */ - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; default: /* impossible */ break; } - return checkout_action_common(data, action, delta, wd); + return checkout_action_common(action, data, delta, wd); } static int checkout_action_with_wd_dir( + int *action, checkout_data *data, const git_diff_delta *delta, const git_index_entry *wd) { - int action = CHECKOUT_ACTION__NONE; + *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* case 19 or 24 (or 34 but not really) */ - if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL) || - checkout_notify( - data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)) - return giterr_user_cancel(); + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL)); + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)); break; case GIT_DELTA_ADDED:/* case 4 (and 7 for dir) */ case GIT_DELTA_MODIFIED: /* case 20 (or 37 but not really) */ if (delta->old_file.mode == GIT_FILEMODE_COMMIT) /* expected submodule (and maybe found one) */; else if (delta->new_file.mode != GIT_FILEMODE_TREE) - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; case GIT_DELTA_DELETED: /* case 11 (and 27 for dir) */ - if (delta->old_file.mode != GIT_FILEMODE_TREE && - checkout_notify( - data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)) - return giterr_user_cancel(); + if (delta->old_file.mode != GIT_FILEMODE_TREE) + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)); break; case GIT_DELTA_TYPECHANGE: /* case 24 or 31 */ if (delta->old_file.mode == GIT_FILEMODE_TREE) { @@ -462,39 +459,41 @@ static int checkout_action_with_wd_dir( * directory if is it left empty, so we can defer removing the * dir and it will succeed if no children are left. */ - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); - if (action != CHECKOUT_ACTION__NONE) - action |= CHECKOUT_ACTION__DEFER_REMOVE; + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + if (*action != CHECKOUT_ACTION__NONE) + *action |= CHECKOUT_ACTION__DEFER_REMOVE; } else if (delta->new_file.mode != GIT_FILEMODE_TREE) /* For typechange to dir, dir is already created so no action */ - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; default: /* impossible */ break; } - return checkout_action_common(data, action, delta, wd); + return checkout_action_common(action, data, delta, wd); } static int checkout_action( + int *action, checkout_data *data, git_diff_delta *delta, git_iterator *workdir, - const git_index_entry **wditem_ptr, + const git_index_entry **wditem, git_vector *pathspec) { - const git_index_entry *wd = *wditem_ptr; - int cmp = -1, act; + int cmp = -1, error; int (*strcomp)(const char *, const char *) = data->diff->strcomp; int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp; - int error; + int (*advance)(const git_index_entry **, git_iterator *) = NULL; /* move workdir iterator to follow along with deltas */ while (1) { + const git_index_entry *wd = *wditem; + if (!wd) - return checkout_action_no_wd(data, delta); + return checkout_action_no_wd(action, data, delta); cmp = strcomp(wd->path, delta->old_file.path); @@ -512,79 +511,77 @@ static int checkout_action( if (cmp == 0) { if (wd->mode == GIT_FILEMODE_TREE) { /* case 2 - entry prefixed by workdir tree */ - error = git_iterator_advance_into_or_over(&wd, workdir); - if (error && error != GIT_ITEROVER) - goto fail; - *wditem_ptr = wd; + error = git_iterator_advance_into_or_over(wditem, workdir); + if (error < 0 && error != GIT_ITEROVER) + goto done; continue; } /* case 3 maybe - wd contains non-dir where dir expected */ if (delta->old_file.path[strlen(wd->path)] == '/') { - act = checkout_action_with_wd_blocker(data, delta, wd); - *wditem_ptr = - git_iterator_advance(&wd, workdir) ? NULL : wd; - return act; + error = checkout_action_with_wd_blocker( + action, data, delta, wd); + advance = git_iterator_advance; + goto done; } } /* case 1 - handle wd item (if it matches pathspec) */ - if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0) - goto fail; - if ((error = git_iterator_advance(&wd, workdir)) < 0 && + error = checkout_action_wd_only(data, workdir, wd, pathspec); + if (error) + goto done; + if ((error = git_iterator_advance(wditem, workdir)) < 0 && error != GIT_ITEROVER) - goto fail; - - *wditem_ptr = wd; + goto done; continue; } if (cmp == 0) { /* case 4 */ - act = checkout_action_with_wd(data, delta, wd); - *wditem_ptr = git_iterator_advance(&wd, workdir) ? NULL : wd; - return act; + error = checkout_action_with_wd(action, data, delta, wd); + advance = git_iterator_advance; + goto done; } cmp = pfxcomp(wd->path, delta->old_file.path); if (cmp == 0) { /* case 5 */ if (wd->path[strlen(delta->old_file.path)] != '/') - return checkout_action_no_wd(data, delta); + return checkout_action_no_wd(action, data, delta); if (delta->status == GIT_DELTA_TYPECHANGE) { if (delta->old_file.mode == GIT_FILEMODE_TREE) { - act = checkout_action_with_wd(data, delta, wd); - if ((error = git_iterator_advance_into(&wd, workdir)) < 0 && - error != GIT_ENOTFOUND) - goto fail; - *wditem_ptr = wd; - return act; + error = checkout_action_with_wd(action, data, delta, wd); + advance = git_iterator_advance_into; + goto done; } if (delta->new_file.mode == GIT_FILEMODE_TREE || delta->new_file.mode == GIT_FILEMODE_COMMIT || delta->old_file.mode == GIT_FILEMODE_COMMIT) { - act = checkout_action_with_wd(data, delta, wd); - if ((error = git_iterator_advance(&wd, workdir)) < 0 && - error != GIT_ITEROVER) - goto fail; - *wditem_ptr = wd; - return act; + error = checkout_action_with_wd(action, data, delta, wd); + advance = git_iterator_advance; + goto done; } } - return checkout_action_with_wd_dir(data, delta, wd); + return checkout_action_with_wd_dir(action, data, delta, wd); } /* case 6 - wd is after delta */ - return checkout_action_no_wd(data, delta); + return checkout_action_no_wd(action, data, delta); } -fail: - *wditem_ptr = NULL; - return -1; +done: + if (!error && advance != NULL && + (error = advance(wditem, workdir)) < 0) { + *wditem = NULL; + if (error == GIT_ITEROVER) + error = 0; + } + + return error; } static int checkout_remaining_wd_items( @@ -965,7 +962,7 @@ static int checkout_get_actions( checkout_data *data, git_iterator *workdir) { - int error = 0; + int error = 0, act; const git_index_entry *wditem; git_vector pathspec = GIT_VECTOR_INIT, *deltas; git_pool pathpool = GIT_POOL_INIT_STRINGPOOL; @@ -992,12 +989,9 @@ static int checkout_get_actions( } git_vector_foreach(deltas, i, delta) { - int act = checkout_action(data, delta, workdir, &wditem, &pathspec); - - if (act < 0) { - error = act; + error = checkout_action(&act, data, delta, workdir, &wditem, &pathspec); + if (error) goto fail; - } actions[i] = act; @@ -1012,7 +1006,7 @@ static int checkout_get_actions( } error = checkout_remaining_wd_items(data, workdir, wditem, &pathspec); - if (error < 0) + if (error) goto fail; counts[CHECKOUT_ACTION__REMOVE] += data->removes.length; diff --git a/src/clone.c b/src/clone.c index 415efabba..ffbe8f8af 100644 --- a/src/clone.c +++ b/src/clone.c @@ -107,7 +107,6 @@ struct head_info { git_buf branchname; const git_refspec *refspec; bool found; - git_error_state error; }; static int reference_matches_remote_head( @@ -147,7 +146,7 @@ static int reference_matches_remote_head( } } - return giterr_capture(&head_info->error, error); + return error; } static int update_head_to_new_branch( @@ -209,12 +208,8 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote) /* Check to see if the remote HEAD points to the remote master */ error = reference_matches_remote_head( git_buf_cstr(&remote_master_name), &head_info); - - if (error < 0) { - error = giterr_restore(&head_info.error); - if (error < 0 && error != GIT_ITEROVER) - goto cleanup; - } + if (error < 0 && error != GIT_ITEROVER) + goto cleanup; if (head_info.found) { error = update_head_to_new_branch( @@ -227,9 +222,6 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote) /* Not master. Check all the other refs. */ error = git_reference_foreach_name( repo, reference_matches_remote_head, &head_info); - - if (error == GIT_EUSER) - error = giterr_restore(&head_info.error); if (error < 0 && error != GIT_ITEROVER) goto cleanup; @@ -349,7 +341,7 @@ int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_ old_fetchhead = git_remote_update_fetchhead(remote); git_remote_set_update_fetchhead(remote, 0); - if ((error = git_remote_fetch(remote)) < 0) + if ((error = git_remote_fetch(remote)) != 0) goto cleanup; if (branch) @@ -363,10 +355,12 @@ int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_ cleanup: git_remote_set_update_fetchhead(remote, old_fetchhead); + /* Go back to the original refspecs */ - if (git_remote_set_fetch_refspecs(remote, &refspecs) < 0) { - git_strarray_free(&refspecs); - return -1; + { + int error_alt = git_remote_set_fetch_refspecs(remote, &refspecs); + if (!error) + error = error_alt; } git_strarray_free(&refspecs); diff --git a/src/common.h b/src/common.h index 0ad4130aa..a0e47df9a 100644 --- a/src/common.h +++ b/src/common.h @@ -62,7 +62,7 @@ * Check a return value and propogate result if non-zero. */ #define GITERR_CHECK_ERROR(code) \ - do { int _err = (code); if (_err < 0) return _err; } while (0) + do { int _err = (code); if (_err) return _err; } while (0) /** * Set the error message for this thread, formatting as needed. @@ -75,6 +75,27 @@ void giterr_set(int error_class, const char *string, ...); */ int giterr_set_regex(const regex_t *regex, int error_code); +/** + * Set error message for user callback if needed. + * + * If the error code in non-zero and no error message is set, this + * sets a generic error message. + * + * @return This always returns the `error_code` parameter. + */ +GIT_INLINE(int) giterr_set_callback(int error_code, const char *action) +{ + if (error_code) { + const git_error *e = giterr_last(); + if (!e || !e->message) + giterr_set(e ? e->klass : GITERR_CALLBACK, + "%s callback returned %d", action, error_code); + } + return error_code; +} + +#define GITERR_CALLBACK(code) giterr_set_callback((code), __func__) + /** * Gets the system error code for this thread. */ @@ -85,15 +106,6 @@ int giterr_system_last(void); */ void giterr_system_set(int code); -/** - * Note that a user cancelled an operation with GIT_EUSER - */ -GIT_INLINE(int) giterr_user_cancel(void) -{ - giterr_clear(); - return GIT_EUSER; -} - /** * Structure to preserve libgit2 error state */ diff --git a/src/config.c b/src/config.c index 3af9d58de..8b3a426ed 100644 --- a/src/config.c +++ b/src/config.c @@ -480,23 +480,23 @@ int git_config_foreach( int git_config_backend_foreach_match( git_config_backend *backend, const char *regexp, - int (*fn)(const git_config_entry *, void *), - void *data) + git_config_foreach_cb cb, + void *payload) { git_config_entry *entry; git_config_iterator* iter; regex_t regex; - int result = 0; + int error = 0; if (regexp != NULL) { - if ((result = regcomp(®ex, regexp, REG_EXTENDED)) < 0) { - giterr_set_regex(®ex, result); + if ((error = regcomp(®ex, regexp, REG_EXTENDED)) < 0) { + giterr_set_regex(®ex, error); regfree(®ex); return -1; } } - if ((result = backend->iterator(&iter, backend)) < 0) { + if ((error = backend->iterator(&iter, backend)) < 0) { iter = NULL; return -1; } @@ -507,10 +507,9 @@ int git_config_backend_foreach_match( continue; /* abort iterator on non-zero return value */ - if (fn(entry, data)) { - result = giterr_user_cancel(); + error = GITERR_CALLBACK( cb(entry, payload) ); + if (error) break; - } } if (regexp != NULL) @@ -518,7 +517,7 @@ int git_config_backend_foreach_match( iter->free(iter); - return result; + return error; } int git_config_foreach_match( @@ -534,12 +533,9 @@ int git_config_foreach_match( if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0) return error; - while ((error = git_config_next(&entry, iter)) == 0) { - if (cb(entry, payload)) { - error = giterr_user_cancel(); - break; - } - } + while (!(error = git_config_next(&entry, iter)) && + !(error = GITERR_CALLBACK( cb(entry, payload) ))) + /* make callback on each config */; git_config_iterator_free(iter); @@ -798,10 +794,8 @@ int git_config_get_multivar_foreach( while ((err = iter->next(&entry, iter)) == 0) { found = 1; - if (cb(entry, payload)) { - iter->free(iter); - return giterr_user_cancel(); - } + if ((err = GITERR_CALLBACK( cb(entry, payload) )) != 0) + break; } iter->free(iter); @@ -1212,7 +1206,6 @@ struct rename_data { git_config *config; git_buf *name; size_t old_len; - git_error_state error; }; static int rename_config_entries_cb( @@ -1235,8 +1228,7 @@ static int rename_config_entries_cb( if (!error) error = git_config_delete_entry(data->config, entry->name); - /* capture error message as needed, since it will become EUSER */ - return giterr_capture(&data->error, error); + return error; } int git_config_rename_section( @@ -1257,7 +1249,6 @@ int git_config_rename_section( if ((error = git_repository_config__weakptr(&config, repo)) < 0) goto cleanup; - memset(&data, 0, sizeof(data)); data.config = config; data.name = &replace; data.old_len = strlen(old_section_name) + 1; @@ -1277,9 +1268,6 @@ int git_config_rename_section( error = git_config_foreach_match( config, git_buf_cstr(&pattern), rename_config_entries_cb, &data); - if (error == GIT_EUSER) - error = giterr_restore(&data.error); - cleanup: git_buf_free(&pattern); git_buf_free(&replace); diff --git a/src/diff.c b/src/diff.c index af47e86aa..101426f6e 100644 --- a/src/diff.c +++ b/src/diff.c @@ -49,16 +49,25 @@ static git_diff_delta *diff_delta__alloc( return delta; } -static int diff_notify( - const git_diff *diff, - const git_diff_delta *delta, - const char *matched_pathspec) +static int diff_insert_delta( + git_diff *diff, git_diff_delta *delta, const char *matched_pathspec) { - if (!diff->opts.notify_cb) - return 0; + int error = 0; + + if (diff->opts.notify_cb) { + error = diff->opts.notify_cb( + diff, delta, matched_pathspec, diff->opts.notify_payload); + + if (error) { + git__free(delta); + return (error > 0) ? 0 : giterr_set_callback(error, "git_diff"); + } + } + + if ((error = git_vector_insert(&diff->deltas, delta)) < 0) + git__free(delta); - return diff->opts.notify_cb( - diff, delta, matched_pathspec, diff->opts.notify_payload); + return error; } static int diff_delta__from_one( @@ -68,7 +77,6 @@ static int diff_delta__from_one( { git_diff_delta *delta; const char *matched_pathspec; - int notify_res; if ((entry->flags & GIT_IDXENTRY_VALID) != 0) return 0; @@ -111,21 +119,12 @@ static int diff_delta__from_one( !git_oid_iszero(&delta->new_file.oid)) delta->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; - notify_res = diff_notify(diff, delta, matched_pathspec); - - if (notify_res) - git__free(delta); - else if (git_vector_insert(&diff->deltas, delta) < 0) { - git__free(delta); - return -1; - } - - return notify_res < 0 ? giterr_user_cancel() : 0; + return diff_insert_delta(diff, delta, matched_pathspec); } static int diff_delta__from_two( git_diff *diff, - git_delta_t status, + git_delta_t status, const git_index_entry *old_entry, uint32_t old_mode, const git_index_entry *new_entry, @@ -134,7 +133,6 @@ static int diff_delta__from_two( const char *matched_pathspec) { git_diff_delta *delta; - int notify_res; const char *canonical_path = old_entry->path; if (status == GIT_DELTA_UNMODIFIED && @@ -173,16 +171,7 @@ static int diff_delta__from_two( if (new_oid || !git_oid_iszero(&new_entry->oid)) delta->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; - notify_res = diff_notify(diff, delta, matched_pathspec); - - if (notify_res) - git__free(delta); - else if (git_vector_insert(&diff->deltas, delta) < 0) { - git__free(delta); - return -1; - } - - return notify_res < 0 ? giterr_user_cancel() : 0; + return diff_insert_delta(diff, delta, matched_pathspec); } static git_diff_delta *diff_delta__last_for_item( @@ -654,6 +643,7 @@ static int maybe_modified( unsigned int nmode = nitem->mode; bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_TYPE_WORKDIR); const char *matched_pathspec; + int error = 0; if (!git_pathspec__match( &diff->pathspec, oitem->path, @@ -688,10 +678,9 @@ static int maybe_modified( if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE)) status = GIT_DELTA_TYPECHANGE; else { - 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 (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem))) + error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem); + return error; } } @@ -713,8 +702,8 @@ static int maybe_modified( /* TODO: add check against index file st_mtime to avoid racy-git */ if (S_ISGITLINK(nmode)) { - if (maybe_modified_submodule(&status, &noid, diff, info) < 0) - return -1; + if ((error = maybe_modified_submodule(&status, &noid, diff, info)) < 0) + return error; } /* if the stat data looks different, then mark modified - this just @@ -741,9 +730,9 @@ static int maybe_modified( */ if (status == GIT_DELTA_MODIFIED && git_oid_iszero(&nitem->oid)) { if (git_oid_iszero(&noid)) { - if (git_diff__oid_for_file(diff->repo, - nitem->path, nitem->mode, nitem->file_size, &noid) < 0) - return -1; + if ((error = git_diff__oid_for_file(diff->repo, + nitem->path, nitem->mode, nitem->file_size, &noid)) < 0) + return error; } /* if oid matches, then mark unmodified (except submodules, where @@ -898,7 +887,7 @@ static int handle_unmatched_new_item( git_diff_delta *last; /* attempt to insert record for this directory */ - if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0) + if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0) return error; /* if delta wasn't created (because of rules), just skip ahead */ @@ -977,7 +966,7 @@ static int handle_unmatched_new_item( } /* Actually create the record for this item if necessary */ - if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0) + if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0) return error; /* If user requested TYPECHANGE records, then check for that instead of @@ -1002,7 +991,7 @@ static int handle_unmatched_old_item( git_diff *diff, diff_in_progress *info) { int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem); - if (error < 0) + if (error != 0) return error; /* if we are generating TYPECHANGE records then check for that @@ -1399,10 +1388,8 @@ int git_diff__paired_foreach( i++; j++; } - if (cb(h2i, i2w, payload)) { - error = giterr_user_cancel(); + if ((error = GITERR_CALLBACK( cb(h2i, i2w, payload) )) != 0) break; - } } /* restore case-insensitive delta sort */ diff --git a/src/diff_patch.c b/src/diff_patch.c index c0910558e..11f02478d 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -32,7 +32,6 @@ struct git_patch { git_array_t(git_diff_line) lines; size_t content_size, context_size, header_size; git_pool flattened; - git_error_state error; }; enum { @@ -200,11 +199,12 @@ static int diff_patch_invoke_file_callback( float progress = patch->diff ? ((float)patch->delta_index / patch->diff->deltas.length) : 1.0f; - if (output->file_cb && - output->file_cb(patch->delta, progress, output->payload) != 0) - return giterr_user_cancel(); + if (!output->file_cb) + return 0; - return 0; + return giterr_set_callback( + output->file_cb(patch->delta, progress, output->payload), + "git_patch"); } static int diff_patch_generate(git_patch *patch, git_diff_output *output) @@ -291,7 +291,7 @@ int git_diff_foreach( git_patch_free(&patch); - if (error < 0) + if (error) break; } @@ -331,9 +331,6 @@ static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo) if (!error) error = diff_patch_generate(patch, (git_diff_output *)xo); - if (error == GIT_EUSER) - giterr_clear(); /* don't leave error message set invalidly */ - return error; } @@ -462,9 +459,6 @@ int git_patch_from_blobs( error = diff_patch_from_blobs( pd, &xo, old_blob, old_path, new_blob, new_path, opts); - if (error == GIT_EUSER) - error = giterr_restore(&pd->patch.error); - if (!error) *out = (git_patch *)pd; else @@ -576,9 +570,6 @@ int git_patch_from_blob_and_buffer( error = diff_patch_from_blob_and_buffer( pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts); - if (error == GIT_EUSER) - error = giterr_restore(&pd->patch.error); - if (!error) *out = (git_patch *)pd; else @@ -627,12 +618,9 @@ int git_patch_from_diff( if (!error) error = diff_patch_generate(patch, &xo.output); - if (error == GIT_EUSER) - error = giterr_restore(&patch->error); - if (!error) { - /* if cumulative diff size is < 0.5 total size, flatten the patch */ - /* unload the file content */ + /* TODO: if cumulative diff size is < 0.5 total size, flatten patch */ + /* TODO: and unload the file content */ } if (error || !patch_ptr) @@ -640,8 +628,6 @@ int git_patch_from_diff( else *patch_ptr = patch; - if (error == GIT_EUSER) - giterr_clear(); /* don't leave error message set invalidly */ return error; } @@ -879,8 +865,7 @@ static int diff_patch_hunk_cb( GIT_UNUSED(delta); hunk = git_array_alloc(patch->hunks); - if (!hunk) - return giterr_capture(&patch->error, -1); + GITERR_CHECK_ALLOC(hunk); memcpy(&hunk->hunk, hunk_, sizeof(hunk->hunk)); @@ -909,8 +894,7 @@ static int diff_patch_line_cb( assert(hunk); /* programmer error if no hunk is available */ line = git_array_alloc(patch->lines); - if (!line) - return giterr_capture(&patch->error, -1); + GITERR_CHECK_ALLOC(line); memcpy(line, line_, sizeof(*line)); diff --git a/src/diff_print.c b/src/diff_print.c index 712402864..ff477e4c8 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -18,7 +18,6 @@ typedef struct { uint32_t flags; int oid_strlen; git_diff_line line; - git_error_state error; } diff_print_info; static int diff_print_info_init( @@ -34,7 +33,6 @@ static int diff_print_info_init( pi->print_cb = cb; pi->payload = payload; pi->buf = out; - memset(&pi->error, 0, sizeof(pi->error)); if (diff) pi->flags = diff->opts.flags; @@ -104,19 +102,16 @@ static int diff_print_one_name_only( return 0; git_buf_clear(out); - - if (git_buf_puts(out, delta->new_file.path) < 0 || - git_buf_putc(out, '\n')) - return giterr_capture(&pi->error, -1); + git_buf_puts(out, delta->new_file.path); + git_buf_putc(out, '\n'); + if (git_buf_oom(out)) + return -1; pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return giterr_user_cancel(); - - return 0; + return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_one_name_status( @@ -150,18 +145,14 @@ static int diff_print_one_name_status( git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix); else git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path); - if (git_buf_oom(out)) - return giterr_capture(&pi->error, -1); + return -1; pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return giterr_user_cancel(); - - return 0; + return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_one_raw( @@ -198,16 +189,13 @@ static int diff_print_one_raw( delta->old_file.path : delta->new_file.path); if (git_buf_oom(out)) - return giterr_capture(&pi->error, -1); + return -1; pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return giterr_user_cancel(); - - return 0; + return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_oid_range( @@ -234,10 +222,7 @@ static int diff_print_oid_range( git_buf_printf(out, "index %s..%s\n", start_oid, end_oid); } - if (git_buf_oom(out)) - return -1; - - return 0; + return git_buf_oom(out) ? -1 : 0; } static int diff_delta_format_with_paths( @@ -281,8 +266,7 @@ int git_diff_delta__format_file_header( git_buf_printf(out, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path); - if (diff_print_oid_range(out, delta, oid_strlen) < 0) - return -1; + GITERR_CHECK_ERROR(diff_print_oid_range(out, delta, oid_strlen)); if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) diff_delta_format_with_paths( @@ -294,6 +278,7 @@ int git_diff_delta__format_file_header( static int diff_print_patch_file( const git_diff_delta *delta, float progress, void *data) { + int error; diff_print_info *pi = data; const char *oldpfx = pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT; @@ -309,36 +294,33 @@ static int diff_print_patch_file( (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0)) return 0; - if (git_diff_delta__format_file_header( - pi->buf, delta, oldpfx, newpfx, pi->oid_strlen) < 0) - return giterr_capture(&pi->error, -1); + if ((error = git_diff_delta__format_file_header( + pi->buf, delta, oldpfx, newpfx, pi->oid_strlen)) < 0) + return error; pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(pi->buf); pi->line.content_len = git_buf_len(pi->buf); - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return giterr_user_cancel(); + if ((error = pi->print_cb(delta, NULL, &pi->line, pi->payload)) != 0) + return error; if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) return 0; git_buf_clear(pi->buf); - if (diff_delta_format_with_paths( + if ((error = diff_delta_format_with_paths( pi->buf, delta, oldpfx, newpfx, - "Binary files %s%s and %s%s differ\n") < 0) - return giterr_capture(&pi->error, -1); + "Binary files %s%s and %s%s differ\n")) < 0) + return error; pi->line.origin = GIT_DIFF_LINE_BINARY; pi->line.content = git_buf_cstr(pi->buf); pi->line.content_len = git_buf_len(pi->buf); pi->line.num_lines = 1; - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return giterr_user_cancel(); - - return 0; + return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_patch_hunk( @@ -355,10 +337,7 @@ static int diff_print_patch_hunk( pi->line.content = h->header; pi->line.content_len = h->header_len; - if (pi->print_cb(d, h, &pi->line, pi->payload)) - return giterr_user_cancel(); - - return 0; + return pi->print_cb(d, h, &pi->line, pi->payload); } static int diff_print_patch_line( @@ -372,10 +351,7 @@ static int diff_print_patch_line( if (S_ISDIR(delta->new_file.mode)) return 0; - if (pi->print_cb(delta, hunk, line, pi->payload)) - return giterr_user_cancel(); - - return 0; + return pi->print_cb(delta, hunk, line, pi->payload); } /* print a git_diff to an output callback */ @@ -421,8 +397,8 @@ int git_diff_print( error = git_diff_foreach( diff, print_file, print_hunk, print_line, &pi); - if (error == GIT_EUSER && pi.error.error_code) - error = giterr_restore(&pi.error); + if (error) /* make sure error message is set */ + giterr_set_callback(error, "git_diff_print"); } git_buf_free(&buf); @@ -450,8 +426,8 @@ int git_patch_print( patch, diff_print_patch_file, diff_print_patch_hunk, diff_print_patch_line, &pi); - if (error && error != GIT_EUSER) - error = giterr_restore(&pi.error); + if (error) /* make sure error message is set */ + giterr_set_callback(error, "git_patch_print"); } git_buf_free(&temp); @@ -484,17 +460,12 @@ int git_patch_to_str( int error; git_buf output = GIT_BUF_INIT; - error = git_patch_print(patch, diff_print_to_buffer_cb, &output); - - /* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1, - * meaning a memory allocation failure, so just map to -1... - */ - if (error == GIT_EUSER) { - giterr_set_oom(); - error = -1; + if (!(error = git_patch_print(patch, diff_print_to_buffer_cb, &output))) + *string = git_buf_detach(&output); + else { + git_buf_free(&output); + *string = NULL; } - *string = git_buf_detach(&output); - return error; } diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c index c6ca48882..e5984f1c9 100644 --- a/src/diff_xdiff.c +++ b/src/diff_xdiff.c @@ -126,8 +126,9 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) info->hunk.header[info->hunk.header_len] = '\0'; if (output->hunk_cb != NULL && - output->hunk_cb(delta, &info->hunk, output->payload)) - return (output->error = giterr_user_cancel()); + (output->error = output->hunk_cb( + delta, &info->hunk, output->payload))) + return output->error; info->old_lineno = info->hunk.old_start; info->new_lineno = info->hunk.new_start; @@ -150,10 +151,9 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) output->error = diff_update_lines( info, &line, bufs[1].ptr, bufs[1].size); - if (!output->error && - output->data_cb != NULL && - output->data_cb(delta, &info->hunk, &line, output->payload)) - output->error = giterr_user_cancel(); + if (!output->error && output->data_cb != NULL) + output->error = output->data_cb( + delta, &info->hunk, &line, output->payload); } if (len == 3 && !output->error) { @@ -172,10 +172,9 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) output->error = diff_update_lines( info, &line, bufs[2].ptr, bufs[2].size); - if (!output->error && - output->data_cb != NULL && - output->data_cb(delta, &info->hunk, &line, output->payload)) - output->error = giterr_user_cancel(); + if (!output->error && output->data_cb != NULL) + output->error = output->data_cb( + delta, &info->hunk, &line, output->payload); } return output->error; diff --git a/src/fetch.c b/src/fetch.c index 276591821..5bf2b93c1 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -104,7 +104,7 @@ cleanup: int git_fetch_negotiate(git_remote *remote) { git_transport *t = remote->transport; - + if (filter_wants(remote) < 0) { giterr_set(GITERR_NET, "Failed to filter the reference list for wants"); return -1; @@ -128,9 +128,9 @@ int git_fetch_download_pack(git_remote *remote) { git_transport *t = remote->transport; - if(!remote->need_pack) + if (!remote->need_pack) return 0; return t->download_pack(t, remote->repo, &remote->stats, - remote->callbacks.transfer_progress, remote->callbacks.payload); + remote->callbacks.transfer_progress, remote->callbacks.payload); } diff --git a/src/fetchhead.c b/src/fetchhead.c index ee1492211..7c37be4c6 100644 --- a/src/fetchhead.c +++ b/src/fetchhead.c @@ -269,10 +269,10 @@ int git_repository_fetchhead_foreach(git_repository *repo, else ref_name = NULL; - if (cb(ref_name, remote_url, &oid, is_merge, payload) != 0) { - error = giterr_user_cancel(); + error = GITERR_CALLBACK( + cb(ref_name, remote_url, &oid, is_merge, payload) ); + if (error) goto done; - } } if (*buffer) { diff --git a/src/fileops.c b/src/fileops.c index 0418e9e52..98dcd3269 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -404,7 +404,6 @@ typedef struct { size_t baselen; uint32_t flags; int depth; - git_error_state error; } futils__rmdir_data; #define FUTILS_MAX_DEPTH 100 @@ -474,16 +473,14 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) data->depth++; error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data); - if (error == GIT_EUSER) - return error; data->depth--; if (error < 0) - goto done; + return error; if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0) - goto done; + return error; if ((error = p_rmdir(path->ptr)) < 0) { if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 && @@ -502,8 +499,7 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0) error = futils__error_cannot_rmdir(path->ptr, "still present"); -done: - return giterr_capture(&data->error, error); + return error; } static int futils__rmdir_empty_parent(void *opaque, git_buf *path) @@ -512,9 +508,9 @@ static int futils__rmdir_empty_parent(void *opaque, git_buf *path) int error = 0; if (git_buf_len(path) <= data->baselen) - return giterr_capture(&data->error, GIT_ITEROVER); + error = GIT_ITEROVER; - if (p_rmdir(git_buf_cstr(path)) < 0) { + else if (p_rmdir(git_buf_cstr(path)) < 0) { int en = errno; if (en == ENOENT || en == ENOTDIR) { @@ -526,7 +522,7 @@ static int futils__rmdir_empty_parent(void *opaque, git_buf *path) } } - return giterr_capture(&data->error, error); + return error; } int git_futils_rmdir_r( @@ -552,10 +548,10 @@ int git_futils_rmdir_r( error = git_path_walk_up( &fullpath, base, futils__rmdir_empty_parent, &data); - if (error == GIT_EUSER) - error = giterr_restore(&data.error); - if (error == GIT_ITEROVER) + if (error == GIT_ITEROVER) { + giterr_clear(); error = 0; + } git_buf_free(&fullpath); @@ -859,7 +855,6 @@ typedef struct { uint32_t flags; uint32_t mkdir_flags; mode_t dirmode; - git_error_state error; } cp_r_info; #define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10) @@ -899,19 +894,19 @@ static int _cp_r_callback(void *ref, git_buf *from) if ((error = git_buf_joinpath( &info->to, info->to_root, from->ptr + info->from_prefix)) < 0) - goto done; + return error; if (!(error = git_path_lstat(info->to.ptr, &to_st))) exists = true; else if (error != GIT_ENOTFOUND) - goto done; + return error; else { giterr_clear(); error = 0; } if ((error = git_path_lstat(from->ptr, &from_st)) < 0) - goto done; + return error; if (S_ISDIR(from_st.st_mode)) { mode_t oldmode = info->dirmode; @@ -925,16 +920,13 @@ static int _cp_r_callback(void *ref, git_buf *from) error = _cp_r_mkdir(info, from); /* recurse onto target directory */ - if (!error && (!exists || S_ISDIR(to_st.st_mode))) { + if (!error && (!exists || S_ISDIR(to_st.st_mode))) error = git_path_direach(from, 0, _cp_r_callback, info); - if (error == GIT_EUSER) - return error; - } if (oldmode != 0) info->dirmode = oldmode; - goto done; + return error; } if (exists) { @@ -944,8 +936,7 @@ static int _cp_r_callback(void *ref, git_buf *from) if (p_unlink(info->to.ptr) < 0) { giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'", info->to.ptr); - error = -1; - goto done; + return GIT_EEXISTS; } } @@ -958,7 +949,7 @@ static int _cp_r_callback(void *ref, git_buf *from) /* Make container directory on demand if needed */ if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 && (error = _cp_r_mkdir(info, from)) < 0) - goto done; + return error; /* make symlink or regular file */ if (S_ISLNK(from_st.st_mode)) @@ -972,8 +963,7 @@ static int _cp_r_callback(void *ref, git_buf *from) error = git_futils_cp(from->ptr, info->to.ptr, usemode); } -done: - return giterr_capture(&info->error, error); + return error; } int git_futils_cp_r( @@ -1015,9 +1005,6 @@ int git_futils_cp_r( git_buf_free(&path); git_buf_free(&info.to); - if (error == GIT_EUSER) - error = giterr_restore(&info.error); - return error; } diff --git a/src/ignore.c b/src/ignore.c index aa53d409d..c79fe4871 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -74,20 +74,12 @@ static int parse_ignore_file( #define push_ignore_file(R,IGN,S,B,F) \ git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(IGN),(S)) -struct ignores_walk_up_data { - git_ignores *ign; - git_error_state error; -}; - -static int push_one_ignore(void *ref, git_buf *path) +static int push_one_ignore(void *payload, git_buf *path) { - struct ignores_walk_up_data *data = ref; + git_ignores *ign = payload; - return giterr_capture( - &data->error, - push_ignore_file( - data->ign->repo, data->ign, &data->ign->ign_path, - path->ptr, GIT_IGNORE_FILE) ); + return push_ignore_file( + ign->repo, ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE); } static int get_internal_ignores(git_attr_file **ign, git_repository *repo) @@ -142,13 +134,8 @@ int git_ignore__for_path( /* load .gitignore up the path */ if (workdir != NULL) { - struct ignores_walk_up_data data = { ignores }; - error = git_path_walk_up( - &ignores->dir, workdir, push_one_ignore, &data); - - if (error == GIT_EUSER) - error = giterr_restore(&data.error); + &ignores->dir, workdir, push_one_ignore, ignores); if (error < 0) goto cleanup; } diff --git a/src/index.c b/src/index.c index d0d2cf187..671bdfa79 100644 --- a/src/index.c +++ b/src/index.c @@ -2036,17 +2036,12 @@ int git_index_read_tree(git_index *index, const git_tree *tree) error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data); - if (error == GIT_EUSER) { - giterr_set_oom(); - git_vector_free(&entries); - return -1; + if (!error) { + git_vector_sort(&entries); + git_index_clear(index); + git_vector_swap(&entries, &index->entries); } - git_vector_sort(&entries); - - git_index_clear(index); - - git_vector_swap(&entries, &index->entries); git_vector_free(&entries); return error; @@ -2122,7 +2117,7 @@ int git_index_add_all( if (error > 0) /* return > 0 means skip this one */ continue; if (error < 0) { /* return < 0 means abort */ - error = giterr_user_cancel(); + GITERR_CALLBACK(error); break; } } @@ -2210,7 +2205,7 @@ static int index_apply_to_all( continue; } if (error < 0) { /* return < 0 means abort */ - error = giterr_user_cancel(); + giterr_set_callback(error, "git_index_matched_path"); break; } } diff --git a/src/indexer.c b/src/indexer.c index d3253cb57..320845bd9 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -386,10 +386,10 @@ on_error: static int do_progress_callback(git_indexer *idx, git_transfer_progress *stats) { - if (idx->progress_cb && - idx->progress_cb(stats, idx->progress_payload)) - return giterr_user_cancel(); - + if (idx->progress_cb) + return giterr_set_callback( + idx->progress_cb(stats, idx->progress_payload), + "indexer progress"); return 0; } @@ -495,7 +495,7 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran processed = stats->indexed_objects = 0; stats->total_objects = total_objects; - if ((error = do_progress_callback(idx, stats)) < 0) + if ((error = do_progress_callback(idx, stats)) != 0) return error; } @@ -520,7 +520,7 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran return 0; } if (error < 0) - return -1; + return error; git_mwindow_close(&w); idx->entry_start = entry_start; @@ -533,7 +533,7 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran return 0; } if (error < 0) - return -1; + return error; idx->have_delta = 1; } else { @@ -578,7 +578,7 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran } stats->received_objects++; - if ((error = do_progress_callback(idx, stats)) < 0) + if ((error = do_progress_callback(idx, stats)) != 0) goto on_error; } diff --git a/src/merge.c b/src/merge.c index d5bc6a39c..6f73fc14a 100644 --- a/src/merge.c +++ b/src/merge.c @@ -287,10 +287,8 @@ int git_repository_mergehead_foreach( if ((error = git_oid_fromstr(&oid, line)) < 0) goto cleanup; - if (cb(&oid, payload) != 0) { - error = giterr_user_cancel(); + if ((error = GITERR_CALLBACK( cb(&oid, payload) )) != 0) goto cleanup; - } ++line_num; } diff --git a/src/notes.c b/src/notes.c index 7e8aecbae..e3a3fccf8 100644 --- a/src/notes.c +++ b/src/notes.c @@ -582,12 +582,10 @@ int git_note_foreach( if ((error = git_note_iterator_new(&iter, repo, notes_ref)) < 0) return error; - while (!(error = git_note_next(¬e_id, &annotated_id, iter))) { - if (note_cb(¬e_id, &annotated_id, payload)) { - error = giterr_user_cancel(); - break; - } - } + while (!(error = git_note_next(¬e_id, &annotated_id, iter)) && + !(error = GITERR_CALLBACK( + note_cb(¬e_id, &annotated_id, payload)))) + /* callback for each note */; if (error == GIT_ITEROVER) error = 0; diff --git a/src/odb_loose.c b/src/odb_loose.c index ae772b425..78cd792bd 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -547,8 +547,7 @@ static int locate_object_short_oid( /* Explore directory to find a unique object matching short_oid */ error = git_path_direach( object_location, 0, fn_locate_object_short_oid, &state); - - if (error && error != GIT_EUSER) + if (error < 0 && error != GIT_EAMBIGUOUS) return error; if (!state.found) @@ -696,7 +695,6 @@ struct foreach_state { size_t dir_len; git_odb_foreach_cb cb; void *data; - git_error_state cb_error; }; GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr) @@ -735,19 +733,15 @@ static int foreach_object_dir_cb(void *_state, git_buf *path) if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0) return 0; - if (state->cb(&oid, state->data)) - return giterr_user_cancel(); - - return 0; + return giterr_set_callback( + state->cb(&oid, state->data), "git_odb_foreach"); } static int foreach_cb(void *_state, git_buf *path) { struct foreach_state *state = (struct foreach_state *) _state; - return giterr_capture( - &state->cb_error, - git_path_direach(path, 0, foreach_object_dir_cb, state)); + return git_path_direach(path, 0, foreach_object_dir_cb, state); } static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data) @@ -762,9 +756,10 @@ static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb objects_dir = backend->objects_dir; - if (git_buf_sets(&buf, objects_dir) < 0) - return -1; + git_buf_sets(&buf, objects_dir); git_path_to_dir(&buf); + if (git_buf_oom(&buf)) + return -1; memset(&state, 0, sizeof(state)); state.cb = cb; @@ -773,9 +768,6 @@ static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb error = git_path_direach(&buf, 0, foreach_cb, &state); - if (error == GIT_EUSER) - error = giterr_restore(&state.cb_error); - git_buf_free(&buf); return error; diff --git a/src/odb_pack.c b/src/odb_pack.c index 2c0319fb6..903b00d26 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -190,15 +190,9 @@ static int packfile_sort__cb(const void *a_, const void *b_) } -struct packfile_load_data { - struct pack_backend *backend; - git_error_state error; -}; - -static int packfile_load__cb(void *_data, git_buf *path) +static int packfile_load__cb(void *data, git_buf *path) { - struct packfile_load_data *data = _data; - struct pack_backend *backend = data->backend; + struct pack_backend *backend = data; struct git_pack_file *pack; const char *path_str = git_buf_cstr(path); size_t i, cmp_len = git_buf_len(path); @@ -227,7 +221,7 @@ static int packfile_load__cb(void *_data, git_buf *path) if (!error) error = git_vector_insert(&backend->packs, pack); - return giterr_capture(&data->error, error); + return error; } @@ -328,32 +322,26 @@ static int pack_entry_find_prefix( * Implement the git_odb_backend API calls * ***********************************************************/ -static int pack_backend__refresh(git_odb_backend *backend) +static int pack_backend__refresh(git_odb_backend *backend_) { - struct packfile_load_data data; int error; struct stat st; git_buf path = GIT_BUF_INIT; + struct pack_backend *backend = (struct pack_backend *)backend_; - memset(&data, 0, sizeof(data)); - data.backend = (struct pack_backend *)backend; - - if (data.backend->pack_folder == NULL) + if (backend->pack_folder == NULL) return 0; - if (p_stat(data.backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode)) + if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode)) return git_odb__error_notfound("failed to refresh packfiles", NULL); - git_buf_sets(&path, data.backend->pack_folder); + git_buf_sets(&path, backend->pack_folder); /* reload all packs */ - error = git_path_direach(&path, 0, packfile_load__cb, &data); - - if (error == GIT_EUSER) - error = giterr_restore(&data.error); + error = git_path_direach(&path, 0, packfile_load__cb, backend); git_buf_free(&path); - git_vector_sort(&data.backend->packs); + git_vector_sort(&backend->packs); return error; } diff --git a/src/pack-objects.c b/src/pack-objects.c index ac0615064..2f0007f4f 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -32,7 +32,6 @@ struct unpacked { struct tree_walk_context { git_packbuilder *pb; git_buf buf; - git_error_state error; }; struct pack_write_context { @@ -206,14 +205,18 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, po = pb->object_list + pb->nr_objects; memset(po, 0x0, sizeof(*po)); - if (git_odb_read_header(&po->size, &po->type, pb->odb, oid) < 0) - return -1; + if ((ret = git_odb_read_header(&po->size, &po->type, pb->odb, oid)) < 0) + return ret; pb->nr_objects++; git_oid_cpy(&po->id, oid); po->hash = name_hash(name); pos = kh_put(oid, pb->object_ix, &po->id, &ret); + if (ret < 0) { + giterr_set_oom(); + return ret; + } assert(ret != 0); kh_value(pb->object_ix, pos) = po; @@ -226,10 +229,9 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, if (elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) { pb->last_progress_report_time = current_time; - if (pb->progress_cb( - GIT_PACKBUILDER_ADDING_OBJECTS, - pb->nr_objects, 0, pb->progress_cb_payload)) - return giterr_user_cancel(); + return GITERR_CALLBACK( pb->progress_cb( + GIT_PACKBUILDER_ADDING_OBJECTS, + pb->nr_objects, 0, pb->progress_cb_payload) ); } } @@ -1303,7 +1305,7 @@ static int cb_tree_walk( error = git_packbuilder_insert( ctx->pb, git_tree_entry_id(entry), git_buf_cstr(&ctx->buf)); - return giterr_capture(&ctx->error, error); + return error; } int git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *oid) @@ -1331,9 +1333,6 @@ int git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *oid) !(error = git_packbuilder_insert(pb, oid, NULL))) error = git_tree_walk(tree, GIT_TREEWALK_PRE, cb_tree_walk, &context); - if (error == GIT_EUSER) - error = giterr_restore(&context.error); - git_tree_free(tree); git_buf_free(&context.buf); return error; diff --git a/src/pack.c b/src/pack.c index f69fe85e8..3f2adb2f3 100644 --- a/src/pack.c +++ b/src/pack.c @@ -1082,15 +1082,16 @@ int git_pack_foreach_entry( git_vector_foreach(&offsets, i, current) git_vector_insert(&oids, (void*)¤t[4]); } + git_vector_free(&offsets); - p->oids = (git_oid **)oids.contents; + p->oids = (git_oid **)git_vector_detach(NULL, NULL, &oids); } for (i = 0; i < p->num_objects; i++) - if (cb(p->oids[i], data)) - return giterr_user_cancel(); + if ((error = GITERR_CALLBACK( cb(p->oids[i], data) )) != 0) + break; - return 0; + return error; } static int pack_entry_find_offset( diff --git a/src/path.c b/src/path.c index 8be41c17e..857a2e61c 100644 --- a/src/path.c +++ b/src/path.c @@ -434,11 +434,11 @@ int git_path_walk_up( iter.asize = path->asize; while (scan >= stop) { - if (cb(data, &iter)) - error = giterr_user_cancel(); + error = GITERR_CALLBACK( cb(data, &iter) ); iter.ptr[scan] = oldc; - if (error < 0) + if (error) break; + scan = git_buf_rfind_next(&iter, '/'); if (scan >= 0) { scan++; @@ -874,14 +874,12 @@ int git_path_direach( if ((error = git_buf_put(path, de_path, de_len)) < 0) break; - error = fn(arg, path); + error = GITERR_CALLBACK( fn(arg, path) ); git_buf_truncate(path, wd_len); /* restore path */ - if (error) { - error = giterr_user_cancel(); + if (error) break; - } } closedir(dir); diff --git a/src/path.h b/src/path.h index 3daafd265..f26175d15 100644 --- a/src/path.h +++ b/src/path.h @@ -255,9 +255,10 @@ enum { * @param flags Combination of GIT_PATH_DIR flags. * @param callback Callback for each entry. Passed the `payload` and each * successive path inside the directory as a full path. This may - * safely append text to the pathbuf if needed. + * safely append text to the pathbuf if needed. Return non-zero to + * cancel iteration (and return value will be propagated back). * @param payload Passed to callback as first argument. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success or error code from OS error or from callback */ extern int git_path_direach( git_buf *pathbuf, @@ -288,7 +289,7 @@ extern int git_path_cmp( * original input path. * @param callback Function to invoke on each path. Passed the `payload` * and the buffer containing the current path. The path should not - * be modified in any way. + * be modified in any way. Return non-zero to stop iteration. * @param state Passed to fn as the first ath. */ extern int git_path_walk_up( diff --git a/src/push.c b/src/push.c index a314922c1..428173397 100644 --- a/src/push.c +++ b/src/push.c @@ -659,8 +659,8 @@ int git_push_status_foreach(git_push *push, unsigned int i; git_vector_foreach(&push->status, i, status) { - if (cb(status->ref, status->msg, data) < 0) - return giterr_user_cancel(); + GITERR_CHECK_ERROR( + GITERR_CALLBACK( cb(status->ref, status->msg, data) ) ); } return 0; diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 938e02a78..df7cb9d4d 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -264,14 +264,9 @@ done: return error; } -struct packed_loadloose_data { - refdb_fs_backend *backend; - git_error_state error; -}; - -static int _dirent_loose_load(void *data_, git_buf *full_path) +static int _dirent_loose_load(void *payload, git_buf *full_path) { - struct packed_loadloose_data *data = data_; + refdb_fs_backend *backend = payload; const char *file_path; if (git__suffixcmp(full_path->ptr, ".lock") == 0) @@ -279,12 +274,11 @@ static int _dirent_loose_load(void *data_, git_buf *full_path) if (git_path_isdir(full_path->ptr)) return git_path_direach( - full_path, data->backend->direach_flags, _dirent_loose_load, data); + full_path, backend->direach_flags, _dirent_loose_load, backend); - file_path = full_path->ptr + strlen(data->backend->path); + file_path = full_path->ptr + strlen(backend->path); - return giterr_capture( - &data->error, loose_lookup_to_packfile(data->backend, file_path)); + return loose_lookup_to_packfile(backend, file_path); } /* @@ -297,27 +291,20 @@ static int packed_loadloose(refdb_fs_backend *backend) { int error; git_buf refs_path = GIT_BUF_INIT; - struct packed_loadloose_data data; if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0) return -1; - memset(&data, 0, sizeof(data)); - data.backend = backend; - /* * 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, backend->direach_flags, _dirent_loose_load, &data); + &refs_path, backend->direach_flags, _dirent_loose_load, backend); git_buf_free(&refs_path); - if (error == GIT_EUSER) - error = giterr_restore(&data.error); - return error; } diff --git a/src/refs.c b/src/refs.c index 60ed9ffb1..afb067986 100644 --- a/src/refs.c +++ b/src/refs.c @@ -516,12 +516,9 @@ int git_reference_foreach( if ((error = git_reference_iterator_new(&iter, repo)) < 0) return error; - while ((error = git_reference_next(&ref, iter)) == 0) { - if (callback(ref, payload)) { - error = giterr_user_cancel(); - break; - } - } + while (!(error = git_reference_next(&ref, iter)) && + !(error = GITERR_CALLBACK( callback(ref, payload) ))) + /* callback on each reference */; if (error == GIT_ITEROVER) error = 0; @@ -542,12 +539,9 @@ int git_reference_foreach_name( if ((error = git_reference_iterator_new(&iter, repo)) < 0) return error; - while ((error = git_reference_next_name(&refname, iter)) == 0) { - if (callback(refname, payload)) { - error = giterr_user_cancel(); - break; - } - } + while (!(error = git_reference_next_name(&refname, iter)) && + !(error = GITERR_CALLBACK( callback(refname, payload) ))) + /* callback on each reference name */; if (error == GIT_ITEROVER) error = 0; @@ -569,12 +563,9 @@ int git_reference_foreach_glob( if ((error = git_reference_iterator_glob_new(&iter, repo, glob)) < 0) return error; - while ((error = git_reference_next_name(&refname, iter)) == 0) { - if (callback(refname, payload)) { - error = giterr_user_cancel(); - break; - } - } + while (!(error = git_reference_next_name(&refname, iter)) && + !(error = GITERR_CALLBACK( callback(refname, payload) ))) + /* callback on each matching reference name */; if (error == GIT_ITEROVER) error = 0; @@ -621,7 +612,9 @@ void git_reference_iterator_free(git_reference_iterator *iter) static int cb__reflist_add(const char *ref, void *data) { - return git_vector_insert((git_vector *)data, git__strdup(ref)); + char *name = git__strdup(ref); + GITERR_CHECK_ALLOC(name); + return git_vector_insert((git_vector *)data, name); } int git_reference_list( @@ -644,8 +637,8 @@ int git_reference_list( return -1; } - array->strings = (char **)ref_list.contents; - array->count = ref_list.length; + array->strings = (char **)git_vector_detach(&array->count, NULL, &ref_list); + return 0; } diff --git a/src/remote.c b/src/remote.c index 93d243001..307306d1a 100644 --- a/src/remote.c +++ b/src/remote.c @@ -251,14 +251,12 @@ int git_remote_create_inmemory(git_remote **out, git_repository *repo, const cha struct refspec_cb_data { git_remote *remote; int fetch; - git_error_state error; }; static int refspec_cb(const git_config_entry *entry, void *payload) { struct refspec_cb_data *data = (struct refspec_cb_data *)payload; - return giterr_capture( - &data->error, add_refspec(data->remote, entry->value, data->fetch)); + return add_refspec(data->remote, entry->value, data->fetch); } static int get_optional_config( @@ -316,17 +314,15 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) if ((error = get_check_cert(&remote->check_cert, repo)) < 0) goto cleanup; - if ((git_vector_init(&remote->refs, 32, NULL) < 0) || - (git_vector_init(&remote->refspecs, 2, NULL) < 0) || - (git_vector_init(&remote->active_refspecs, 2, NULL) < 0)) { + if (git_vector_init(&remote->refs, 32, NULL) < 0 || + git_vector_init(&remote->refspecs, 2, NULL) < 0 || + git_vector_init(&remote->active_refspecs, 2, NULL) < 0) { error = -1; goto cleanup; } - if (git_buf_printf(&buf, "remote.%s.url", name) < 0) { - error = -1; + if ((error = git_buf_printf(&buf, "remote.%s.url", name)) < 0) goto cleanup; - } if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0) goto cleanup; @@ -387,9 +383,6 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) cleanup: git_buf_free(&buf); - if (error == GIT_EUSER) - error = giterr_restore(&data.error); - if (error < 0) git_remote_free(remote); @@ -636,7 +629,7 @@ int git_remote_connect(git_remote *remote, git_direction direction) if (!remote->check_cert) flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT; - if ((error = t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags)) < 0) + if ((error = t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags)) != 0) goto on_error; remote->transport = t; @@ -788,7 +781,7 @@ int git_remote_download(git_remote *remote) git_vector_free(&refs); if (error < 0) - return -1; + return error; if ((error = git_fetch_negotiate(remote)) < 0) return error; @@ -801,10 +794,10 @@ int git_remote_fetch(git_remote *remote) int error; /* Connect and download everything */ - if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) < 0) + if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) != 0) return error; - if ((error = git_remote_download(remote)) < 0) + if ((error = git_remote_download(remote)) != 0) return error; /* We don't need to be connected anymore */ @@ -1032,7 +1025,6 @@ int git_remote_update_tips(git_remote *remote) int error; size_t i; - if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) return -1; @@ -1112,14 +1104,9 @@ void git_remote_free(git_remote *remote) git__free(remote); } -struct remote_list_data { - git_vector list; - git_error_state error; -}; - static int remote_list_cb(const git_config_entry *entry, void *payload) { - struct remote_list_data *data = payload; + git_vector *list = payload; const char *name = entry->name + strlen("remote."); size_t namelen = strlen(name); char *remote_name; @@ -1130,42 +1117,35 @@ static int remote_list_cb(const git_config_entry *entry, void *payload) remote_name = git__strndup(name, namelen - 4); /* strip ".url" */ else remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */ - if (!remote_name) - return giterr_capture(&data->error, -1); + GITERR_CHECK_ALLOC(remote_name); - return giterr_capture( - &data->error, git_vector_insert(&data->list, remote_name)); + return git_vector_insert(list, remote_name); } int git_remote_list(git_strarray *remotes_list, git_repository *repo) { int error; git_config *cfg; - struct remote_list_data data; + git_vector list = GIT_VECTOR_INIT; if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) return error; - memset(&data, 0, sizeof(data)); - if ((error = git_vector_init(&data.list, 4, git__strcmp_cb)) < 0) + if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0) return error; error = git_config_foreach_match( - cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &data); - - /* cb error is converted to GIT_EUSER by git_config_foreach */ - if (error == GIT_EUSER) - error = giterr_restore(&data.error); + cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list); if (error < 0) { - git_vector_free_all(&data.list); + git_vector_free_all(&list); return error; } - git_vector_uniq(&data.list, git__free); + git_vector_uniq(&list, git__free); - remotes_list->strings = (char **)data.list.contents; - remotes_list->count = data.list.length; + remotes_list->strings = + (char **)git_vector_detach(&remotes_list->count, NULL, &list); return 0; } @@ -1256,7 +1236,6 @@ struct update_data { git_config *config; const char *old_remote_name; const char *new_remote_name; - git_error_state error; }; static int update_config_entries_cb( @@ -1268,9 +1247,8 @@ static int update_config_entries_cb( if (strcmp(entry->value, data->old_remote_name)) return 0; - return giterr_capture( - &data->error, git_config_set_string( - data->config, entry->name, data->new_remote_name)); + return git_config_set_string( + data->config, entry->name, data->new_remote_name); } static int update_branch_remote_config_entry( @@ -1287,13 +1265,8 @@ static int update_branch_remote_config_entry( data.old_remote_name = old_name; data.new_remote_name = new_name; - error = git_config_foreach_match( + return git_config_foreach_match( data.config, "branch\\..+\\.remote", update_config_entries_cb, &data); - - if (error == GIT_EUSER) - error = giterr_restore(&data.error); - - return error; } static int rename_one_remote_reference( @@ -1357,13 +1330,14 @@ static int rename_fetch_refspecs( git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT; const git_refspec *spec; size_t i; - int error = -1; - - if (git_buf_printf(&base, "+refs/heads/*:refs/remotes/%s/*", remote->name) < 0) - goto cleanup; + int error = 0; if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0) - goto cleanup; + return error; + + if ((error = git_buf_printf( + &base, "+refs/heads/*:refs/remotes/%s/*", remote->name)) < 0) + return error; git_vector_foreach(&remote->refspecs, i, spec) { if (spec->push) @@ -1374,10 +1348,9 @@ static int rename_fetch_refspecs( if (!remote->name || strcmp(git_buf_cstr(&base), spec->string)) { - if (callback(spec->string, payload) < 0) { - error = giterr_user_cancel(); - goto cleanup; - } + error = GITERR_CALLBACK( callback(spec->string, payload) ); + if (error) + break; continue; } @@ -1391,15 +1364,14 @@ static int rename_fetch_refspecs( git_buf_printf(&var, "remote.%s.fetch", new_name) < 0) { error = -1; - goto cleanup; + break; } if ((error = git_config_set_string( config, git_buf_cstr(&var), git_buf_cstr(&val))) < 0) - goto cleanup; + break; } -cleanup: git_buf_free(&base); git_buf_free(&var); git_buf_free(&val); diff --git a/src/repository.c b/src/repository.c index 443744504..94f6603aa 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1608,15 +1608,14 @@ static int at_least_one_cb(const char *refname, void *payload) { GIT_UNUSED(refname); GIT_UNUSED(payload); - - return GIT_EUSER; + return GIT_PASSTHROUGH; } static int repo_contains_no_reference(git_repository *repo) { int error = git_reference_foreach_name(repo, &at_least_one_cb, NULL); - if (error == GIT_EUSER) + if (error == GIT_PASSTHROUGH) return 0; if (!error) diff --git a/src/revwalk.c b/src/revwalk.c index e8c7f23ec..c0a053211 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -169,15 +169,12 @@ static int push_ref(git_revwalk *walk, const char *refname, int hide) struct push_cb_data { git_revwalk *walk; int hide; - git_error_state error; }; static int push_glob_cb(const char *refname, void *data_) { struct push_cb_data *data = (struct push_cb_data *)data_; - - return giterr_capture( - &data->error, push_ref(data->walk, refname, data->hide) ); + return push_ref(data->walk, refname, data->hide); } static int push_glob(git_revwalk *walk, const char *glob, int hide) @@ -204,12 +201,9 @@ static int push_glob(git_revwalk *walk, const char *glob, int hide) data.walk = walk; data.hide = hide; - memset(&data.error, 0, sizeof(data.error)); error = git_reference_foreach_glob( walk->repo, git_buf_cstr(&buf), push_glob_cb, &data); - if (error == GIT_EUSER) - error = giterr_restore(&data.error); git_buf_free(&buf); return error; diff --git a/src/stash.c b/src/stash.c index 06a7b9a33..458a1e175 100644 --- a/src/stash.c +++ b/src/stash.c @@ -440,7 +440,7 @@ static int is_dirty_cb(const char *path, unsigned int status, void *payload) GIT_UNUSED(status); GIT_UNUSED(payload); - return 1; + return GIT_PASSTHROUGH; } static int ensure_there_are_changes_to_stash( @@ -463,7 +463,7 @@ static int ensure_there_are_changes_to_stash( error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL); - if (error == GIT_EUSER) + if (error == GIT_PASSTHROUGH) return 0; if (!error) @@ -582,13 +582,13 @@ int git_stash_foreach( for (i = 0; i < max; i++) { entry = git_reflog_entry_byindex(reflog, i); - if (callback(i, - git_reflog_entry_message(entry), - git_reflog_entry_id_new(entry), - payload)) { - error = giterr_user_cancel(); + error = GITERR_CALLBACK( + callback(i, + git_reflog_entry_message(entry), + git_reflog_entry_id_new(entry), + payload) ); + if (error) break; - } } cleanup: diff --git a/src/status.c b/src/status.c index ce571a2d8..d76617a72 100644 --- a/src/status.c +++ b/src/status.c @@ -152,32 +152,25 @@ static git_status_t status_compute( return st; } -struct status_data { - git_status_list *status; - git_error_state err; -}; - static int status_collect( git_diff_delta *head2idx, git_diff_delta *idx2wd, void *payload) { - struct status_data *data = payload; + git_status_list *status = payload; git_status_entry *status_entry; - if (!status_is_included(data->status, head2idx, idx2wd)) + if (!status_is_included(status, head2idx, idx2wd)) return 0; status_entry = git__malloc(sizeof(git_status_entry)); - if (!status_entry) - return giterr_capture(&data->err, -1); + GITERR_CHECK_ALLOC(status_entry); - status_entry->status = status_compute(data->status, head2idx, idx2wd); + status_entry->status = status_compute(status, head2idx, idx2wd); status_entry->head_to_index = head2idx; status_entry->index_to_workdir = idx2wd; - return giterr_capture( - &data->err, git_vector_insert(&data->status->paired, status_entry)); + return git_vector_insert(&status->paired, status_entry); } GIT_INLINE(int) status_entry_cmp_base( @@ -321,18 +314,10 @@ int git_status_list_new( goto done; } - { - struct status_data data = { 0 }; - data.status = status; - - error = git_diff__paired_foreach( - status->head2idx, status->idx2wd, status_collect, &data); - - if (error == GIT_EUSER) - error = giterr_restore(&data.err); - if (error < 0) - goto done; - } + error = git_diff__paired_foreach( + status->head2idx, status->idx2wd, status_collect, status); + if (error < 0) + goto done; if (flags & GIT_STATUS_OPT_SORT_CASE_SENSITIVELY) git_vector_set_cmp(&status->paired, status_entry_cmp); @@ -407,10 +392,9 @@ int git_status_foreach_ext( status_entry->head_to_index->old_file.path : status_entry->index_to_workdir->old_file.path; - if (cb(path, status_entry->status, payload) != 0) { - error = giterr_user_cancel(); + error = GITERR_CALLBACK( cb(path, status_entry->status, payload) ); + if (error) break; - } } git_status_list_free(status); diff --git a/src/submodule.c b/src/submodule.c index 15c87c0b4..e9d534ae8 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -71,11 +71,6 @@ __KHASH_IMPL( str, static kh_inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash); -struct submodule_callback_payload { - git_repository *repo; - git_error_state error; -}; - static int load_submodule_config(git_repository *repo); static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *); static int lookup_head_remote(git_buf *url, git_repository *repo); @@ -173,10 +168,8 @@ int git_submodule_foreach( break; } - if (callback(sm, sm->name, payload)) { - error = giterr_user_cancel(); + if ((error = GITERR_CALLBACK(callback(sm, sm->name, payload))) != 0) break; - } }); git_vector_free(&seen); @@ -825,7 +818,6 @@ int git_submodule_reload(git_submodule *submodule) { int error = 0; git_config_backend *mods; - struct submodule_callback_payload p; assert(submodule); @@ -838,9 +830,6 @@ int git_submodule_reload(git_submodule *submodule) return error; /* refresh config data */ - memset(&p, 0, sizeof(p)); - p.repo = submodule->repo; - mods = open_gitmodules(submodule->repo, false, NULL); if (mods != NULL) { git_buf path = GIT_BUF_INIT; @@ -851,13 +840,9 @@ int git_submodule_reload(git_submodule *submodule) if (git_buf_oom(&path)) error = -1; - else { + else error = git_config_file_foreach_match( - mods, path.ptr, submodule_load_from_config, &p); - - if (error == GIT_EUSER) - error = giterr_restore(&p.error); - } + mods, path.ptr, submodule_load_from_config, submodule->repo); git_buf_free(&path); git_config_file_free(mods); @@ -867,15 +852,11 @@ int git_submodule_reload(git_submodule *submodule) } /* refresh wd data */ - submodule->flags = submodule->flags & ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID); - error = submodule_load_from_wd_lite(submodule, submodule->path, &p); - if (error) - error = giterr_restore(&p.error); - - return error; + return submodule_load_from_wd_lite( + submodule, submodule->path, submodule->repo); } static void submodule_copy_oid_maybe( @@ -1100,8 +1081,8 @@ int git_submodule_parse_update(git_submodule_update_t *out, const char *value) static int submodule_load_from_config( const git_config_entry *entry, void *payload) { - struct submodule_callback_payload *p = payload; - git_strmap *smcfg = p->repo->submodules; + git_repository *repo = payload; + git_strmap *smcfg = repo->submodules; const char *namestart, *property, *alternate = NULL; const char *key = entry->name, *value = entry->value, *path; git_buf name = GIT_BUF_INIT; @@ -1121,7 +1102,7 @@ static int submodule_load_from_config( path = !strcasecmp(property, "path") ? value : NULL; if ((error = git_buf_set(&name, namestart, property - namestart - 1)) < 0 || - (error = submodule_get(&sm, p->repo, name.ptr, path)) < 0) + (error = submodule_get(&sm, repo, name.ptr, path)) < 0) goto done; sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; @@ -1197,22 +1178,21 @@ static int submodule_load_from_config( done: git_buf_free(&name); - return giterr_capture(&p->error, error); + return error; } static int submodule_load_from_wd_lite( git_submodule *sm, const char *name, void *payload) { - struct submodule_callback_payload *p = payload; git_buf path = GIT_BUF_INIT; - GIT_UNUSED(name); + GIT_UNUSED(name); GIT_UNUSED(payload); if (git_repository_is_bare(sm->repo)) return 0; if (git_buf_joinpath(&path, git_repository_workdir(sm->repo), sm->path) < 0) - return giterr_capture(&p->error, -1); + return -1; if (git_path_isdir(path.ptr)) sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED; @@ -1355,14 +1335,11 @@ static int load_submodule_config(git_repository *repo) int error; git_oid gitmodules_oid; git_config_backend *mods = NULL; - struct submodule_callback_payload p; if (repo->submodules) return 0; memset(&gitmodules_oid, 0, sizeof(gitmodules_oid)); - memset(&p, 0, sizeof(p)); - p.repo = repo; /* Submodule data is kept in a hashtable keyed by both name and path. * These are usually the same, but that is not guaranteed. @@ -1386,21 +1363,18 @@ static int load_submodule_config(git_repository *repo) if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL && (error = git_config_file_foreach( - mods, submodule_load_from_config, &p)) < 0) + mods, submodule_load_from_config, repo)) < 0) goto cleanup; /* shallow scan submodules in work tree */ if (!git_repository_is_bare(repo)) - error = git_submodule_foreach(repo, submodule_load_from_wd_lite, &p); + error = git_submodule_foreach(repo, submodule_load_from_wd_lite, NULL); cleanup: if (mods != NULL) git_config_file_free(mods); - if (error == GIT_EUSER) - error = giterr_restore(&p.error); - if (error) git_submodule_config_free(repo); diff --git a/src/tag.c b/src/tag.c index 5d4e45e5d..adf2819d7 100644 --- a/src/tag.c +++ b/src/tag.c @@ -414,7 +414,6 @@ typedef struct { git_repository *repo; git_tag_foreach_cb cb; void *cb_data; - git_error_state error; } tag_cb_data; static int tags_cb(const char *ref, void *data) @@ -427,16 +426,15 @@ static int tags_cb(const char *ref, void *data) return 0; /* no tag */ if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) { - if (d->cb(ref, &oid, d->cb_data)) - error = giterr_user_cancel(); + error = d->cb(ref, &oid, d->cb_data); + giterr_set_callback(error, "git_tag_foreach"); } - return giterr_capture(&d->error, error); + return error; } int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data) { - int error; tag_cb_data data; assert(repo && cb); @@ -444,14 +442,8 @@ int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data) data.cb = cb; data.cb_data = cb_data; data.repo = repo; - memset(&data.error, 0, sizeof(data.error)); - - error = git_reference_foreach_name(repo, &tags_cb, &data); - - if (error == GIT_EUSER) - error = giterr_restore(&data.error); - return error; + return git_reference_foreach_name(repo, &tags_cb, &data); } typedef struct { @@ -470,8 +462,8 @@ static int tag_list_cb(const char *tag_name, git_oid *oid, void *data) p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0) { char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN); - if (!matched) - return -1; + GITERR_CHECK_ALLOC(matched); + return git_vector_insert(filter->taglist, matched); } @@ -494,19 +486,12 @@ int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_reposit error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter); - /* the only case where callback will return an error is oom */ - if (error == GIT_EUSER) { - giterr_set_oom(); - error = -1; - } - - if (error < 0) { + if (error < 0) git_vector_free(&taglist); - return error; - } - tag_names->strings = (char **)taglist.contents; - tag_names->count = taglist.length; + tag_names->strings = + (char **)git_vector_detach(&tag_names->count, NULL, &taglist); + return 0; } diff --git a/src/transports/http.c b/src/transports/http.c index ace0d97d0..0e1bbf60d 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -382,9 +382,6 @@ static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len) static void clear_parser_state(http_subtransport *t) { - unsigned i; - char *entry; - http_parser_init(&t->parser, HTTP_RESPONSE); gitno_buffer_setup(&t->socket, &t->parse_buffer, @@ -407,10 +404,7 @@ static void clear_parser_state(http_subtransport *t) git__free(t->location); t->location = NULL; - git_vector_foreach(&t->www_authenticate, i, entry) - git__free(entry); - - git_vector_free(&t->www_authenticate); + git_vector_free_all(&t->www_authenticate); } static int write_chunk(gitno_socket *socket, const char *buffer, size_t len) diff --git a/src/transports/local.c b/src/transports/local.c index f09e797ce..4635d5dd3 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -43,43 +43,43 @@ typedef struct { static int add_ref(transport_local *t, const char *name) { const char peeled[] = "^{}"; + git_oid head_oid; git_remote_head *head; git_object *obj = NULL, *target = NULL; git_buf buf = GIT_BUF_INIT; int error; - head = git__calloc(1, sizeof(git_remote_head)); - GITERR_CHECK_ALLOC(head); - - head->name = git__strdup(name); - GITERR_CHECK_ALLOC(head->name); - - error = git_reference_name_to_id(&head->oid, t->repo, name); + error = git_reference_name_to_id(&head_oid, t->repo, name); if (error < 0) { - git__free(head->name); - git__free(head); if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) { - /* This is actually okay. Empty repos often have a HEAD that points to - * a nonexistent "refs/heads/master". */ + /* This is actually okay. Empty repos often have a HEAD that + * points to a nonexistent "refs/heads/master". */ giterr_clear(); return 0; } return error; } - if (git_vector_insert(&t->refs, head) < 0) - { + head = git__calloc(1, sizeof(git_remote_head)); + GITERR_CHECK_ALLOC(head); + + head->name = git__strdup(name); + GITERR_CHECK_ALLOC(head->name); + + git_oid_cpy(&head->oid, &head_oid); + + if ((error = git_vector_insert(&t->refs, head)) < 0) { git__free(head->name); git__free(head); - return -1; + return error; } /* If it's not a tag, we don't need to try to peel it */ if (git__prefixcmp(name, GIT_REFS_TAGS_DIR)) return 0; - if (git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY) < 0) - return -1; + if ((error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY)) < 0) + return error; head = NULL; @@ -94,27 +94,24 @@ static int add_ref(transport_local *t, const char *name) /* And if it's a tag, peel it, and add it to the list */ head = git__calloc(1, sizeof(git_remote_head)); GITERR_CHECK_ALLOC(head); + if (git_buf_join(&buf, 0, name, peeled) < 0) return -1; - head->name = git_buf_detach(&buf); - if (git_tag_peel(&target, (git_tag *) obj) < 0) - goto on_error; + if (!(error = git_tag_peel(&target, (git_tag *)obj))) { + git_oid_cpy(&head->oid, git_object_id(target)); - git_oid_cpy(&head->oid, git_object_id(target)); - git_object_free(obj); - git_object_free(target); - - if (git_vector_insert(&t->refs, head) < 0) - return -1; - - return 0; + if ((error = git_vector_insert(&t->refs, head)) < 0) { + git__free(head->name); + git__free(head); + } + } -on_error: git_object_free(obj); git_object_free(target); - return -1; + + return error; } static int store_refs(transport_local *t) @@ -222,7 +219,7 @@ static int local_ls(const git_remote_head ***out, size_t *size, git_transport *t return -1; } - *out = (const git_remote_head **) t->refs.contents; + *out = (const git_remote_head **)t->refs.contents; *size = t->refs.length; return 0; @@ -529,7 +526,7 @@ static int local_download_pack( } } - if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0) + if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0) goto cleanup; /* Write the data to the ODB */ @@ -540,7 +537,7 @@ static int local_download_pack( data.progress_payload = progress_payload; data.writepack = writepack; - if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) < 0) + if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0) goto cleanup; } error = writepack->commit(writepack, stats); diff --git a/src/transports/smart.c b/src/transports/smart.c index e298f3510..69eaf9b78 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -23,12 +23,13 @@ static int git_smart__recv_cb(gitno_buffer *buf) buf->offset += bytes_read; - if (t->packetsize_cb && !t->cancelled.val) - if (t->packetsize_cb(bytes_read, t->packetsize_payload)) { + if (t->packetsize_cb && !t->cancelled.val) { + error = t->packetsize_cb(bytes_read, t->packetsize_payload); + if (error) { git_atomic_set(&t->cancelled, 1); - - return giterr_user_cancel(); + return GIT_EUSER; } + } return (int)(buf->offset - old_len); } @@ -341,7 +342,7 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param) t->parent.is_connected = git_smart__is_connected; t->parent.read_flags = git_smart__read_flags; t->parent.cancel = git_smart__cancel; - + t->owner = owner; t->rpc = definition->rpc; @@ -358,7 +359,7 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param) if (definition->callback(&t->wrapped, &t->parent) < 0) { git__free(t); return -1; - } + } *out = (git_transport *) t; return 0; diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index a4046ee43..dd9b5e0ed 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -45,7 +45,7 @@ int git_smart__store_refs(transport_smart *t, int flushes) error = GIT_EBUFS; if (error < 0 && error != GIT_EBUFS) - return -1; + return error; if (error == GIT_EBUFS) { if ((recvd = gitno_recv(buf)) < 0) @@ -209,12 +209,13 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo) git_strarray refs; unsigned int i; git_reference *ref; + int error; - if (git_reference_list(&refs, repo) < 0) - return -1; + if ((error = git_reference_list(&refs, repo)) < 0) + return error; - if (git_revwalk_new(&walk, repo) < 0) - return -1; + if ((error = git_revwalk_new(&walk, repo)) < 0) + return error; git_revwalk_sorting(walk, GIT_SORT_TIME); @@ -223,13 +224,13 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo) if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR)) continue; - if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0) + if ((error = 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_target(ref)) < 0) + if ((error = git_revwalk_push(walk, git_reference_target(ref))) < 0) goto on_error; git_reference_free(ref); @@ -242,7 +243,7 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo) on_error: git_reference_free(ref); git_strarray_free(&refs); - return -1; + return error; } static int wait_while_ack(gitno_buffer *buf) @@ -503,7 +504,7 @@ int git_smart__download_pack( } if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 || - ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0)) + ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0)) goto done; /* @@ -539,11 +540,9 @@ int git_smart__download_pack( if (pkt->type == GIT_PKT_PROGRESS) { if (t->progress_cb) { git_pkt_progress *p = (git_pkt_progress *) pkt; - if (t->progress_cb(p->data, p->len, t->message_cb_payload)) { - giterr_clear(); - error = GIT_EUSER; + error = t->progress_cb(p->data, p->len, t->message_cb_payload); + if (error) goto done; - } } git__free(pkt); } else if (pkt->type == GIT_PKT_DATA) { @@ -551,7 +550,7 @@ int git_smart__download_pack( error = writepack->append(writepack, p->data, p->len, stats); git__free(pkt); - if (error < 0) + if (error != 0) goto done; } else if (pkt->type == GIT_PKT_FLUSH) { /* A flush indicates the end of the packfile */ @@ -564,17 +563,15 @@ int git_smart__download_pack( * Trailing execution of progress_cb, if necessary... * Only the callback through the npp datastructure currently * updates the last_fired_bytes value. It is possible that - * progress has already been reported with the correct + * progress has already been reported with the correct * "received_bytes" value, but until (if?) this is unified * then we will report progress again to be sure that the * correct last received_bytes value is reported. */ if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes) { - if (npp.callback(npp.stats, npp.payload) < 0) { - giterr_clear(); - error = GIT_EUSER; + error = npp.callback(npp.stats, npp.payload); + if (error != 0) goto done; - } } error = writepack->commit(writepack, stats); diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 673cd0faf..e47e19cca 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -667,9 +667,11 @@ replay: if (allowed_types && (!t->cred || 0 == (t->cred->credtype & allowed_types))) { - if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->connection_data.user, allowed_types, - t->owner->cred_acquire_payload) < 0) - return GIT_EUSER; + int error = t->owner->cred_acquire_cb( + &t->cred, t->owner->url, t->connection_data.user, + allowed_types, t->owner->cred_acquire_payload); + if (error < 0) + return error; assert(t->cred); diff --git a/src/tree.c b/src/tree.c index 8ded007eb..5f35ac3a8 100644 --- a/src/tree.c +++ b/src/tree.c @@ -883,9 +883,8 @@ static int tree_walk( git_vector_foreach(&tree->entries, i, entry) { if (preorder) { - error = callback(path->ptr, entry, payload); - if (error < 0) - return giterr_user_cancel(); + if ((error = callback(path->ptr, entry, payload)) < 0) + return giterr_set_callback(error, "git_tree_walk"); if (error > 0) { error = 0; continue; @@ -896,8 +895,8 @@ static int tree_walk( git_tree *subtree; size_t path_len = git_buf_len(path); - if ((error = git_tree_lookup( - &subtree, tree->object.repo, &entry->oid)) < 0) + error = git_tree_lookup(&subtree, tree->object.repo, &entry->oid); + if (error < 0) break; /* append the next entry to the path */ @@ -905,19 +904,22 @@ static int tree_walk( git_buf_putc(path, '/'); if (git_buf_oom(path)) - return -1; + error = -1; + else + error = tree_walk(subtree, callback, path, payload, preorder); - error = tree_walk(subtree, callback, path, payload, preorder); git_tree_free(subtree); - if (error != 0) break; git_buf_truncate(path, path_len); } - if (!preorder && callback(path->ptr, entry, payload) < 0) - return giterr_user_cancel(); + if (!preorder) { + if ((error = callback(path->ptr, entry, payload)) < 0) + return giterr_set_callback(error, "git_tree_walk"); + error = 0; + } } return error; diff --git a/src/vector.c b/src/vector.c index 9e217b831..b1ea89606 100644 --- a/src/vector.c +++ b/src/vector.c @@ -104,6 +104,22 @@ int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp) return resize_vector(v, max(initial_size, MIN_ALLOCSIZE)); } +void **git_vector_detach(size_t *size, size_t *asize, git_vector *v) +{ + void **data = v->contents; + + if (size) + *size = v->length; + if (asize) + *asize = v->_alloc_size; + + v->_alloc_size = 0; + v->length = 0; + v->contents = NULL; + + return data; +} + int git_vector_insert(git_vector *v, void *element) { assert(v); diff --git a/src/vector.h b/src/vector.h index c6d3e9d55..defe22466 100644 --- a/src/vector.h +++ b/src/vector.h @@ -28,6 +28,8 @@ void git_vector_clear(git_vector *v); int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp); void git_vector_swap(git_vector *a, git_vector *b); +void **git_vector_detach(size_t *size, size_t *asize, git_vector *v); + void git_vector_sort(git_vector *v); /** Linear search for matching entry using internal comparison function */ diff --git a/tests/attr/repo.c b/tests/attr/repo.c index ef2ad5ce9..f9ba585fb 100644 --- a/tests/attr/repo.c +++ b/tests/attr/repo.c @@ -129,6 +129,8 @@ static int count_attrs( return 0; } +#define CANCEL_VALUE 12345 + static int cancel_iteration( const char *name, const char *value, @@ -140,7 +142,7 @@ static int cancel_iteration( *((int *)payload) -= 1; if (*((int *)payload) < 0) - return -1; + return CANCEL_VALUE; return 0; } @@ -166,7 +168,7 @@ void test_attr_repo__foreach(void) count = 2; cl_assert_equal_i( - GIT_EUSER, git_attr_foreach( + CANCEL_VALUE, git_attr_foreach( g_repo, 0, "sub/subdir_test1", &cancel_iteration, &count) ); } diff --git a/tests/config/read.c b/tests/config/read.c index abc088d59..25672729f 100644 --- a/tests/config/read.c +++ b/tests/config/read.c @@ -247,7 +247,7 @@ void test_config_read__foreach(void) count = 3; cl_git_fail(ret = git_config_foreach(cfg, cfg_callback_countdown, &count)); - cl_assert_equal_i(GIT_EUSER, ret); + cl_assert_equal_i(-100, ret); git_config_free(cfg); } diff --git a/tests/config/rename.c b/tests/config/rename.c index 29ade7b00..db07c798f 100644 --- a/tests/config/rename.c +++ b/tests/config/rename.c @@ -44,7 +44,6 @@ void test_config_rename__can_rename(void) void test_config_rename__prevent_overwrite(void) { const git_config_entry *ce; - const git_error *err; cl_git_pass(git_config_set_string( g_config, "branch.local-track.remote", "yellow")); @@ -60,8 +59,12 @@ void test_config_rename__prevent_overwrite(void) &ce, g_config, "branch.local-track.remote")); cl_assert_equal_s(".", ce->value); -// cl_assert((err = giterr_last()) != NULL); -// cl_assert(err->message != NULL); + /* so, we don't currently prevent overwrite... */ + /* { + const git_error *err; + cl_assert((err = giterr_last()) != NULL); + cl_assert(err->message != NULL); + } */ } static void assert_invalid_config_section_name( diff --git a/tests/core/path.c b/tests/core/path.c index 3858e30dd..471491b87 100644 --- a/tests/core/path.c +++ b/tests/core/path.c @@ -354,13 +354,15 @@ typedef struct { char **expect; } check_walkup_info; +#define CANCEL_VALUE 1234 + static int check_one_walkup_step(void *ref, git_buf *path) { check_walkup_info *info = (check_walkup_info *)ref; if (!info->cancel_after) { cl_assert_equal_s(info->expect[info->expect_idx], "[CANCEL]"); - return -1; + return CANCEL_VALUE; } info->cancel_after--; @@ -435,7 +437,7 @@ void test_core_path__11a_walkup_cancel(void) info.expect_idx = i; cl_assert_equal_i( - GIT_EUSER, + CANCEL_VALUE, git_path_walk_up(&p, root[j], check_one_walkup_step, &info) ); diff --git a/tests/diff/index.c b/tests/diff/index.c index 8f4567137..21afe8da2 100644 --- a/tests/diff/index.c +++ b/tests/diff/index.c @@ -128,9 +128,7 @@ void test_diff_index__1(void) cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts)); cl_assert_equal_i( - GIT_EUSER, - git_diff_foreach(diff, diff_stop_after_2_files, NULL, NULL, &exp) - ); + 1, git_diff_foreach(diff, diff_stop_after_2_files, NULL, NULL, &exp) ); cl_assert_equal_i(2, exp.files); diff --git a/tests/diff/notify.c b/tests/diff/notify.c index cc33cb71c..f6accd004 100644 --- a/tests/diff/notify.c +++ b/tests/diff/notify.c @@ -20,7 +20,7 @@ static int assert_called_notifications( { bool found = false; notify_expected *exp = (notify_expected*)payload; - notify_expected *e;; + notify_expected *e; GIT_UNUSED(diff_so_far); diff --git a/tests/diff/rename.c b/tests/diff/rename.c index ca6d076d6..93e69f479 100644 --- a/tests/diff/rename.c +++ b/tests/diff/rename.c @@ -111,6 +111,28 @@ void test_diff_rename__match_oid(void) git_diff_free(diff); + cl_git_pass(git_diff_tree_to_tree( + &diff, g_repo, old_tree, new_tree, &diffopts)); + + /* git diff --find-copies-harder -M100 -B100 \ + * 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \ + * 2bc7f351d20b53f1c72c16c4b036e491c478c49a + */ + opts.flags = GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED | + GIT_DIFF_FIND_EXACT_MATCH_ONLY; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + + git_diff_free(diff); + git_tree_free(old_tree); git_tree_free(new_tree); } diff --git a/tests/notes/notes.c b/tests/notes/notes.c index 82dcaf8ca..c2579a2c4 100644 --- a/tests/notes/notes.c +++ b/tests/notes/notes.c @@ -129,7 +129,7 @@ void test_notes_notes__can_cancel_foreach(void) create_note(¬e_oid4, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 9fd7 and 4a20\n"); cl_assert_equal_i( - GIT_EUSER, + 1, git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_cancel_cb, &retrieved_notes)); } diff --git a/tests/object/tree/walk.c b/tests/object/tree/walk.c index 1207e864c..f8005e579 100644 --- a/tests/object/tree/walk.c +++ b/tests/object/tree/walk.c @@ -59,7 +59,7 @@ static int treewalk_stop_cb( (*count) += 1; - return (*count == 2) ? -1 : 0; + return (*count == 2) ? -123 : 0; } static int treewalk_stop_immediately_cb( @@ -83,20 +83,20 @@ void test_object_tree_walk__1(void) ct = 0; cl_assert_equal_i( - GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_stop_cb, &ct)); + -123, git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_stop_cb, &ct)); cl_assert_equal_i(2, ct); ct = 0; cl_assert_equal_i( - GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_stop_cb, &ct)); + -123, git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_stop_cb, &ct)); cl_assert_equal_i(2, ct); cl_assert_equal_i( - GIT_EUSER, git_tree_walk( + -100, git_tree_walk( tree, GIT_TREEWALK_PRE, treewalk_stop_immediately_cb, NULL)); cl_assert_equal_i( - GIT_EUSER, git_tree_walk( + -100, git_tree_walk( tree, GIT_TREEWALK_POST, treewalk_stop_immediately_cb, NULL)); git_tree_free(tree); @@ -152,7 +152,7 @@ void test_object_tree_walk__2(void) memset(&data, 0, sizeof(data)); data.stop = "3.txt"; - cl_assert_equal_i(GIT_EUSER, git_tree_walk( + cl_assert_equal_i(-1, git_tree_walk( tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data)); cl_assert_equal_i(3, data.files); cl_assert_equal_i(2, data.dirs); @@ -168,7 +168,7 @@ void test_object_tree_walk__2(void) memset(&data, 0, sizeof(data)); data.stop = "new.txt"; - cl_assert_equal_i(GIT_EUSER, git_tree_walk( + cl_assert_equal_i(-1, git_tree_walk( tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data)); cl_assert_equal_i(7, data.files); cl_assert_equal_i(4, data.dirs); diff --git a/tests/odb/foreach.c b/tests/odb/foreach.c index ebb8866f7..256ae9cd7 100644 --- a/tests/odb/foreach.c +++ b/tests/odb/foreach.c @@ -5,7 +5,6 @@ static git_odb *_odb; static git_repository *_repo; -static int nobj; void test_odb_foreach__cleanup(void) { @@ -18,10 +17,10 @@ void test_odb_foreach__cleanup(void) static int foreach_cb(const git_oid *oid, void *data) { - GIT_UNUSED(data); - GIT_UNUSED(oid); + int *nobj = data; + (*nobj)++; - nobj++; + GIT_UNUSED(oid); return 0; } @@ -38,43 +37,46 @@ static int foreach_cb(const git_oid *oid, void *data) */ void test_odb_foreach__foreach(void) { + int nobj = 0; + cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git"))); git_repository_odb(&_odb, _repo); - cl_git_pass(git_odb_foreach(_odb, foreach_cb, NULL)); + cl_git_pass(git_odb_foreach(_odb, foreach_cb, &nobj)); cl_assert_equal_i(47 + 1640, nobj); /* count + in-pack */ } void test_odb_foreach__one_pack(void) { git_odb_backend *backend = NULL; + int nobj = 0; cl_git_pass(git_odb_new(&_odb)); cl_git_pass(git_odb_backend_one_pack(&backend, cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx"))); cl_git_pass(git_odb_add_backend(_odb, backend, 1)); _repo = NULL; - nobj = 0; - cl_git_pass(git_odb_foreach(_odb, foreach_cb, NULL)); + cl_git_pass(git_odb_foreach(_odb, foreach_cb, &nobj)); cl_assert(nobj == 1628); } static int foreach_stop_cb(const git_oid *oid, void *data) { - GIT_UNUSED(data); - GIT_UNUSED(oid); + int *nobj = data; + (*nobj)++; - nobj++; + GIT_UNUSED(oid); - return (nobj == 1000); + return (*nobj == 1000) ? -321 : 0; } void test_odb_foreach__interrupt_foreach(void) { - nobj = 0; + int nobj = 0; + cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git"))); git_repository_odb(&_odb, _repo); - cl_assert_equal_i(GIT_EUSER, git_odb_foreach(_odb, foreach_stop_cb, NULL)); + cl_assert_equal_i(-321, git_odb_foreach(_odb, foreach_stop_cb, &nobj)); cl_assert(nobj == 1000); } diff --git a/tests/online/clone.c b/tests/online/clone.c index efc76d958..be4421ae5 100644 --- a/tests/online/clone.c +++ b/tests/online/clone.c @@ -273,7 +273,7 @@ static int cancel_at_half(const git_transfer_progress *stats, void *payload) GIT_UNUSED(payload); if (stats->received_objects > (stats->total_objects/2)) - return 1; + return 4321; return 0; } @@ -281,7 +281,8 @@ void test_online_clone__can_cancel(void) { g_options.remote_callbacks.transfer_progress = cancel_at_half; - cl_git_fail_with(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), GIT_EUSER); + cl_git_fail_with( + git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), 4321); } diff --git a/tests/online/fetch.c b/tests/online/fetch.c index 5153a7ae0..7e9dfdbbe 100644 --- a/tests/online/fetch.c +++ b/tests/online/fetch.c @@ -129,7 +129,7 @@ static int cancel_at_half(const git_transfer_progress *stats, void *payload) GIT_UNUSED(payload); if (stats->received_objects > (stats->total_objects/2)) - return -1; + return -4321; return 0; } @@ -147,7 +147,7 @@ void test_online_fetch__can_cancel(void) git_remote_set_callbacks(remote, &callbacks); cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); - cl_git_fail_with(git_remote_download(remote), GIT_EUSER); + cl_git_fail_with(git_remote_download(remote), -4321); git_remote_disconnect(remote); git_remote_free(remote); } diff --git a/tests/refs/foreachglob.c b/tests/refs/foreachglob.c index 2c458082f..c0f6ce763 100644 --- a/tests/refs/foreachglob.c +++ b/tests/refs/foreachglob.c @@ -81,14 +81,14 @@ static int interrupt_cb(const char *reference_name, void *payload) (*count)++; - return (*count == 11); + return (*count == 11) ? -1000 : 0; } void test_refs_foreachglob__can_cancel(void) { int count = 0; - cl_assert_equal_i(GIT_EUSER, git_reference_foreach_glob( + cl_assert_equal_i(-1000, git_reference_foreach_glob( repo, "*", interrupt_cb, &count) ); cl_assert_equal_i(11, count); diff --git a/tests/status/worktree.c b/tests/status/worktree.c index 34be6d34c..fd57fcc1e 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -552,7 +552,7 @@ static int cb_status__interrupt(const char *p, unsigned int s, void *payload) (*count)++; - return (*count == 8); + return (*count == 8) ? -111 : 0; } void test_status_worktree__interruptable_foreach(void) @@ -561,7 +561,7 @@ void test_status_worktree__interruptable_foreach(void) git_repository *repo = cl_git_sandbox_init("status"); cl_assert_equal_i( - GIT_EUSER, git_status_foreach(repo, cb_status__interrupt, &count) + -111, git_status_foreach(repo, cb_status__interrupt, &count) ); cl_assert_equal_i(8, count); -- cgit v1.2.1 From 60058018dcadbaa1f70281c9d29faf1e46d3a87c Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 6 Dec 2013 15:20:41 -0800 Subject: Fix C99 __func__ for MSVC --- src/common.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/common.h b/src/common.h index a0e47df9a..71182bbb9 100644 --- a/src/common.h +++ b/src/common.h @@ -94,7 +94,11 @@ GIT_INLINE(int) giterr_set_callback(int error_code, const char *action) return error_code; } +#ifdef GIT_WIN32 +#define GITERR_CALLBACK(code) giterr_set_callback((code), __FUNCTION__) +#else #define GITERR_CALLBACK(code) giterr_set_callback((code), __func__) +#endif /** * Gets the system error code for this thread. -- cgit v1.2.1 From c7b3e1b32040d05f3cb996d754a28af3b4d06d0b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 6 Dec 2013 15:42:20 -0800 Subject: Some callback error check style cleanups I find this easier to read... --- src/attr.c | 7 ++++--- src/diff.c | 4 +++- src/fetchhead.c | 11 ++++++----- src/merge.c | 4 +++- src/pack-objects.c | 7 +++++-- src/pack.c | 4 +++- src/path.c | 13 +++++++++---- src/push.c | 5 +++-- src/remote.c | 6 ++++-- src/stash.c | 14 ++++++++------ src/status.c | 5 +++-- src/submodule.c | 4 +++- 12 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/attr.c b/src/attr.c index 08d7ee99d..553c071be 100644 --- a/src/attr.c +++ b/src/attr.c @@ -191,10 +191,11 @@ int git_attr_foreach( if (error < 0) goto cleanup; - error = GITERR_CALLBACK( - callback(assign->name, assign->value, payload) ); - if (error) + error = callback(assign->name, assign->value, payload); + if (error) { + GITERR_CALLBACK(error); goto cleanup; + } } } } diff --git a/src/diff.c b/src/diff.c index 101426f6e..b7657e432 100644 --- a/src/diff.c +++ b/src/diff.c @@ -1388,8 +1388,10 @@ int git_diff__paired_foreach( i++; j++; } - if ((error = GITERR_CALLBACK( cb(h2i, i2w, payload) )) != 0) + if ((error = cb(h2i, i2w, payload)) != 0) { + GITERR_CALLBACK(error); break; + } } /* restore case-insensitive delta sort */ diff --git a/src/fetchhead.c b/src/fetchhead.c index 7c37be4c6..2f217fad1 100644 --- a/src/fetchhead.c +++ b/src/fetchhead.c @@ -260,8 +260,8 @@ int git_repository_fetchhead_foreach(git_repository *repo, while ((line = git__strsep(&buffer, "\n")) != NULL) { ++line_num; - if ((error = fetchhead_ref_parse(&oid, &is_merge, &name, &remote_url, - line, line_num)) < 0) + if ((error = fetchhead_ref_parse( + &oid, &is_merge, &name, &remote_url, line, line_num)) < 0) goto done; if (git_buf_len(&name) > 0) @@ -269,10 +269,11 @@ int git_repository_fetchhead_foreach(git_repository *repo, else ref_name = NULL; - error = GITERR_CALLBACK( - cb(ref_name, remote_url, &oid, is_merge, payload) ); - if (error) + error = cb(ref_name, remote_url, &oid, is_merge, payload); + if (error) { + GITERR_CALLBACK(error); goto done; + } } if (*buffer) { diff --git a/src/merge.c b/src/merge.c index 6f73fc14a..f1778c950 100644 --- a/src/merge.c +++ b/src/merge.c @@ -287,8 +287,10 @@ int git_repository_mergehead_foreach( if ((error = git_oid_fromstr(&oid, line)) < 0) goto cleanup; - if ((error = GITERR_CALLBACK( cb(&oid, payload) )) != 0) + if ((error = cb(&oid, payload)) != 0) { + GITERR_CALLBACK(error); goto cleanup; + } ++line_num; } diff --git a/src/pack-objects.c b/src/pack-objects.c index 2f0007f4f..09b7248af 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -229,9 +229,12 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, if (elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) { pb->last_progress_report_time = current_time; - return GITERR_CALLBACK( pb->progress_cb( + ret = pb->progress_cb( GIT_PACKBUILDER_ADDING_OBJECTS, - pb->nr_objects, 0, pb->progress_cb_payload) ); + pb->nr_objects, 0, pb->progress_cb_payload); + + if (ret) + return GITERR_CALLBACK(ret); } } diff --git a/src/pack.c b/src/pack.c index 3f2adb2f3..fd53ef49a 100644 --- a/src/pack.c +++ b/src/pack.c @@ -1088,8 +1088,10 @@ int git_pack_foreach_entry( } for (i = 0; i < p->num_objects; i++) - if ((error = GITERR_CALLBACK( cb(p->oids[i], data) )) != 0) + if ((error = cb(p->oids[i], data)) != 0) { + GITERR_CALLBACK(error); break; + } return error; } diff --git a/src/path.c b/src/path.c index 857a2e61c..8c19e004e 100644 --- a/src/path.c +++ b/src/path.c @@ -434,10 +434,13 @@ int git_path_walk_up( iter.asize = path->asize; while (scan >= stop) { - error = GITERR_CALLBACK( cb(data, &iter) ); + error = cb(data, &iter); iter.ptr[scan] = oldc; - if (error) + + if (error) { + GITERR_CALLBACK(error); break; + } scan = git_buf_rfind_next(&iter, '/'); if (scan >= 0) { @@ -874,12 +877,14 @@ int git_path_direach( if ((error = git_buf_put(path, de_path, de_len)) < 0) break; - error = GITERR_CALLBACK( fn(arg, path) ); + error = fn(arg, path); git_buf_truncate(path, wd_len); /* restore path */ - if (error) + if (error != 0) { + GITERR_CALLBACK(error); break; + } } closedir(dir); diff --git a/src/push.c b/src/push.c index 428173397..8ebba15eb 100644 --- a/src/push.c +++ b/src/push.c @@ -659,8 +659,9 @@ int git_push_status_foreach(git_push *push, unsigned int i; git_vector_foreach(&push->status, i, status) { - GITERR_CHECK_ERROR( - GITERR_CALLBACK( cb(status->ref, status->msg, data) ) ); + int error = cb(status->ref, status->msg, data); + if (error) + return GITERR_CALLBACK(error); } return 0; diff --git a/src/remote.c b/src/remote.c index 307306d1a..d46364a81 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1348,9 +1348,11 @@ static int rename_fetch_refspecs( if (!remote->name || strcmp(git_buf_cstr(&base), spec->string)) { - error = GITERR_CALLBACK( callback(spec->string, payload) ); - if (error) + if ((error = callback(spec->string, payload)) != 0) { + GITERR_CALLBACK(error); break; + } + continue; } diff --git a/src/stash.c b/src/stash.c index 458a1e175..fb5bb2e55 100644 --- a/src/stash.c +++ b/src/stash.c @@ -582,13 +582,15 @@ int git_stash_foreach( for (i = 0; i < max; i++) { entry = git_reflog_entry_byindex(reflog, i); - error = GITERR_CALLBACK( - callback(i, - git_reflog_entry_message(entry), - git_reflog_entry_id_new(entry), - payload) ); - if (error) + error = callback(i, + git_reflog_entry_message(entry), + git_reflog_entry_id_new(entry), + payload); + + if (error) { + GITERR_CALLBACK(error); break; + } } cleanup: diff --git a/src/status.c b/src/status.c index d76617a72..d4a436283 100644 --- a/src/status.c +++ b/src/status.c @@ -392,9 +392,10 @@ int git_status_foreach_ext( status_entry->head_to_index->old_file.path : status_entry->index_to_workdir->old_file.path; - error = GITERR_CALLBACK( cb(path, status_entry->status, payload) ); - if (error) + if ((error = cb(path, status_entry->status, payload)) != 0) { + GITERR_CALLBACK(error); break; + } } git_status_list_free(status); diff --git a/src/submodule.c b/src/submodule.c index e9d534ae8..5298302c7 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -168,8 +168,10 @@ int git_submodule_foreach( break; } - if ((error = GITERR_CALLBACK(callback(sm, sm->name, payload))) != 0) + if ((error = callback(sm, sm->name, payload)) != 0) { + GITERR_CALLBACK(error); break; + } }); git_vector_free(&seen); -- cgit v1.2.1 From f10d7a368fa4af28b1e6f082349ffa4f62b3c00e Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 6 Dec 2013 15:53:26 -0800 Subject: Further callback error check style fixes Okay, I've decided I like the readability of this style much better so I used it everywhere. --- src/config.c | 18 ++++++++++++------ src/notes.c | 10 ++++++---- src/refs.c | 27 ++++++++++++++++++--------- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/config.c b/src/config.c index 8b3a426ed..5477b04c8 100644 --- a/src/config.c +++ b/src/config.c @@ -507,9 +507,10 @@ int git_config_backend_foreach_match( continue; /* abort iterator on non-zero return value */ - error = GITERR_CALLBACK( cb(entry, payload) ); - if (error) + if ((error = cb(entry, payload)) != 0) { + GITERR_CALLBACK(error); break; + } } if (regexp != NULL) @@ -533,9 +534,12 @@ int git_config_foreach_match( if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0) return error; - while (!(error = git_config_next(&entry, iter)) && - !(error = GITERR_CALLBACK( cb(entry, payload) ))) - /* make callback on each config */; + while (!(error = git_config_next(&entry, iter))) { + if ((error = cb(entry, payload)) != 0) { + GITERR_CALLBACK(error); + break; + } + } git_config_iterator_free(iter); @@ -794,8 +798,10 @@ int git_config_get_multivar_foreach( while ((err = iter->next(&entry, iter)) == 0) { found = 1; - if ((err = GITERR_CALLBACK( cb(entry, payload) )) != 0) + if ((err = cb(entry, payload)) != 0) { + GITERR_CALLBACK(err); break; + } } iter->free(iter); diff --git a/src/notes.c b/src/notes.c index e3a3fccf8..e0d5ad19e 100644 --- a/src/notes.c +++ b/src/notes.c @@ -582,10 +582,12 @@ int git_note_foreach( if ((error = git_note_iterator_new(&iter, repo, notes_ref)) < 0) return error; - while (!(error = git_note_next(¬e_id, &annotated_id, iter)) && - !(error = GITERR_CALLBACK( - note_cb(¬e_id, &annotated_id, payload)))) - /* callback for each note */; + while (!(error = git_note_next(¬e_id, &annotated_id, iter))) { + if ((error = note_cb(¬e_id, &annotated_id, payload)) != 0) { + GITERR_CALLBACK(error); + break; + } + } if (error == GIT_ITEROVER) error = 0; diff --git a/src/refs.c b/src/refs.c index afb067986..25037cc4f 100644 --- a/src/refs.c +++ b/src/refs.c @@ -516,9 +516,12 @@ int git_reference_foreach( if ((error = git_reference_iterator_new(&iter, repo)) < 0) return error; - while (!(error = git_reference_next(&ref, iter)) && - !(error = GITERR_CALLBACK( callback(ref, payload) ))) - /* callback on each reference */; + while (!(error = git_reference_next(&ref, iter))) { + if ((error = callback(ref, payload)) != 0) { + GITERR_CALLBACK(error); + break; + } + } if (error == GIT_ITEROVER) error = 0; @@ -539,9 +542,12 @@ int git_reference_foreach_name( if ((error = git_reference_iterator_new(&iter, repo)) < 0) return error; - while (!(error = git_reference_next_name(&refname, iter)) && - !(error = GITERR_CALLBACK( callback(refname, payload) ))) - /* callback on each reference name */; + while (!(error = git_reference_next_name(&refname, iter))) { + if ((error = callback(refname, payload)) != 0) { + GITERR_CALLBACK(error); + break; + } + } if (error == GIT_ITEROVER) error = 0; @@ -563,9 +569,12 @@ int git_reference_foreach_glob( if ((error = git_reference_iterator_glob_new(&iter, repo, glob)) < 0) return error; - while (!(error = git_reference_next_name(&refname, iter)) && - !(error = GITERR_CALLBACK( callback(refname, payload) ))) - /* callback on each matching reference name */; + while (!(error = git_reference_next_name(&refname, iter))) { + if ((error = callback(refname, payload)) != 0) { + GITERR_CALLBACK(error); + break; + } + } if (error == GIT_ITEROVER) error = 0; -- cgit v1.2.1 From 26c1cb91beccb44425864bd233ed0e35f5801868 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 9 Dec 2013 09:44:03 -0800 Subject: One more rename/cleanup for callback err functions --- src/attr.c | 2 +- src/checkout.c | 11 +++++++---- src/common.h | 9 ++++++--- src/config.c | 6 +++--- src/diff.c | 8 ++++++-- src/diff_patch.c | 2 +- src/diff_print.c | 4 ++-- src/fetchhead.c | 2 +- src/index.c | 20 ++++++++++++++------ src/indexer.c | 2 +- src/merge.c | 2 +- src/notes.c | 2 +- src/odb_loose.c | 2 +- src/pack-objects.c | 2 +- src/pack.c | 6 ++---- src/path.c | 4 ++-- src/push.c | 2 +- src/refs.c | 6 +++--- src/remote.c | 2 +- src/stash.c | 2 +- src/status.c | 2 +- src/submodule.c | 2 +- src/tag.c | 4 ++-- src/tree.c | 16 +++++++++++----- 24 files changed, 71 insertions(+), 49 deletions(-) diff --git a/src/attr.c b/src/attr.c index 553c071be..e6e274e42 100644 --- a/src/attr.c +++ b/src/attr.c @@ -193,7 +193,7 @@ int git_attr_foreach( error = callback(assign->name, assign->value, payload); if (error) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); goto cleanup; } } diff --git a/src/checkout.c b/src/checkout.c index 4c64252e4..a292e3d4c 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -123,10 +123,13 @@ static int checkout_notify( path = delta->old_file.path; } - return giterr_set_callback( - data->opts.notify_cb( - why, path, baseline, target, workdir, data->opts.notify_payload), - "git_checkout notification"); + { + int error = data->opts.notify_cb( + why, path, baseline, target, workdir, data->opts.notify_payload); + + return giterr_set_after_callback_function( + error, "git_checkout notification"); + } } static bool checkout_is_workdir_modified( diff --git a/src/common.h b/src/common.h index 71182bbb9..e315b5979 100644 --- a/src/common.h +++ b/src/common.h @@ -83,7 +83,8 @@ int giterr_set_regex(const regex_t *regex, int error_code); * * @return This always returns the `error_code` parameter. */ -GIT_INLINE(int) giterr_set_callback(int error_code, const char *action) +GIT_INLINE(int) giterr_set_after_callback_function( + int error_code, const char *action) { if (error_code) { const git_error *e = giterr_last(); @@ -95,9 +96,11 @@ GIT_INLINE(int) giterr_set_callback(int error_code, const char *action) } #ifdef GIT_WIN32 -#define GITERR_CALLBACK(code) giterr_set_callback((code), __FUNCTION__) +#define giterr_set_after_callback(code) \ + giterr_set_after_callback_function((code), __FUNCTION__) #else -#define GITERR_CALLBACK(code) giterr_set_callback((code), __func__) +#define giterr_set_after_callback(code) \ + giterr_set_after_callback_function((code), __func__) #endif /** diff --git a/src/config.c b/src/config.c index 5477b04c8..056a6ae13 100644 --- a/src/config.c +++ b/src/config.c @@ -508,7 +508,7 @@ int git_config_backend_foreach_match( /* abort iterator on non-zero return value */ if ((error = cb(entry, payload)) != 0) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } } @@ -536,7 +536,7 @@ int git_config_foreach_match( while (!(error = git_config_next(&entry, iter))) { if ((error = cb(entry, payload)) != 0) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } } @@ -799,7 +799,7 @@ int git_config_get_multivar_foreach( found = 1; if ((err = cb(entry, payload)) != 0) { - GITERR_CALLBACK(err); + giterr_set_after_callback(err); break; } } diff --git a/src/diff.c b/src/diff.c index b7657e432..83adc2a8c 100644 --- a/src/diff.c +++ b/src/diff.c @@ -60,7 +60,11 @@ static int diff_insert_delta( if (error) { git__free(delta); - return (error > 0) ? 0 : giterr_set_callback(error, "git_diff"); + + if (error > 0) /* positive value means to skip this delta */ + return 0; + else /* negative value means to cancel diff */ + return giterr_set_after_callback_function(error, "git_diff"); } } @@ -1389,7 +1393,7 @@ int git_diff__paired_foreach( } if ((error = cb(h2i, i2w, payload)) != 0) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } } diff --git a/src/diff_patch.c b/src/diff_patch.c index 11f02478d..9c2eb885f 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -202,7 +202,7 @@ static int diff_patch_invoke_file_callback( if (!output->file_cb) return 0; - return giterr_set_callback( + return giterr_set_after_callback_function( output->file_cb(patch->delta, progress, output->payload), "git_patch"); } diff --git a/src/diff_print.c b/src/diff_print.c index ff477e4c8..7a70e2b18 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -398,7 +398,7 @@ int git_diff_print( diff, print_file, print_hunk, print_line, &pi); if (error) /* make sure error message is set */ - giterr_set_callback(error, "git_diff_print"); + giterr_set_after_callback_function(error, "git_diff_print"); } git_buf_free(&buf); @@ -427,7 +427,7 @@ int git_patch_print( diff_print_patch_line, &pi); if (error) /* make sure error message is set */ - giterr_set_callback(error, "git_patch_print"); + giterr_set_after_callback_function(error, "git_patch_print"); } git_buf_free(&temp); diff --git a/src/fetchhead.c b/src/fetchhead.c index 2f217fad1..4435454ef 100644 --- a/src/fetchhead.c +++ b/src/fetchhead.c @@ -271,7 +271,7 @@ int git_repository_fetchhead_foreach(git_repository *repo, error = cb(ref_name, remote_url, &oid, is_merge, payload); if (error) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); goto done; } } diff --git a/src/index.c b/src/index.c index 671bdfa79..bb81f666e 100644 --- a/src/index.c +++ b/src/index.c @@ -2117,7 +2117,7 @@ int git_index_add_all( if (error > 0) /* return > 0 means skip this one */ continue; if (error < 0) { /* return < 0 means abort */ - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } } @@ -2204,10 +2204,8 @@ static int index_apply_to_all( error = 0; continue; } - if (error < 0) { /* return < 0 means abort */ - giterr_set_callback(error, "git_index_matched_path"); + if (error < 0) /* return < 0 means abort */ break; - } } /* index manipulation may alter entry, so don't depend on it */ @@ -2252,8 +2250,13 @@ int git_index_remove_all( git_index_matched_path_cb cb, void *payload) { - return index_apply_to_all( + int error = index_apply_to_all( index, INDEX_ACTION_REMOVE, pathspec, cb, payload); + + if (error) /* make sure error is set if callback stopped iteration */ + giterr_set_after_callback(error); + + return error; } int git_index_update_all( @@ -2262,6 +2265,11 @@ int git_index_update_all( git_index_matched_path_cb cb, void *payload) { - return index_apply_to_all( + int error = index_apply_to_all( index, INDEX_ACTION_UPDATE, pathspec, cb, payload); + + if (error) /* make sure error is set if callback stopped iteration */ + giterr_set_after_callback(error); + + return error; } diff --git a/src/indexer.c b/src/indexer.c index 320845bd9..88897d07d 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -387,7 +387,7 @@ on_error: static int do_progress_callback(git_indexer *idx, git_transfer_progress *stats) { if (idx->progress_cb) - return giterr_set_callback( + return giterr_set_after_callback_function( idx->progress_cb(stats, idx->progress_payload), "indexer progress"); return 0; diff --git a/src/merge.c b/src/merge.c index f1778c950..5640be56b 100644 --- a/src/merge.c +++ b/src/merge.c @@ -288,7 +288,7 @@ int git_repository_mergehead_foreach( goto cleanup; if ((error = cb(&oid, payload)) != 0) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); goto cleanup; } diff --git a/src/notes.c b/src/notes.c index e0d5ad19e..795904917 100644 --- a/src/notes.c +++ b/src/notes.c @@ -584,7 +584,7 @@ int git_note_foreach( while (!(error = git_note_next(¬e_id, &annotated_id, iter))) { if ((error = note_cb(¬e_id, &annotated_id, payload)) != 0) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } } diff --git a/src/odb_loose.c b/src/odb_loose.c index 78cd792bd..fd4ffff1e 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -733,7 +733,7 @@ static int foreach_object_dir_cb(void *_state, git_buf *path) if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0) return 0; - return giterr_set_callback( + return giterr_set_after_callback_function( state->cb(&oid, state->data), "git_odb_foreach"); } diff --git a/src/pack-objects.c b/src/pack-objects.c index 09b7248af..7ce1b6cb3 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -234,7 +234,7 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, pb->nr_objects, 0, pb->progress_cb_payload); if (ret) - return GITERR_CALLBACK(ret); + return giterr_set_after_callback(ret); } } diff --git a/src/pack.c b/src/pack.c index fd53ef49a..23fcf3530 100644 --- a/src/pack.c +++ b/src/pack.c @@ -1088,10 +1088,8 @@ int git_pack_foreach_entry( } for (i = 0; i < p->num_objects; i++) - if ((error = cb(p->oids[i], data)) != 0) { - GITERR_CALLBACK(error); - break; - } + if ((error = cb(p->oids[i], data)) != 0) + return giterr_set_after_callback(error); return error; } diff --git a/src/path.c b/src/path.c index 8c19e004e..365bd6c00 100644 --- a/src/path.c +++ b/src/path.c @@ -438,7 +438,7 @@ int git_path_walk_up( iter.ptr[scan] = oldc; if (error) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } @@ -882,7 +882,7 @@ int git_path_direach( git_buf_truncate(path, wd_len); /* restore path */ if (error != 0) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } } diff --git a/src/push.c b/src/push.c index 8ebba15eb..adba880df 100644 --- a/src/push.c +++ b/src/push.c @@ -661,7 +661,7 @@ int git_push_status_foreach(git_push *push, git_vector_foreach(&push->status, i, status) { int error = cb(status->ref, status->msg, data); if (error) - return GITERR_CALLBACK(error); + return giterr_set_after_callback(error); } return 0; diff --git a/src/refs.c b/src/refs.c index 25037cc4f..4f3a557c6 100644 --- a/src/refs.c +++ b/src/refs.c @@ -518,7 +518,7 @@ int git_reference_foreach( while (!(error = git_reference_next(&ref, iter))) { if ((error = callback(ref, payload)) != 0) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } } @@ -544,7 +544,7 @@ int git_reference_foreach_name( while (!(error = git_reference_next_name(&refname, iter))) { if ((error = callback(refname, payload)) != 0) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } } @@ -571,7 +571,7 @@ int git_reference_foreach_glob( while (!(error = git_reference_next_name(&refname, iter))) { if ((error = callback(refname, payload)) != 0) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } } diff --git a/src/remote.c b/src/remote.c index d46364a81..689de230a 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1349,7 +1349,7 @@ static int rename_fetch_refspecs( strcmp(git_buf_cstr(&base), spec->string)) { if ((error = callback(spec->string, payload)) != 0) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } diff --git a/src/stash.c b/src/stash.c index fb5bb2e55..eae56966c 100644 --- a/src/stash.c +++ b/src/stash.c @@ -588,7 +588,7 @@ int git_stash_foreach( payload); if (error) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } } diff --git a/src/status.c b/src/status.c index d4a436283..9bde8fb57 100644 --- a/src/status.c +++ b/src/status.c @@ -393,7 +393,7 @@ int git_status_foreach_ext( status_entry->index_to_workdir->old_file.path; if ((error = cb(path, status_entry->status, payload)) != 0) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } } diff --git a/src/submodule.c b/src/submodule.c index 5298302c7..1c36d3656 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -169,7 +169,7 @@ int git_submodule_foreach( } if ((error = callback(sm, sm->name, payload)) != 0) { - GITERR_CALLBACK(error); + giterr_set_after_callback(error); break; } }); diff --git a/src/tag.c b/src/tag.c index adf2819d7..be56b05ce 100644 --- a/src/tag.c +++ b/src/tag.c @@ -426,8 +426,8 @@ static int tags_cb(const char *ref, void *data) return 0; /* no tag */ if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) { - error = d->cb(ref, &oid, d->cb_data); - giterr_set_callback(error, "git_tag_foreach"); + if ((error = d->cb(ref, &oid, d->cb_data)) != 0) + giterr_set_after_callback_function(error, "git_tag_foreach"); } return error; diff --git a/src/tree.c b/src/tree.c index 5f35ac3a8..4d77ff778 100644 --- a/src/tree.c +++ b/src/tree.c @@ -883,9 +883,12 @@ static int tree_walk( git_vector_foreach(&tree->entries, i, entry) { if (preorder) { - if ((error = callback(path->ptr, entry, payload)) < 0) - return giterr_set_callback(error, "git_tree_walk"); - if (error > 0) { + error = callback(path->ptr, entry, payload); + if (error < 0) { /* negative value stops iteration */ + giterr_set_after_callback_function(error, "git_tree_walk"); + break; + } + if (error > 0) { /* positive value skips this entry */ error = 0; continue; } @@ -916,8 +919,11 @@ static int tree_walk( } if (!preorder) { - if ((error = callback(path->ptr, entry, payload)) < 0) - return giterr_set_callback(error, "git_tree_walk"); + error = callback(path->ptr, entry, payload); + if (error < 0) { /* negative value stops iteration */ + giterr_set_after_callback_function(error, "git_tree_walk"); + break; + } error = 0; } } -- cgit v1.2.1 From 373cf6a932a64d1cbe5f5cd8333546dcc2ca0b92 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 9 Dec 2013 10:17:47 -0800 Subject: Update docs for new callback return value behavior --- include/git2/attr.h | 3 ++- include/git2/config.h | 6 +++--- include/git2/diff.h | 12 ++++++------ include/git2/errors.h | 45 +++++++++++++++++++++++++++++---------------- include/git2/index.h | 8 ++++---- include/git2/notes.h | 2 +- include/git2/odb.h | 2 +- include/git2/patch.h | 4 ++-- include/git2/push.h | 6 ++++-- include/git2/refs.h | 17 +++++++++++++++-- include/git2/repository.h | 20 ++++++++++++++------ include/git2/stash.h | 18 +++++++----------- include/git2/status.h | 6 +++--- 13 files changed, 91 insertions(+), 58 deletions(-) diff --git a/include/git2/attr.h b/include/git2/attr.h index f256ff861..3adbb2c24 100644 --- a/include/git2/attr.h +++ b/include/git2/attr.h @@ -199,8 +199,9 @@ typedef int (*git_attr_foreach_cb)(const char *name, const char *value, void *pa * only once per attribute name, even if there are multiple * rules for a given file. The highest priority rule will be * used. Return a non-zero value from this to stop looping. + * The value will be returned from `git_attr_foreach`. * @param payload Passed on as extra parameter to callback function. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_attr_foreach( git_repository *repo, diff --git a/include/git2/config.h b/include/git2/config.h index 3ab58f1a7..f650f1b41 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -450,13 +450,13 @@ GIT_EXTERN(int) git_config_delete_multivar(git_config *cfg, const char *name, co * * The callback receives the normalized name and value of each variable * in the config backend, and the data pointer passed to this function. - * As soon as one of the callback functions returns something other than 0, - * this function stops iterating and returns `GIT_EUSER`. + * If the callback returns a non-zero value, the function stops iterating + * and returns that value to the caller. * * @param cfg where to get the variables from * @param callback the function to call on each variable * @param payload the data to pass to the callback - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_config_foreach( const git_config *cfg, diff --git a/include/git2/diff.h b/include/git2/diff.h index d6919393a..76fb23654 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -870,7 +870,7 @@ GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff *diff); * files whose only changed is a file mode change. * * Returning a non-zero value from any of the callbacks will terminate - * the iteration and cause this return `GIT_EUSER`. + * the iteration and return the value to the user. * * @param diff A git_diff generated by one of the above functions. * @param file_cb Callback function to make per file in the diff. @@ -881,7 +881,7 @@ GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff *diff); * same callback will be made for context lines, added, and * removed lines, and even for a deleted trailing newline. * @param payload Reference pointer that will be passed to your callbacks. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_foreach( git_diff *diff, @@ -918,13 +918,13 @@ typedef enum { * Iterate over a diff generating formatted text output. * * Returning a non-zero value from the callbacks will terminate the - * iteration and cause this return `GIT_EUSER`. + * iteration and return the non-zero value to the caller. * * @param diff A git_diff generated by one of the above functions. * @param format A git_diff_format_t value to pick the text format. * @param print_cb Callback to make per line of diff text. * @param payload Reference pointer that will be passed to your callback. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_print( git_diff *diff, @@ -964,7 +964,7 @@ GIT_EXTERN(int) git_diff_print( * @param hunk_cb Callback for each hunk in diff; can be NULL * @param line_cb Callback for each line in diff; can be NULL * @param payload Payload passed to each callback function - * @return 0 on success, GIT_EUSER on non-zero callback return, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_blobs( const git_blob *old_blob, @@ -999,7 +999,7 @@ GIT_EXTERN(int) git_diff_blobs( * @param hunk_cb Callback for each hunk in diff; can be NULL * @param line_cb Callback for each line in diff; can be NULL * @param payload Payload passed to each callback function - * @return 0 on success, GIT_EUSER on non-zero callback return, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_blob_to_buffer( const git_blob *old_blob, diff --git a/include/git2/errors.h b/include/git2/errors.h index 26f9a747c..973d56003 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -19,25 +19,38 @@ GIT_BEGIN_DECL /** Generic return codes */ typedef enum { - GIT_OK = 0, - GIT_ERROR = -1, - GIT_ENOTFOUND = -3, - GIT_EEXISTS = -4, - GIT_EAMBIGUOUS = -5, - GIT_EBUFS = -6, - GIT_EUSER = -7, - GIT_EBAREREPO = -8, - GIT_EUNBORNBRANCH = -9, - GIT_EUNMERGED = -10, - GIT_ENONFASTFORWARD = -11, - GIT_EINVALIDSPEC = -12, - GIT_EMERGECONFLICT = -13, - GIT_ELOCKED = -14, + GIT_OK = 0, /*< No error */ - GIT_PASSTHROUGH = -30, - GIT_ITEROVER = -31, + GIT_ERROR = -1, /*< Generic error */ + GIT_ENOTFOUND = -3, /*< Requested object could not be found */ + GIT_EEXISTS = -4, /*< Object exists preventing operation */ + GIT_EAMBIGUOUS = -5, /*< More than one object matches */ + GIT_EBUFS = -6, /*< Output buffer too short to hold data */ + + /* GIT_EUSER is a special error that is never generated by libgit2 + * code. You can return it from a callback (e.g to stop an iteration) + * to know that it was generated by the callback and not by libgit2. + */ + GIT_EUSER = -7, + + GIT_EBAREREPO = -8, /*< Operation not allowed on bare repository */ + GIT_EUNBORNBRANCH = -9, /*< HEAD refers to branch with no commits */ + GIT_EUNMERGED = -10, /*< Merge in progress prevented operation */ + GIT_ENONFASTFORWARD = -11, /*< Reference was not fast-forwardable */ + GIT_EINVALIDSPEC = -12, /*< Name/ref spec was not in a valid format */ + GIT_EMERGECONFLICT = -13, /*< Merge conflicts prevented operation */ + GIT_ELOCKED = -14, /*< Lock file prevented operation */ + + GIT_PASSTHROUGH = -30, /*< Internal only */ + GIT_ITEROVER = -31, /*< Signals end of iteration with iterator */ } git_error_code; +/** + * Structure to store extra details of the last error that occurred. + * + * This is kept on a per-thread basis if GIT_THREADS was defined when the + * library was build, otherwise one is kept globally for the library + */ typedef struct { char *message; int klass; diff --git a/include/git2/index.h b/include/git2/index.h index a60db370a..ffefad15d 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -493,7 +493,7 @@ GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path); * item in the working directory immediately *before* it is added to / * updated in the index. Returning zero will add the item to the index, * greater than zero will skip the item, and less than zero will abort the - * scan and cause GIT_EUSER to be returned. + * scan and return that value to the caller. * * @param index an existing index object * @param pathspec array of path patterns @@ -502,7 +502,7 @@ GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path); * gets index of matching pathspec entry); can be NULL; * return 0 to add, >0 to skip, <0 to abort scan. * @param payload payload passed through to callback function - * @return 0 or an error code + * @return 0 on success, negative callback return value, or error code */ GIT_EXTERN(int) git_index_add_all( git_index *index, @@ -524,7 +524,7 @@ GIT_EXTERN(int) git_index_add_all( * gets index of matching pathspec entry); can be NULL; * return 0 to add, >0 to skip, <0 to abort scan. * @param payload payload passed through to callback function - * @return 0 or an error code + * @return 0 on success, negative callback return value, or error code */ GIT_EXTERN(int) git_index_remove_all( git_index *index, @@ -553,7 +553,7 @@ GIT_EXTERN(int) git_index_remove_all( * gets index of matching pathspec entry); can be NULL; * return 0 to add, >0 to skip, <0 to abort scan. * @param payload payload passed through to callback function - * @return 0 or an error code + * @return 0 on success, negative callback return value, or error code */ GIT_EXTERN(int) git_index_update_all( git_index *index, diff --git a/include/git2/notes.h b/include/git2/notes.h index 76361633b..0cb6ce5f1 100644 --- a/include/git2/notes.h +++ b/include/git2/notes.h @@ -189,7 +189,7 @@ GIT_EXTERN(int) git_note_default_ref(const char **out, git_repository *repo); * * @param payload Extra parameter to callback function. * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_note_foreach( git_repository *repo, diff --git a/include/git2/odb.h b/include/git2/odb.h index ad56384f0..82df4d300 100644 --- a/include/git2/odb.h +++ b/include/git2/odb.h @@ -189,7 +189,7 @@ GIT_EXTERN(int) git_odb_refresh(struct git_odb *db); * @param db database to use * @param cb the callback to call for each object * @param payload data to pass to the callback - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload); diff --git a/include/git2/patch.h b/include/git2/patch.h index 6a6ad92d7..e09f625c0 100644 --- a/include/git2/patch.h +++ b/include/git2/patch.h @@ -218,13 +218,13 @@ GIT_EXTERN(size_t) git_patch_size( * Serialize the patch to text via callback. * * Returning a non-zero value from the callback will terminate the iteration - * and cause this return `GIT_EUSER`. + * and return that value to the caller. * * @param patch A git_patch representing changes to one file * @param print_cb Callback function to output lines of the patch. Will be * called for file headers, hunk headers, and diff lines. * @param payload Reference pointer that will be passed to your callbacks. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_patch_print( git_patch *patch, diff --git a/include/git2/push.h b/include/git2/push.h index 77ef74039..12f0e7f2c 100644 --- a/include/git2/push.h +++ b/include/git2/push.h @@ -132,17 +132,19 @@ GIT_EXTERN(int) git_push_finish(git_push *push); GIT_EXTERN(int) git_push_unpack_ok(git_push *push); /** - * Call callback `cb' on each status + * Invoke callback `cb' on each status entry * * For each of the updated references, we receive a status report in the * form of `ok refs/heads/master` or `ng refs/heads/master `. * `msg != NULL` means the reference has not been updated for the given * reason. * + * Return a non-zero value from the callback to stop the loop. + * * @param push The push object * @param cb The callback to call on each object * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_push_status_foreach(git_push *push, int (*cb)(const char *ref, const char *msg, void *data), diff --git a/include/git2/refs.h b/include/git2/refs.h index 4041947f6..e2bfa9615 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -310,20 +310,33 @@ typedef int (*git_reference_foreach_name_cb)(const char *name, void *payload); * Perform a callback on each reference in the repository. * * The `callback` function will be called for each reference in the - * repository, receiving the name of the reference and the `payload` value + * repository, receiving the reference object and the `payload` value * passed to this method. Returning a non-zero value from the callback * will terminate the iteration. * * @param repo Repository where to find the refs * @param callback Function which will be called for every listed ref * @param payload Additional data to pass to the callback - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_reference_foreach( git_repository *repo, git_reference_foreach_cb callback, void *payload); +/** + * Perform a callback on the fully-qualified name of each reference. + * + * The `callback` function will be called for each reference in the + * repository, receiving the name of the reference and the `payload` value + * passed to this method. Returning a non-zero value from the callback + * will terminate the iteration. + * + * @param repo Repository where to find the refs + * @param callback Function which will be called for every listed ref name + * @param payload Additional data to pass to the callback + * @return 0 on success, non-zero callback return value, or error code + */ GIT_EXTERN(int) git_reference_foreach_name( git_repository *repo, git_reference_foreach_name_cb callback, diff --git a/include/git2/repository.h b/include/git2/repository.h index c6bd4dca4..9f71d2959 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -503,14 +503,18 @@ typedef int (*git_repository_fetchhead_foreach_cb)(const char *ref_name, void *payload); /** - * Call callback 'callback' for each entry in the given FETCH_HEAD file. + * Invoke 'callback' for each entry in the given FETCH_HEAD file. + * + * Return a non-zero value from the callback to stop the loop. * * @param repo A repository object * @param callback Callback function * @param payload Pointer to callback data (optional) - * @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error + * @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if + * there is no FETCH_HEAD file, or other error code. */ -GIT_EXTERN(int) git_repository_fetchhead_foreach(git_repository *repo, +GIT_EXTERN(int) git_repository_fetchhead_foreach( + git_repository *repo, git_repository_fetchhead_foreach_cb callback, void *payload); @@ -518,15 +522,19 @@ typedef int (*git_repository_mergehead_foreach_cb)(const git_oid *oid, void *payload); /** - * If a merge is in progress, call callback 'cb' for each commit ID in the + * If a merge is in progress, invoke 'callback' for each commit ID in the * MERGE_HEAD file. * + * Return a non-zero value from the callback to stop the loop. + * * @param repo A repository object * @param callback Callback function * @param payload Pointer to callback data (optional) - * @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error + * @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if + * there is no MERGE_HEAD file, or other error code. */ -GIT_EXTERN(int) git_repository_mergehead_foreach(git_repository *repo, +GIT_EXTERN(int) git_repository_mergehead_foreach( + git_repository *repo, git_repository_mergehead_foreach_cb callback, void *payload); diff --git a/include/git2/stash.h b/include/git2/stash.h index b48d33f5d..e2fe2cf0b 100644 --- a/include/git2/stash.h +++ b/include/git2/stash.h @@ -62,19 +62,15 @@ GIT_EXTERN(int) git_stash_save( unsigned int flags); /** - * When iterating over all the stashed states, callback that will be - * issued per entry. + * This is a callback function you can provide to iterate over all the + * stashed states that will be invoked per entry. * * @param index The position within the stash list. 0 points to the - * most recent stashed state. - * + * most recent stashed state. * @param message The stash message. - * * @param stash_id The commit oid of the stashed state. - * * @param payload Extra parameter to callback function. - * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 to continue iterating or non-zero to stop */ typedef int (*git_stash_cb)( size_t index, @@ -89,12 +85,12 @@ typedef int (*git_stash_cb)( * * @param repo Repository where to find the stash. * - * @param callback Callback to invoke per found stashed state. The most recent - * stash state will be enumerated first. + * @param callback Callback to invoke per found stashed state. The most + * recent stash state will be enumerated first. * * @param payload Extra parameter to callback function. * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_stash_foreach( git_repository *repo, diff --git a/include/git2/status.h b/include/git2/status.h index 4ec3432df..aa68c0da0 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -203,12 +203,12 @@ typedef struct { * into this function. * * If the callback returns a non-zero value, this function will stop looping - * and return GIT_EUSER. + * and return that value to caller. * * @param repo A repository object * @param callback The function to call on each file * @param payload Pointer to pass through to callback function - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_status_foreach( git_repository *repo, @@ -227,7 +227,7 @@ GIT_EXTERN(int) git_status_foreach( * @param opts Status options structure * @param callback The function to call on each file * @param payload Pointer to pass through to callback function - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_status_foreach_ext( git_repository *repo, -- cgit v1.2.1 From 19853bdd97e006b6e4519bc352c3e8fd7586e9c3 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 10 Dec 2013 13:01:34 -0800 Subject: Update git_blob_create_fromchunks callback behavr The callback to supply data chunks could return a negative value to stop creation of the blob, but we were neither using GIT_EUSER nor propagating the return value. This makes things use the new behavior of returning the negative value back to the user. --- include/git2/blob.h | 43 +++++++++++++++++++----------------------- src/blob.c | 34 ++++++++++++++++++++------------- tests/object/blob/fromchunks.c | 39 +++++++++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 38 deletions(-) diff --git a/include/git2/blob.h b/include/git2/blob.h index dcab4fbe0..19ad4d949 100644 --- a/include/git2/blob.h +++ b/include/git2/blob.h @@ -159,37 +159,32 @@ typedef int (*git_blob_chunk_cb)(char *content, size_t max_length, void *payload * Write a loose blob to the Object Database from a * provider of chunks of data. * - * Provided the `hintpath` parameter is filled, its value - * will help to determine what git filters should be applied - * to the object before it can be placed to the object database. + * If the `hintpath` parameter is filled, it will be used to determine + * what git filters should be applied to the object before it is written + * to the object database. * + * The implementation of the callback MUST respect the following rules: * - * The implementation of the callback has to respect the - * following rules: + * - `content` must be filled by the callback. The maximum number of + * bytes that the buffer can accept per call is defined by the + * `max_length` parameter. Allocation and freeing of the buffer will + * be taken care of by libgit2. * - * - `content` will have to be filled by the consumer. The maximum number - * of bytes that the buffer can accept per call is defined by the - * `max_length` parameter. Allocation and freeing of the buffer will be taken - * care of by the function. + * - The `callback` must return the number of bytes that have been + * written to the `content` buffer. * - * - The callback is expected to return the number of bytes - * that `content` have been filled with. - * - * - When there is no more data to stream, the callback should - * return 0. This will prevent it from being invoked anymore. - * - * - When an error occurs, the callback should return -1. + * - When there is no more data to stream, `callback` should return + * 0. This will prevent it from being invoked anymore. * + * - If an error occurs, the callback should return a negative value. + * This value will be returned to the caller. * * @param id Return the id of the written blob - * - * @param repo repository where the blob will be written. - * This repository can be bare or not. - * - * @param hintpath if not NULL, will help selecting the filters - * to apply onto the content of the blob to be created. - * - * @return 0 or an error code + * @param repo Repository where the blob will be written. + * This repository can be bare or not. + * @param hintpath If not NULL, will be used to select data filters + * to apply onto the content of the blob to be created. + * @return 0 or error code (from either libgit2 or callback function) */ GIT_EXTERN(int) git_blob_create_fromchunks( git_oid *id, diff --git a/src/blob.c b/src/blob.c index 2c6d52800..ab344ae98 100644 --- a/src/blob.c +++ b/src/blob.c @@ -272,37 +272,44 @@ int git_blob_create_fromchunks( int (*source_cb)(char *content, size_t max_length, void *payload), void *payload) { - int error = -1, read_bytes; + int error; char *content = NULL; git_filebuf file = GIT_FILEBUF_INIT; git_buf path = GIT_BUF_INIT; - if (git_buf_joinpath( - &path, git_repository_path(repo), GIT_OBJECTS_DIR "streamed") < 0) + assert(oid && repo && source_cb); + + if ((error = git_buf_joinpath( + &path, git_repository_path(repo), GIT_OBJECTS_DIR "streamed")) < 0) goto cleanup; content = git__malloc(BUFFER_SIZE); GITERR_CHECK_ALLOC(content); - if (git_filebuf_open(&file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, 0666) < 0) + if ((error = git_filebuf_open( + &file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, 0666)) < 0) goto cleanup; while (1) { - read_bytes = source_cb(content, BUFFER_SIZE, payload); - - assert(read_bytes <= BUFFER_SIZE); + int read_bytes = source_cb(content, BUFFER_SIZE, payload); - if (read_bytes <= 0) + if (!read_bytes) break; - if (git_filebuf_write(&file, content, read_bytes) < 0) + if (read_bytes > BUFFER_SIZE) { + giterr_set(GITERR_OBJECT, "Invalid chunk size while creating blob"); + error = GIT_EBUFS; + } else if (read_bytes < 0) { + error = giterr_set_after_callback(read_bytes); + } else { + error = git_filebuf_write(&file, content, read_bytes); + } + + if (error < 0) goto cleanup; } - if (read_bytes < 0) - goto cleanup; - - if (git_filebuf_flush(&file) < 0) + if ((error = git_filebuf_flush(&file)) < 0) goto cleanup; error = git_blob__create_from_paths( @@ -312,6 +319,7 @@ cleanup: git_buf_free(&path); git_filebuf_cleanup(&file); git__free(content); + return error; } diff --git a/tests/object/blob/fromchunks.c b/tests/object/blob/fromchunks.c index 03ed4efb4..b61cabfe1 100644 --- a/tests/object/blob/fromchunks.c +++ b/tests/object/blob/fromchunks.c @@ -59,7 +59,7 @@ void test_object_blob_fromchunks__doesnot_overwrite_an_already_existing_object(v git_buf content = GIT_BUF_INIT; git_oid expected_oid, oid; int howmany = 7; - + cl_git_pass(git_oid_fromstr(&expected_oid, "321cbdf08803c744082332332838df6bd160f8f9")); cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany)); @@ -117,3 +117,40 @@ void test_object_blob_fromchunks__creating_a_blob_from_chunks_honors_the_attribu assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.txt"); assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.dunno"); } + +static int failing_chunked_source_cb( + char *content, size_t max_length, void *payload) +{ + int *count = (int *)payload; + + GIT_UNUSED(max_length); + + (*count)--; + if (*count == 0) + return -1234; + + strcpy(content, textual_content); + return (int)strlen(textual_content); +} + +void test_object_blob_fromchunks__can_stop_with_error(void) +{ + git_oid expected_oid, oid; + git_object *blob; + int howmany = 7; + + cl_git_pass(git_oid_fromstr( + &expected_oid, "321cbdf08803c744082332332838df6bd160f8f9")); + + cl_git_fail_with( + git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY), + GIT_ENOTFOUND); + + cl_git_fail_with(git_blob_create_fromchunks( + &oid, repo, NULL, failing_chunked_source_cb, &howmany), -1234); + + cl_git_fail_with( + git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY), + GIT_ENOTFOUND); +} + -- cgit v1.2.1 From cbd048969e2d53790472118bf2d337cd1d90ca94 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 10 Dec 2013 14:38:35 -0800 Subject: Fix checkout notify callback docs and tests The checkout notify callback behavior on non-zero return values was not being tested. This adds tests, fixes a bug with positive values, and clarifies the documentation to make it clear that the checkout can be canceled via this mechanism. --- include/git2/checkout.h | 21 ++++++++----- src/checkout.c | 4 +-- tests/checkout/tree.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 10 deletions(-) diff --git a/include/git2/checkout.h b/include/git2/checkout.h index efafdc3e4..0e9d338c6 100644 --- a/include/git2/checkout.h +++ b/include/git2/checkout.h @@ -174,7 +174,12 @@ typedef enum { * - GIT_CHECKOUT_NOTIFY_IGNORED notifies about ignored files. * * Returning a non-zero value from this callback will cancel the checkout. - * Notification callbacks are made prior to modifying any files on disk. + * The non-zero return value will be propagated back and returned by the + * git_checkout_... call. + * + * Notification callbacks are made prior to modifying any files on disk, + * so canceling on any notification will still happen prior to any files + * being modified. */ typedef enum { GIT_CHECKOUT_NOTIFY_NONE = 0, @@ -252,9 +257,9 @@ typedef struct git_checkout_opts { * * @param repo repository to check out (must be non-bare) * @param opts specifies checkout options (may be NULL) - * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing - * branch, GIT_ERROR otherwise (use giterr_last for information - * about the error) + * @return 0 on success, GIT_EUNBORNBRANCH if HEAD points to a non + * existing branch, non-zero value returned by `notify_cb`, or + * other error code < 0 (use giterr_last for error details) */ GIT_EXTERN(int) git_checkout_head( git_repository *repo, @@ -266,8 +271,8 @@ GIT_EXTERN(int) git_checkout_head( * @param repo repository into which to check out (must be non-bare) * @param index index to be checked out (or NULL to use repository index) * @param opts specifies checkout options (may be NULL) - * @return 0 on success, GIT_ERROR otherwise (use giterr_last for information - * about the error) + * @return 0 on success, non-zero return value from `notify_cb`, or error + * code < 0 (use giterr_last for error details) */ GIT_EXTERN(int) git_checkout_index( git_repository *repo, @@ -282,8 +287,8 @@ GIT_EXTERN(int) git_checkout_index( * @param treeish a commit, tag or tree which content will be used to update * the working directory (or NULL to use HEAD) * @param opts specifies checkout options (may be NULL) - * @return 0 on success, GIT_ERROR otherwise (use giterr_last for information - * about the error) + * @return 0 on success, non-zero return value from `notify_cb`, or error + * code < 0 (use giterr_last for error details) */ GIT_EXTERN(int) git_checkout_tree( git_repository *repo, diff --git a/src/checkout.c b/src/checkout.c index a292e3d4c..f7dd052c7 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -993,7 +993,7 @@ static int checkout_get_actions( git_vector_foreach(deltas, i, delta) { error = checkout_action(&act, data, delta, workdir, &wditem, &pathspec); - if (error) + if (error != 0) goto fail; actions[i] = act; @@ -1957,7 +1957,7 @@ int git_checkout_iterator( * actions to be taken, plus look for conflicts and send notifications, * then loop through conflicts. */ - if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) < 0) + if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) != 0) goto cleanup; data.total_steps = counts[CHECKOUT_ACTION__REMOVE] + diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index 66b01bc7f..ac48bfcdd 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -486,6 +486,84 @@ void test_checkout_tree__donot_update_deleted_file_by_default(void) git_index_free(index); } +struct checkout_cancel_at { + const char *filename; + int error; + int count; +}; + +static int checkout_cancel_cb( + git_checkout_notify_t why, + const char *path, + const git_diff_file *b, + const git_diff_file *t, + const git_diff_file *w, + void *payload) +{ + struct checkout_cancel_at *ca = payload; + + GIT_UNUSED(why); GIT_UNUSED(b); GIT_UNUSED(t); GIT_UNUSED(w); + + ca->count++; + + if (!strcmp(path, ca->filename)) + return ca->error; + + return 0; +} + +void test_checkout_tree__can_cancel_checkout_from_notify(void) +{ + struct checkout_cancel_at ca; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_oid oid; + git_object *obj = NULL; + + assert_on_branch(g_repo, "master"); + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir")); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + + ca.filename = "new.txt"; + ca.error = -5555; + ca.count = 0; + + opts.notify_flags = GIT_CHECKOUT_NOTIFY_UPDATED; + opts.notify_cb = checkout_cancel_cb; + opts.notify_payload = &ca; + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_assert(!git_path_exists("testrepo/new.txt")); + + cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), -5555); + + cl_assert(!git_path_exists("testrepo/new.txt")); + + /* on case-insensitive FS = a/b.txt, branch_file.txt, new.txt */ + /* on case-sensitive FS = README, then above */ + + if (cl_repo_get_bool(g_repo, "core.ignorecase")) + cl_assert_equal_i(3, ca.count); + else + cl_assert_equal_i(4, ca.count); + + /* and again with a different stopping point and return code */ + ca.filename = "README"; + ca.error = 123; + ca.count = 0; + + cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), 123); + + cl_assert(!git_path_exists("testrepo/new.txt")); + + if (cl_repo_get_bool(g_repo, "core.ignorecase")) + cl_assert_equal_i(4, ca.count); + else + cl_assert_equal_i(1, ca.count); + + git_object_free(obj); +} + void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void) { git_index *index = NULL; -- cgit v1.2.1 From 8f1066a05f15ce0e3f91614cf9915162ce6447ee Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 10 Dec 2013 16:02:24 -0800 Subject: Update clone doc and tests for callback return val Clone callbacks can return non-zero values to cancel the clone. This adds some tests to verify that this actually works and updates the documentation to be clearer that this can happen and that the return value will be propagated back by the clone function. --- include/git2/clone.h | 57 ++++++++++++++++++++++++++++--------------------- src/clone.c | 3 ++- tests/clone/nonetwork.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 26 deletions(-) diff --git a/include/git2/clone.h b/include/git2/clone.h index 331cf92e7..59a73aa15 100644 --- a/include/git2/clone.h +++ b/include/git2/clone.h @@ -26,23 +26,25 @@ GIT_BEGIN_DECL /** * Clone options structure * - * Use zeros to indicate default settings. It's easiest to use the - * `GIT_CLONE_OPTIONS_INIT` macro: + * Use the GIT_CLONE_OPTIONS_INIT to get the default settings, like this: * * git_clone_options opts = GIT_CLONE_OPTIONS_INIT; * - * - `checkout_opts` is options for the checkout step. To disable checkout, - * set the `checkout_strategy` to GIT_CHECKOUT_DEFAULT. - * - `bare` should be set to zero to create a standard repo, non-zero for - * a bare repo - * - `ignore_cert_errors` should be set to 1 if errors validating the remote host's - * certificate should be ignored. + * - `checkout_opts` are option passed to the checkout step. To disable + * checkout, set the `checkout_strategy` to GIT_CHECKOUT_NONE. + * Generally you will want the use GIT_CHECKOUT_SAFE_CREATE to create + * all files in the working directory for the newly cloned repository. + * - `bare` should be set to zero (false) to create a standard repo, + * or non-zero for a bare repo + * - `ignore_cert_errors` should be set to 1 if errors validating the + * remote host's certificate should be ignored. * * ** "origin" remote options: ** - * - `remote_name` is the name given to the "origin" remote. The default is - * "origin". - * - `checkout_branch` gives the name of the branch to checkout. NULL means - * use the remote's HEAD. + * + * - `remote_name` is the name to be given to the "origin" remote. The + * default is "origin". + * - `checkout_branch` gives the name of the branch to checkout. NULL + * means use the remote's HEAD. */ typedef struct git_clone_options { @@ -70,16 +72,17 @@ typedef struct git_clone_options { * @param out pointer that will receive the resulting repository object * @param url the remote repository to clone * @param local_path local directory to clone to - * @param options configuration options for the clone. If NULL, the function - * works as though GIT_OPTIONS_INIT were passed. - * @return 0 on success, GIT_ERROR otherwise (use giterr_last for information - * about the error) + * @param options configuration options for the clone. If NULL, the + * function works as though GIT_OPTIONS_INIT were passed. + * @return 0 on success, any non-zero return value from a callback + * function, or a negative value to indicate an error (use + * `giterr_last` for a detailed error message) */ GIT_EXTERN(int) git_clone( - git_repository **out, - const char *url, - const char *local_path, - const git_clone_options *options); + git_repository **out, + const char *url, + const char *local_path, + const git_clone_options *options); /** * Clone into a repository @@ -91,11 +94,17 @@ GIT_EXTERN(int) git_clone( * @param repo the repository to use * @param remote the remote repository to clone from * @param co_opts options to use during checkout - * @param branch the branch to checkout after the clone, pass NULL for the remote's - * default branch - * @return 0 on success or an error code + * @param branch the branch to checkout after the clone, pass NULL for the + * remote's default branch + * @return 0 on success, any non-zero return value from a callback + * function, or a negative value to indicate an error (use + * `giterr_last` for a detailed error message) */ -GIT_EXTERN(int) git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch); +GIT_EXTERN(int) git_clone_into( + git_repository *repo, + git_remote *remote, + const git_checkout_opts *co_opts, + const char *branch); /** @} */ GIT_END_DECL diff --git a/src/clone.c b/src/clone.c index ffbe8f8af..828c47ffb 100644 --- a/src/clone.c +++ b/src/clone.c @@ -408,9 +408,10 @@ int git_clone( git_remote_free(origin); } - if (error < 0) { + if (error != 0) { git_repository_free(repo); repo = NULL; + (void)git_futils_rmdir_r(local_path, NULL, rmdir_flags); } diff --git a/tests/clone/nonetwork.c b/tests/clone/nonetwork.c index a286e2a8f..3cd5fb7f6 100644 --- a/tests/clone/nonetwork.c +++ b/tests/clone/nonetwork.c @@ -22,7 +22,7 @@ void test_clone_nonetwork__initialize(void) memset(&g_options, 0, sizeof(git_clone_options)); g_options.version = GIT_CLONE_OPTIONS_VERSION; g_options.checkout_opts = dummy_opts; - g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; g_options.remote_callbacks = dummy_callbacks; } @@ -151,6 +151,61 @@ void test_clone_nonetwork__can_checkout_given_branch(void) cl_git_pass(git_repository_head(&g_ref, g_repo)); cl_assert_equal_s(git_reference_name(g_ref), "refs/heads/test"); + + cl_assert(git_path_exists("foo/readme.txt")); +} + +static int clone_cancel_fetch_transfer_progress_cb( + const git_transfer_progress *stats, void *data) +{ + GIT_UNUSED(stats); GIT_UNUSED(data); + return -54321; +} + +void test_clone_nonetwork__can_cancel_clone_in_fetch(void) +{ + g_options.checkout_branch = "test"; + + g_options.remote_callbacks.transfer_progress = + clone_cancel_fetch_transfer_progress_cb; + + cl_git_fail_with(git_clone( + &g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options), + -54321); + + cl_assert(!g_repo); + cl_assert(!git_path_exists("foo/readme.txt")); +} + +static int clone_cancel_checkout_cb( + git_checkout_notify_t why, + const char *path, + const git_diff_file *b, + const git_diff_file *t, + const git_diff_file *w, + void *payload) +{ + const char *at_file = payload; + GIT_UNUSED(why); GIT_UNUSED(b); GIT_UNUSED(t); GIT_UNUSED(w); + if (!strcmp(path, at_file)) + return -12345; + return 0; +} + +void test_clone_nonetwork__can_cancel_clone_in_checkout(void) +{ + g_options.checkout_branch = "test"; + + g_options.checkout_opts.notify_flags = GIT_CHECKOUT_NOTIFY_UPDATED; + g_options.checkout_opts.notify_cb = clone_cancel_checkout_cb; + g_options.checkout_opts.notify_payload = "readme.txt"; + + cl_git_fail_with(git_clone( + &g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options), + -12345); + + cl_assert(!g_repo); + cl_assert(!git_path_exists("foo/readme.txt")); } void test_clone_nonetwork__can_detached_head(void) -- cgit v1.2.1 From 8046b26cb1e06ef7699d16395598754a8ec5564b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 10 Dec 2013 16:16:36 -0800 Subject: Try a test that won't assert on Linux --- tests/checkout/tree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index ac48bfcdd..d2e92f8e8 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -542,7 +542,7 @@ void test_checkout_tree__can_cancel_checkout_from_notify(void) /* on case-insensitive FS = a/b.txt, branch_file.txt, new.txt */ /* on case-sensitive FS = README, then above */ - if (cl_repo_get_bool(g_repo, "core.ignorecase")) + if (git_path_exists("testrepo/.git/CoNfIg")) /* case insensitive */ cl_assert_equal_i(3, ca.count); else cl_assert_equal_i(4, ca.count); @@ -556,7 +556,7 @@ void test_checkout_tree__can_cancel_checkout_from_notify(void) cl_assert(!git_path_exists("testrepo/new.txt")); - if (cl_repo_get_bool(g_repo, "core.ignorecase")) + if (git_path_exists("testrepo/.git/CoNfIg")) /* case insensitive */ cl_assert_equal_i(4, ca.count); else cl_assert_equal_i(1, ca.count); -- cgit v1.2.1 From 8b22d862fb4419b219210027f18c1e97dd36fa8b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 11 Dec 2013 11:55:00 -0800 Subject: More improvements to callback return value tests This time actually checking return values in diff notify tests and actually testing callbacks for the index all-all/update-all/etc functions. --- tests/diff/notify.c | 6 +- tests/index/addall.c | 158 +++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 132 insertions(+), 32 deletions(-) diff --git a/tests/diff/notify.c b/tests/diff/notify.c index f6accd004..da7390d3f 100644 --- a/tests/diff/notify.c +++ b/tests/diff/notify.c @@ -182,10 +182,12 @@ void test_diff_notify__notify_cb_can_abort_diff(void) opts.pathspec.count = 1; pathspec = "file_deleted"; - cl_git_fail(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + cl_git_fail_with( + git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42); pathspec = "staged_changes_modified_file"; - cl_git_fail(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + cl_git_fail_with( + git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42); } static int filter_all( diff --git a/tests/index/addall.c b/tests/index/addall.c index 44c51279d..452733710 100644 --- a/tests/index/addall.c +++ b/tests/index/addall.c @@ -4,6 +4,7 @@ #include "fileops.h" git_repository *g_repo = NULL; +#define TEST_DIR "addall" void test_index_addall__initialize(void) { @@ -13,6 +14,8 @@ void test_index_addall__cleanup(void) { git_repository_free(g_repo); g_repo = NULL; + + cl_fixture_cleanup(TEST_DIR); } #define STATUS_INDEX_FLAGS \ @@ -132,6 +135,25 @@ static void check_stat_data(git_index *index, const char *path, bool match) } } +static void addall_create_test_repo(bool check_every_step) +{ + cl_git_pass(git_repository_init(&g_repo, TEST_DIR, false)); + if (check_every_step) + check_status(g_repo, 0, 0, 0, 0, 0, 0, 0); + + cl_git_mkfile(TEST_DIR "/file.foo", "a file"); + if (check_every_step) + check_status(g_repo, 0, 0, 0, 1, 0, 0, 0); + + cl_git_mkfile(TEST_DIR "/.gitignore", "*.foo\n"); + if (check_every_step) + check_status(g_repo, 0, 0, 0, 1, 0, 0, 1); + + cl_git_mkfile(TEST_DIR "/file.bar", "another file"); + if (check_every_step) + check_status(g_repo, 0, 0, 0, 2, 0, 0, 1); +} + void test_index_addall__repo_lifecycle(void) { int error; @@ -139,43 +161,33 @@ void test_index_addall__repo_lifecycle(void) git_strarray paths = { NULL, 0 }; char *strs[1]; - cl_git_pass(git_repository_init(&g_repo, "addall", false)); - check_status(g_repo, 0, 0, 0, 0, 0, 0, 0); + addall_create_test_repo(true); cl_git_pass(git_repository_index(&index, g_repo)); - cl_git_mkfile("addall/file.foo", "a file"); - check_status(g_repo, 0, 0, 0, 1, 0, 0, 0); - - cl_git_mkfile("addall/.gitignore", "*.foo\n"); - check_status(g_repo, 0, 0, 0, 1, 0, 0, 1); - - cl_git_mkfile("addall/file.bar", "another file"); - check_status(g_repo, 0, 0, 0, 2, 0, 0, 1); - strs[0] = "file.*"; paths.strings = strs; paths.count = 1; cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); - check_stat_data(index, "addall/file.bar", true); + check_stat_data(index, TEST_DIR "/file.bar", true); check_status(g_repo, 1, 0, 0, 1, 0, 0, 1); - cl_git_rewritefile("addall/file.bar", "new content for file"); - check_stat_data(index, "addall/file.bar", false); + cl_git_rewritefile(TEST_DIR "/file.bar", "new content for file"); + check_stat_data(index, TEST_DIR "/file.bar", false); check_status(g_repo, 1, 0, 0, 1, 0, 1, 1); - cl_git_mkfile("addall/file.zzz", "yet another one"); - cl_git_mkfile("addall/other.zzz", "yet another one"); - cl_git_mkfile("addall/more.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/file.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/other.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/more.zzz", "yet another one"); check_status(g_repo, 1, 0, 0, 4, 0, 1, 1); cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); - check_stat_data(index, "addall/file.bar", true); + check_stat_data(index, TEST_DIR "/file.bar", true); check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); - check_stat_data(index, "addall/file.zzz", true); + check_stat_data(index, TEST_DIR "/file.zzz", true); check_status(g_repo, 2, 0, 0, 3, 0, 0, 1); cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "first commit"); @@ -195,27 +207,27 @@ void test_index_addall__repo_lifecycle(void) /* add with force - should allow */ cl_git_pass(git_index_add_all( index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL)); - check_stat_data(index, "addall/file.foo", true); + check_stat_data(index, TEST_DIR "/file.foo", true); check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); /* now it's in the index, so regular add should work */ - cl_git_rewritefile("addall/file.foo", "new content for file"); - check_stat_data(index, "addall/file.foo", false); + cl_git_rewritefile(TEST_DIR "/file.foo", "new content for file"); + check_stat_data(index, TEST_DIR "/file.foo", false); check_status(g_repo, 1, 0, 0, 3, 0, 1, 0); cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); - check_stat_data(index, "addall/file.foo", true); + check_stat_data(index, TEST_DIR "/file.foo", true); check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); cl_git_pass(git_index_add_bypath(index, "more.zzz")); - check_stat_data(index, "addall/more.zzz", true); + check_stat_data(index, TEST_DIR "/more.zzz", true); check_status(g_repo, 2, 0, 0, 2, 0, 0, 0); - cl_git_rewritefile("addall/file.zzz", "new content for file"); + cl_git_rewritefile(TEST_DIR "/file.zzz", "new content for file"); check_status(g_repo, 2, 0, 0, 2, 0, 1, 0); cl_git_pass(git_index_add_bypath(index, "file.zzz")); - check_stat_data(index, "addall/file.zzz", true); + check_stat_data(index, TEST_DIR "/file.zzz", true); check_status(g_repo, 2, 0, 1, 2, 0, 0, 0); strs[0] = "*.zzz"; @@ -228,7 +240,7 @@ void test_index_addall__repo_lifecycle(void) cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "second commit"); check_status(g_repo, 0, 0, 0, 3, 0, 0, 0); - cl_must_pass(p_unlink("addall/file.zzz")); + cl_must_pass(p_unlink(TEST_DIR "/file.zzz")); check_status(g_repo, 0, 0, 0, 3, 1, 0, 0); /* update_all should be able to remove entries */ @@ -240,9 +252,9 @@ void test_index_addall__repo_lifecycle(void) check_status(g_repo, 3, 1, 0, 0, 0, 0, 0); /* must be able to remove at any position while still updating other files */ - cl_must_pass(p_unlink("addall/.gitignore")); - cl_git_rewritefile("addall/file.zzz", "reconstructed file"); - cl_git_rewritefile("addall/more.zzz", "altered file reality"); + cl_must_pass(p_unlink(TEST_DIR "/.gitignore")); + cl_git_rewritefile(TEST_DIR "/file.zzz", "reconstructed file"); + cl_git_rewritefile(TEST_DIR "/more.zzz", "altered file reality"); check_status(g_repo, 3, 1, 0, 1, 1, 1, 0); cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); @@ -256,3 +268,89 @@ void test_index_addall__repo_lifecycle(void) git_index_free(index); } + +static int addall_match_prefix( + const char *path, const char *matched_pathspec, void *payload) +{ + GIT_UNUSED(matched_pathspec); + return !git__prefixcmp(path, payload) ? 0 : 1; +} + +static int addall_match_suffix( + const char *path, const char *matched_pathspec, void *payload) +{ + GIT_UNUSED(matched_pathspec); + return !git__suffixcmp(path, payload) ? 0 : 1; +} + +static int addall_cancel_at( + const char *path, const char *matched_pathspec, void *payload) +{ + GIT_UNUSED(matched_pathspec); + return !strcmp(path, payload) ? -123 : 0; +} + +void test_index_addall__callback_filtering(void) +{ + git_index *index; + + addall_create_test_repo(false); + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass( + git_index_add_all(index, NULL, 0, addall_match_prefix, "file.")); + check_stat_data(index, TEST_DIR "/file.bar", true); + check_status(g_repo, 1, 0, 0, 1, 0, 0, 1); + + cl_git_mkfile(TEST_DIR "/file.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/more.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/other.zzz", "yet another one"); + + cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); + check_stat_data(index, TEST_DIR "/file.bar", true); + check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); + + cl_git_pass( + git_index_add_all(index, NULL, 0, addall_match_prefix, "other")); + check_stat_data(index, TEST_DIR "/other.zzz", true); + check_status(g_repo, 2, 0, 0, 3, 0, 0, 1); + + cl_git_pass( + git_index_add_all(index, NULL, 0, addall_match_suffix, ".zzz")); + check_status(g_repo, 4, 0, 0, 1, 0, 0, 1); + + cl_git_pass( + git_index_remove_all(index, NULL, addall_match_suffix, ".zzz")); + check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); + + cl_git_fail_with( + git_index_add_all(index, NULL, 0, addall_cancel_at, "more.zzz"), -123); + check_status(g_repo, 3, 0, 0, 2, 0, 0, 1); + + cl_git_fail_with( + git_index_add_all(index, NULL, 0, addall_cancel_at, "other.zzz"), -123); + check_status(g_repo, 4, 0, 0, 1, 0, 0, 1); + + cl_git_pass( + git_index_add_all(index, NULL, 0, addall_match_suffix, ".zzz")); + check_status(g_repo, 5, 0, 0, 0, 0, 0, 1); + + cl_must_pass(p_unlink(TEST_DIR "/file.zzz")); + cl_must_pass(p_unlink(TEST_DIR "/more.zzz")); + cl_must_pass(p_unlink(TEST_DIR "/other.zzz")); + + cl_git_fail_with( + git_index_update_all(index, NULL, addall_cancel_at, "more.zzz"), -123); + /* file.zzz removed from index (so Index Adds 5 -> 4) and + * more.zzz + other.zzz removed (so Worktree Dels 0 -> 2) */ + check_status(g_repo, 4, 0, 0, 0, 2, 0, 1); + + cl_git_fail_with( + git_index_update_all(index, NULL, addall_cancel_at, "other.zzz"), -123); + /* more.zzz removed from index (so Index Adds 4 -> 3) and + * Just other.zzz removed (so Worktree Dels 2 -> 1) */ + check_status(g_repo, 3, 0, 0, 0, 1, 0, 1); + + git_index_free(index); +} -- cgit v1.2.1 From 7697e54176ccab22ed6d4597d7256e9a1e6ff202 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 11 Dec 2013 15:02:20 -0800 Subject: Test cancel from indexer progress callback This adds tests that try canceling an indexer operation from within the progress callback. After writing the tests, I wanted to run this under valgrind and had a number of errors in that situation because mmap wasn't working. I added a CMake option to force emulation of mmap and consolidated the Amiga-specific code into that new place (so we don't actually need separate Amiga code now, just have to turn on -DNO_MMAP). Additionally, I made the indexer code propagate error codes more reliably than it used to. --- CMakeLists.txt | 7 +++++-- include/git2/pack.h | 3 ++- src/amiga/map.c | 48 --------------------------------------------- src/indexer.c | 28 ++++++++++++++------------ src/posix.c | 36 ++++++++++++++++++++++++++++++++++ src/unix/map.c | 2 +- src/win32/map.c | 3 ++- tests/pack/indexer.c | 23 +++++++++++----------- tests/pack/packbuilder.c | 25 ++++++++++++++++++++--- tests/valgrind-supp-mac.txt | 8 -------- 10 files changed, 95 insertions(+), 88 deletions(-) delete mode 100644 src/amiga/map.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c19a5a79..48cbccb4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ OPTION( ANDROID "Build for android NDK" OFF ) OPTION( USE_ICONV "Link with and use iconv library" OFF ) OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) +OPTION( VALGRIND "Configure build for valgrind" OFF ) IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") SET( USE_ICONV ON ) @@ -340,9 +341,11 @@ IF (WIN32 AND NOT CYGWIN) ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0501) FILE(GLOB SRC_OS src/win32/*.c src/win32/*.h) ELSEIF (AMIGA) - ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R) - FILE(GLOB SRC_OS src/amiga/*.c src/amiga/*.h) + ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP) ELSE() + IF (VALGRIND) + ADD_DEFINITIONS(-DNO_MMAP) + ENDIF() FILE(GLOB SRC_OS src/unix/*.c src/unix/*.h) ENDIF() FILE(GLOB SRC_GIT2 src/*.c src/*.h src/transports/*.c src/transports/*.h src/xdiff/*.c src/xdiff/*.h) diff --git a/include/git2/pack.h b/include/git2/pack.h index 88a2716bb..11bb559d8 100644 --- a/include/git2/pack.h +++ b/include/git2/pack.h @@ -52,7 +52,7 @@ typedef enum { GIT_PACKBUILDER_ADDING_OBJECTS = 0, GIT_PACKBUILDER_DELTAFICATION = 1, } git_packbuilder_stage_t; - + /** * Initialize a new packbuilder * @@ -143,6 +143,7 @@ GIT_EXTERN(int) git_packbuilder_write( GIT_EXTERN(const git_oid *) git_packbuilder_hash(git_packbuilder *pb); typedef int (*git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload); + /** * Create the new pack and pass each object to the callback * diff --git a/src/amiga/map.c b/src/amiga/map.c deleted file mode 100644 index 0ba7995c6..000000000 --- a/src/amiga/map.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * 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 - -#ifndef GIT_WIN32 - -#include "posix.h" -#include "map.h" -#include - -int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) -{ - GIT_MMAP_VALIDATE(out, len, prot, flags); - - out->data = NULL; - out->len = 0; - - if ((prot & GIT_PROT_WRITE) && ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)) { - giterr_set(GITERR_OS, "Trying to map shared-writeable"); - return -1; - } - - out->data = malloc(len); - GITERR_CHECK_ALLOC(out->data); - - if ((p_lseek(fd, offset, SEEK_SET) < 0) || ((size_t)p_read(fd, out->data, len) != len)) { - giterr_set(GITERR_OS, "mmap emulation failed"); - return -1; - } - - out->len = len; - return 0; -} - -int p_munmap(git_map *map) -{ - assert(map != NULL); - free(map->data); - - return 0; -} - -#endif - diff --git a/src/indexer.c b/src/indexer.c index 88897d07d..718c69814 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -441,8 +441,8 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran processed = stats->indexed_objects; - if (git_filebuf_write(&idx->pack_file, data, size) < 0) - return -1; + if ((error = git_filebuf_write(&idx->pack_file, data, size)) < 0) + return error; hash_partially(idx, data, (int)size); @@ -450,12 +450,12 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran if (idx->opened_pack) { idx->pack->mwf.size += size; } else { - if (open_pack(&idx->pack, idx->pack_file.path_lock) < 0) - return -1; + if ((error = open_pack(&idx->pack, idx->pack_file.path_lock)) < 0) + return error; idx->opened_pack = 1; mwf = &idx->pack->mwf; - if (git_mwindow_file_register(&idx->pack->mwf) < 0) - return -1; + if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0) + return error; } if (!idx->parsed_header) { @@ -464,8 +464,8 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran if ((unsigned)idx->pack->mwf.size < sizeof(struct git_pack_header)) return 0; - if (parse_header(&idx->hdr, idx->pack) < 0) - return -1; + if ((error = parse_header(&idx->hdr, idx->pack)) < 0) + return error; idx->parsed_header = 1; idx->nr_objects = ntohl(hdr->hdr_entries); @@ -503,6 +503,7 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran /* 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_packfile_stream *stream = &idx->stream; git_off_t entry_start = idx->off; @@ -520,7 +521,7 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran return 0; } if (error < 0) - return error; + goto on_error; git_mwindow_close(&w); idx->entry_start = entry_start; @@ -533,7 +534,7 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran return 0; } if (error < 0) - return error; + goto on_error; idx->have_delta = 1; } else { @@ -542,9 +543,10 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran } idx->have_stream = 1; - if (git_packfile_stream_open(stream, idx->pack, idx->off) < 0) - goto on_error; + error = git_packfile_stream_open(stream, idx->pack, idx->off); + if (error < 0) + goto on_error; } if (idx->have_delta) { @@ -858,7 +860,7 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) /* Test for this before resolve_deltas(), as it plays with idx->off */ if (idx->off < idx->pack->mwf.size - 20) { - giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack"); + giterr_set(GITERR_INDEXER, "Unexpected data at the end of the pack"); return -1; } diff --git a/src/posix.c b/src/posix.c index b75109b83..525785f35 100644 --- a/src/posix.c +++ b/src/posix.c @@ -203,4 +203,40 @@ int p_write(git_file fd, const void *buf, size_t cnt) return 0; } +#ifdef NO_MMAP +#include "map.h" + +int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) +{ + GIT_MMAP_VALIDATE(out, len, prot, flags); + + out->data = NULL; + out->len = 0; + + if ((prot & GIT_PROT_WRITE) && ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)) { + giterr_set(GITERR_OS, "Trying to map shared-writeable"); + return -1; + } + + out->data = malloc(len); + GITERR_CHECK_ALLOC(out->data); + + if ((p_lseek(fd, offset, SEEK_SET) < 0) || ((size_t)p_read(fd, out->data, len) != len)) { + giterr_set(GITERR_OS, "mmap emulation failed"); + return -1; + } + + out->len = len; + return 0; +} + +int p_munmap(git_map *map) +{ + assert(map != NULL); + free(map->data); + + return 0; +} + +#endif diff --git a/src/unix/map.c b/src/unix/map.c index 7de99c99d..e62ab3e76 100644 --- a/src/unix/map.c +++ b/src/unix/map.c @@ -6,7 +6,7 @@ */ #include -#ifndef GIT_WIN32 +#if !defined(GIT_WIN32) && !defined(NO_MMAP) #include "map.h" #include diff --git a/src/win32/map.c b/src/win32/map.c index 44c6c4e2e..902ea3994 100644 --- a/src/win32/map.c +++ b/src/win32/map.c @@ -8,6 +8,7 @@ #include "map.h" #include +#ifndef NO_MMAP static DWORD get_page_size(void) { @@ -112,4 +113,4 @@ int p_munmap(git_map *map) return error; } - +#endif diff --git a/tests/pack/indexer.c b/tests/pack/indexer.c index 07963a9e7..084f8e666 100644 --- a/tests/pack/indexer.c +++ b/tests/pack/indexer.c @@ -11,7 +11,7 @@ * This is a packfile with three objects. The second is a delta which * depends on the third, which is also a delta. */ -unsigned char out_of_order_pack[] = { +static const unsigned char out_of_order_pack[] = { 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76, 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10, @@ -23,13 +23,13 @@ unsigned char out_of_order_pack[] = { 0x19, 0x87, 0x58, 0x80, 0x61, 0x09, 0x9a, 0x33, 0xca, 0x7a, 0x31, 0x92, 0x6f, 0xae, 0x66, 0x75 }; -unsigned int out_of_order_pack_len = 112; +static const unsigned int out_of_order_pack_len = 112; /* * Packfile with two objects. The second is a delta against an object * which is not in the packfile */ -unsigned char thin_pack[] = { +static const unsigned char thin_pack[] = { 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76, 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10, @@ -38,18 +38,19 @@ unsigned char thin_pack[] = { 0x3a, 0x6f, 0x39, 0xd1, 0xfe, 0x66, 0x68, 0x6b, 0xa5, 0xe5, 0xe2, 0x97, 0xac, 0x94, 0x6c, 0x76, 0x0b, 0x04 }; -unsigned int thin_pack_len = 78; +static const unsigned int thin_pack_len = 78; -unsigned char base_obj[] = { 07, 076 }; -unsigned int base_obj_len = 2; +static const unsigned char base_obj[] = { 07, 076 }; +static const unsigned int base_obj_len = 2; void test_pack_indexer__out_of_order(void) { - git_indexer *idx; - git_transfer_progress stats; + git_indexer *idx = 0; + git_transfer_progress stats = { 0 }; cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL)); - cl_git_pass(git_indexer_append(idx, out_of_order_pack, out_of_order_pack_len, &stats)); + cl_git_pass(git_indexer_append( + idx, out_of_order_pack, out_of_order_pack_len, &stats)); cl_git_pass(git_indexer_commit(idx, &stats)); cl_assert_equal_i(stats.total_objects, 3); @@ -61,8 +62,8 @@ void test_pack_indexer__out_of_order(void) void test_pack_indexer__fix_thin(void) { - git_indexer *idx; - git_transfer_progress stats; + git_indexer *idx = NULL; + git_transfer_progress stats = { 0 }; git_repository *repo; git_odb *odb; git_oid id, should_id; diff --git a/tests/pack/packbuilder.c b/tests/pack/packbuilder.c index 1ae2322a5..53db81828 100644 --- a/tests/pack/packbuilder.c +++ b/tests/pack/packbuilder.c @@ -12,6 +12,7 @@ static git_packbuilder *_packbuilder; static git_indexer *_indexer; static git_vector _commits; static int _commits_is_initialized; +static git_transfer_progress _stats; void test_pack_packbuilder__initialize(void) { @@ -20,6 +21,7 @@ void test_pack_packbuilder__initialize(void) cl_git_pass(git_packbuilder_new(&_packbuilder, _repo)); cl_git_pass(git_vector_init(&_commits, 0, NULL)); _commits_is_initialized = 1; + memset(&_stats, 0, sizeof(_stats)); } void test_pack_packbuilder__cleanup(void) @@ -184,11 +186,10 @@ void test_pack_packbuilder__permissions_readwrite(void) test_write_pack_permission(0666, 0666); } -static git_transfer_progress stats; static int foreach_cb(void *buf, size_t len, void *payload) { git_indexer *idx = (git_indexer *) payload; - cl_git_pass(git_indexer_append(idx, buf, len, &stats)); + cl_git_pass(git_indexer_append(idx, buf, len, &_stats)); return 0; } @@ -199,6 +200,24 @@ void test_pack_packbuilder__foreach(void) seed_packbuilder(); cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL)); cl_git_pass(git_packbuilder_foreach(_packbuilder, foreach_cb, idx)); - cl_git_pass(git_indexer_commit(idx, &stats)); + cl_git_pass(git_indexer_commit(idx, &_stats)); + git_indexer_free(idx); +} + +static int foreach_cancel_cb(void *buf, size_t len, void *payload) +{ + git_indexer *idx = (git_indexer *)payload; + cl_git_pass(git_indexer_append(idx, buf, len, &_stats)); + return (_stats.total_objects > 2) ? -1111 : 0; +} + +void test_pack_packbuilder__foreach_with_cancel(void) +{ + git_indexer *idx; + + seed_packbuilder(); + cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL)); + cl_git_fail_with( + git_packbuilder_foreach(_packbuilder, foreach_cancel_cb, idx), -1111); git_indexer_free(idx); } diff --git a/tests/valgrind-supp-mac.txt b/tests/valgrind-supp-mac.txt index 99833d091..0cdc975fa 100644 --- a/tests/valgrind-supp-mac.txt +++ b/tests/valgrind-supp-mac.txt @@ -102,14 +102,6 @@ ... fun:ssl23_connect } -{ - mac-ssl-uninitialized-4 - Memcheck:Param - ... - obj:/usr/lib/libcrypto.0.9.8.dylib - ... - fun:ssl23_connect -} { mac-ssl-leak-1 Memcheck:Leak -- cgit v1.2.1 From 7e3ed419593a2dc9fae3bd69fdf172de015d79d9 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 11 Dec 2013 16:56:17 -0800 Subject: Fix up some valgrind leaks and warnings --- src/diff_tform.c | 29 ++++++++++++++--------------- src/pack-objects.c | 13 ++++++++++++- src/path.c | 2 +- src/submodule.c | 2 +- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/diff_tform.c b/src/diff_tform.c index 16184910a..da4bdb3f5 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -280,11 +280,8 @@ static int normalize_find_opts( git_repository_config__weakptr(&cfg, diff->repo) < 0) return -1; - if (given) { + if (given) memcpy(opts, given, sizeof(*opts)); - } else { - GIT_INIT_STRUCTURE(opts, GIT_DIFF_FIND_OPTIONS_VERSION); - } if (!given || (given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG) @@ -815,11 +812,11 @@ int git_diff_find_similar( int error = 0, result; uint16_t similarity; git_diff_delta *src, *tgt; - git_diff_find_options opts; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; size_t num_deltas, num_srcs = 0, num_tgts = 0; size_t tried_srcs = 0, tried_tgts = 0; size_t num_rewrites = 0, num_updates = 0, num_bumped = 0; - void **sigcache; /* cache of similarity metric file signatures */ + void **sigcache = NULL; /* cache of similarity metric file signatures */ diff_find_match *tgt2src = NULL; diff_find_match *src2tgt = NULL; diff_find_match *tgt2src_copy = NULL; @@ -829,15 +826,15 @@ int git_diff_find_similar( if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0) return error; - /* No flags set; nothing to do */ - if ((opts.flags & GIT_DIFF_FIND_ALL) == 0) - return 0; - num_deltas = diff->deltas.length; /* TODO: maybe abort if deltas.length > rename_limit ??? */ if (!git__is_uint32(num_deltas)) - return 0; + goto cleanup; + + /* No flags set; nothing to do */ + if ((opts.flags & GIT_DIFF_FIND_ALL) == 0) + goto cleanup; sigcache = git__calloc(num_deltas * 2, sizeof(void *)); GITERR_CHECK_ALLOC(sigcache); @@ -1112,11 +1109,13 @@ cleanup: git__free(src2tgt); git__free(tgt2src_copy); - for (t = 0; t < num_deltas * 2; ++t) { - if (sigcache[t] != NULL) - opts.metric->free_signature(sigcache[t], opts.metric->payload); + if (sigcache) { + for (t = 0; t < num_deltas * 2; ++t) { + if (sigcache[t] != NULL) + opts.metric->free_signature(sigcache[t], opts.metric->payload); + } + git__free(sigcache); } - git__free(sigcache); if (!given_opts || !given_opts->metric) git__free(opts.metric); diff --git a/src/pack-objects.c b/src/pack-objects.c index 7ce1b6cb3..335944c0c 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -332,8 +332,10 @@ static int write_object(git_buf *buf, git_packbuilder *pb, git_pobject *po) git_hash_update(&pb->ctx, data, size) < 0) goto on_error; - if (po->delta_data) + if (po->delta_data) { git__free(po->delta_data); + po->delta_data = NULL; + } git_odb_object_free(obj); git_buf_free(&zbuf); @@ -612,6 +614,15 @@ static int write_pack(git_packbuilder *pb, error = cb(entry_oid.id, GIT_OID_RAWSZ, data); done: + /* if callback cancelled writing, we must still free delta_data */ + for ( ; i < pb->nr_objects; ++i) { + po = write_order[i]; + if (po->delta_data) { + git__free(po->delta_data); + po->delta_data = NULL; + } + } + git__free(write_order); git_buf_free(&buf); return error; diff --git a/src/path.c b/src/path.c index 365bd6c00..feb273915 100644 --- a/src/path.c +++ b/src/path.c @@ -784,7 +784,7 @@ int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen) return 0; while (1) { - if (git_buf_grow(&ic->buf, wantlen) < 0) + if (git_buf_grow(&ic->buf, wantlen + 1) < 0) return -1; nfc = ic->buf.ptr + ic->buf.size; diff --git a/src/submodule.c b/src/submodule.c index 1c36d3656..f6660a87e 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -1148,7 +1148,7 @@ static int submodule_load_from_config( */ if (path) - return 0; + goto done; /* copy other properties into submodule entry */ if (strcasecmp(property, "url") == 0) { -- cgit v1.2.1 From 11bd7a034ba9046a7ba601c446e937377d507065 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 12 Dec 2013 11:14:51 -0800 Subject: More tests of canceling from callbacks This covers diff print, push, and ref foreach. This also has a fix for a small memory leak in the push tests. --- tests/diff/patch.c | 39 ++++++++++++++-- tests/online/push.c | 63 ++++++++++++++++--------- tests/refs/iterator.c | 127 +++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 186 insertions(+), 43 deletions(-) diff --git a/tests/diff/patch.c b/tests/diff/patch.c index bd1598b21..0cef3bd3a 100644 --- a/tests/diff/patch.c +++ b/tests/diff/patch.c @@ -30,8 +30,6 @@ static int check_removal_cb( const git_diff_line *line, void *payload) { - GIT_UNUSED(payload); - switch (line->origin) { case GIT_DIFF_LINE_FILE_HDR: cl_assert_equal_s(EXPECTED_HEADER, line->content); @@ -40,10 +38,12 @@ static int check_removal_cb( case GIT_DIFF_LINE_HUNK_HDR: cl_assert_equal_s(EXPECTED_HUNK, line->content); - /* Fall through */ + goto check_hunk; case GIT_DIFF_LINE_CONTEXT: case GIT_DIFF_LINE_DELETION: + if (payload != NULL) + return *(int *)payload; goto check_hunk; default: @@ -101,6 +101,39 @@ void test_diff_patch__can_properly_display_the_removal_of_a_file(void) git_tree_free(one); } +void test_diff_patch__can_cancel_diff_print(void) +{ + const char *one_sha = "26a125e"; + const char *another_sha = "735b6a2"; + git_tree *one, *another; + git_diff *diff; + int fail_with; + + g_repo = cl_git_sandbox_init("status"); + + one = resolve_commit_oid_to_tree(g_repo, one_sha); + another = resolve_commit_oid_to_tree(g_repo, another_sha); + + cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL)); + + fail_with = -2323; + + cl_git_fail_with(git_diff_print( + diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, &fail_with), + fail_with); + + fail_with = 45; + + cl_git_fail_with(git_diff_print( + diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, &fail_with), + fail_with); + + git_diff_free(diff); + + git_tree_free(another); + git_tree_free(one); +} + void test_diff_patch__to_string(void) { const char *one_sha = "26a125e"; diff --git a/tests/online/push.c b/tests/online/push.c index be505c3a1..33f174654 100644 --- a/tests/online/push.c +++ b/tests/online/push.c @@ -207,6 +207,7 @@ static void verify_tracking_branches(git_remote *remote, expected_ref expected_r } cl_assert_equal_i(error, GIT_ITEROVER); + git_branch_iterator_free(iter); /* Loop through expected refs, make sure they exist */ for (i = 0; i < expected_refs_len; i++) { @@ -371,19 +372,25 @@ void test_online_push__cleanup(void) cl_git_sandbox_cleanup(); } -static int push_pack_progress_cb(int stage, unsigned int current, unsigned int total, void* payload) +static int push_pack_progress_cb( + int stage, unsigned int current, unsigned int total, void* payload) { - int *was_called = (int *) payload; + int *calls = (int *)payload; GIT_UNUSED(stage); GIT_UNUSED(current); GIT_UNUSED(total); - *was_called = 1; + if (*calls < 0) + return *calls; + (*calls)++; return 0; } -static int push_transfer_progress_cb(unsigned int current, unsigned int total, size_t bytes, void* payload) +static int push_transfer_progress_cb( + unsigned int current, unsigned int total, size_t bytes, void* payload) { - int *was_called = (int *) payload; + int *calls = (int *)payload; GIT_UNUSED(current); GIT_UNUSED(total); GIT_UNUSED(bytes); - *was_called = 1; + if (*calls < 0) + return *calls; + (*calls)++; return 0; } @@ -397,15 +404,16 @@ static int push_transfer_progress_cb(unsigned int current, unsigned int total, s * @param expected_ret expected return value from git_push_finish() * @param check_progress_cb Check that the push progress callbacks are called */ -static void do_push(const char *refspecs[], size_t refspecs_len, +static void do_push( + const char *refspecs[], size_t refspecs_len, push_status expected_statuses[], size_t expected_statuses_len, - expected_ref expected_refs[], size_t expected_refs_len, int expected_ret, int check_progress_cb) + expected_ref expected_refs[], size_t expected_refs_len, + int expected_ret, int check_progress_cb) { git_push *push; git_push_options opts = GIT_PUSH_OPTIONS_INIT; size_t i; - int ret; - int pack_progress_called = 0, transfer_progress_called = 0; + int pack_progress_calls = 0, transfer_progress_calls = 0; if (_remote) { /* Auto-detect the number of threads to use */ @@ -416,30 +424,35 @@ static void do_push(const char *refspecs[], size_t refspecs_len, cl_git_pass(git_push_new(&push, _remote)); cl_git_pass(git_push_set_options(push, &opts)); - if (check_progress_cb) - cl_git_pass(git_push_set_callbacks(push, push_pack_progress_cb, &pack_progress_called, push_transfer_progress_cb, &transfer_progress_called)); + if (check_progress_cb) { + /* if EUSER, then abort in transfer */ + if (expected_ret == GIT_EUSER) + transfer_progress_calls = GIT_EUSER; + + cl_git_pass( + git_push_set_callbacks( + push, push_pack_progress_cb, &pack_progress_calls, + push_transfer_progress_cb, &transfer_progress_calls)); + } for (i = 0; i < refspecs_len; i++) cl_git_pass(git_push_add_refspec(push, refspecs[i])); if (expected_ret < 0) { - cl_git_fail(ret = git_push_finish(push)); + cl_git_fail_with(git_push_finish(push), expected_ret); cl_assert_equal_i(0, git_push_unpack_ok(push)); - } - else { - cl_git_pass(ret = git_push_finish(push)); + } else { + cl_git_pass(git_push_finish(push)); cl_assert_equal_i(1, git_push_unpack_ok(push)); } - if (check_progress_cb) { - cl_assert_equal_i(1, pack_progress_called); - cl_assert_equal_i(1, transfer_progress_called); + if (check_progress_cb && !expected_ret) { + cl_assert(pack_progress_calls > 0); + cl_assert(transfer_progress_calls > 0); } do_verify_push_status(push, expected_statuses, expected_statuses_len); - cl_assert_equal_i(expected_ret, ret); - verify_refs(_remote, expected_refs, expected_refs_len); cl_git_pass(git_push_update_tips(push)); @@ -507,6 +520,12 @@ void test_online_push__b5(void) exp_refs, ARRAY_SIZE(exp_refs), 0, 1); } +void test_online_push__b5_cancel(void) +{ + const char *specs[] = { "refs/heads/b5:refs/heads/b5" }; + do_push(specs, ARRAY_SIZE(specs), NULL, 0, NULL, 0, GIT_EUSER, 1); +} + void test_online_push__multi(void) { const char *specs[] = { @@ -731,7 +750,7 @@ void test_online_push__bad_refspecs(void) git_push *push; if (_remote) { -// cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH)); +/* cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH)); */ cl_git_pass(git_push_new(&push, _remote)); /* Unexpanded branch names not supported */ diff --git a/tests/refs/iterator.c b/tests/refs/iterator.c index 266410fdf..a29b0cf8b 100644 --- a/tests/refs/iterator.c +++ b/tests/refs/iterator.c @@ -46,36 +46,43 @@ static int refcmp_cb(const void *a, const void *b) return strcmp(refa->name, refb->name); } +static void assert_all_refnames_match(git_vector *output) +{ + size_t i; + git_reference *ref; + + cl_assert_equal_sz(output->length, ARRAY_SIZE(refnames)); + + git_vector_sort(output); + + git_vector_foreach(output, i, ref) { + cl_assert_equal_s(ref->name, refnames[i]); + git_reference_free(ref); + } + + git_vector_free(output); +} + void test_refs_iterator__list(void) { git_reference_iterator *iter; git_vector output; git_reference *ref; - int error; - size_t i; cl_git_pass(git_vector_init(&output, 32, &refcmp_cb)); cl_git_pass(git_reference_iterator_new(&iter, repo)); - do { - error = git_reference_next(&ref, iter); - cl_assert(error == 0 || error == GIT_ITEROVER); - if (error != GIT_ITEROVER) { - cl_git_pass(git_vector_insert(&output, ref)); - } - } while (!error); + while (1) { + int error = git_reference_next(&ref, iter); + if (error == GIT_ITEROVER) + break; + cl_git_pass(error); + cl_git_pass(git_vector_insert(&output, ref)); + } git_reference_iterator_free(iter); - cl_assert_equal_sz(output.length, ARRAY_SIZE(refnames)); - git_vector_sort(&output); - - git_vector_foreach(&output, i, ref) { - cl_assert_equal_s(ref->name, refnames[i]); - git_reference_free(ref); - } - - git_vector_free(&output); + assert_all_refnames_match(&output); } void test_refs_iterator__empty(void) @@ -95,3 +102,87 @@ void test_refs_iterator__empty(void) git_odb_free(odb); git_repository_free(empty); } + +static int refs_foreach_cb(git_reference *reference, void *payload) +{ + git_vector *output = payload; + cl_git_pass(git_vector_insert(output, reference)); + return 0; +} + +void test_refs_iterator__foreach(void) +{ + git_vector output; + cl_git_pass(git_vector_init(&output, 32, &refcmp_cb)); + cl_git_pass(git_reference_foreach(repo, refs_foreach_cb, &output)); + assert_all_refnames_match(&output); +} + +static int refs_foreach_cancel_cb(git_reference *reference, void *payload) +{ + int *cancel_after = payload; + + git_reference_free(reference); + + if (!*cancel_after) + return -333; + (*cancel_after)--; + return 0; +} + +void test_refs_iterator__foreach_can_cancel(void) +{ + int cancel_after = 3; + cl_git_fail_with( + git_reference_foreach(repo, refs_foreach_cancel_cb, &cancel_after), + -333); + cl_assert_equal_i(0, cancel_after); +} + +static int refs_foreach_name_cb(const char *name, void *payload) +{ + git_vector *output = payload; + cl_git_pass(git_vector_insert(output, git__strdup(name))); + return 0; +} + +void test_refs_iterator__foreach_name(void) +{ + git_vector output; + size_t i; + char *name; + + cl_git_pass(git_vector_init(&output, 32, &git__strcmp_cb)); + cl_git_pass( + git_reference_foreach_name(repo, refs_foreach_name_cb, &output)); + + cl_assert_equal_sz(output.length, ARRAY_SIZE(refnames)); + git_vector_sort(&output); + + git_vector_foreach(&output, i, name) { + cl_assert_equal_s(name, refnames[i]); + git__free(name); + } + + git_vector_free(&output); +} + +static int refs_foreach_name_cancel_cb(const char *name, void *payload) +{ + int *cancel_after = payload; + if (!*cancel_after) + return -333; + GIT_UNUSED(name); + (*cancel_after)--; + return 0; +} + +void test_refs_iterator__foreach_name_can_cancel(void) +{ + int cancel_after = 5; + cl_git_fail_with( + git_reference_foreach_name( + repo, refs_foreach_name_cancel_cb, &cancel_after), + -333); + cl_assert_equal_i(0, cancel_after); +} -- cgit v1.2.1 From 9cfce2735d77f4d8b6005e62349dd97c0c6de5ab Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 12 Dec 2013 12:11:38 -0800 Subject: Cleanups, renames, and leak fixes This renames git_vector_free_all to the better git_vector_free_deep and also contains a couple of memory leak fixes based on valgrind checks. The fixes are specifically: failure to free global dir path variables when not compiled with threading on and failure to free filters from the filter registry that had not be initialized fully. --- include/git2/sys/filter.h | 1 + src/blame.c | 2 +- src/checkout.c | 2 +- src/diff.c | 2 +- src/diff_tform.c | 4 ++-- src/fileops.c | 13 ++++++++++--- src/filter.c | 2 +- src/indexer.c | 4 ++-- src/iterator.c | 2 +- src/merge.c | 2 +- src/pathspec.c | 2 +- src/push.c | 2 +- src/remote.c | 4 ++-- src/status.c | 2 +- src/transports/http.c | 2 +- src/vector.c | 2 +- src/vector.h | 2 +- 17 files changed, 29 insertions(+), 21 deletions(-) diff --git a/include/git2/sys/filter.h b/include/git2/sys/filter.h index 94ad3aed4..8fe21c9c0 100644 --- a/include/git2/sys/filter.h +++ b/include/git2/sys/filter.h @@ -149,6 +149,7 @@ typedef int (*git_filter_init_fn)(git_filter *self); * Specified as `filter.shutdown`, this is an optional callback invoked * when the filter is unregistered or when libgit2 is shutting down. It * will be called once at most and should release resources as needed. + * This may be called even if the `initialize` callback was not made. * * Typically this function will free the `git_filter` object itself. */ diff --git a/src/blame.c b/src/blame.c index f10ed409a..a1357415a 100644 --- a/src/blame.c +++ b/src/blame.c @@ -139,7 +139,7 @@ void git_blame_free(git_blame *blame) free_hunk(hunk); git_vector_free(&blame->hunks); - git_vector_free_all(&blame->paths); + git_vector_free_deep(&blame->paths); git_array_clear(blame->line_index); diff --git a/src/checkout.c b/src/checkout.c index f7dd052c7..0f30d16f3 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1765,7 +1765,7 @@ static void checkout_data_clear(checkout_data *data) git_vector_free(&data->removes); git_pool_clear(&data->pool); - git_vector_free_all(&data->conflicts); + git_vector_free_deep(&data->conflicts); git__free(data->pfx); data->pfx = NULL; diff --git a/src/diff.c b/src/diff.c index 83adc2a8c..7f2e58c0c 100644 --- a/src/diff.c +++ b/src/diff.c @@ -476,7 +476,7 @@ static int diff_list_apply_options( static void diff_list_free(git_diff *diff) { - git_vector_free_all(&diff->deltas); + git_vector_free_deep(&diff->deltas); git_pathspec__vfree(&diff->pathspec); git_pool_clear(&diff->pool); diff --git a/src/diff_tform.c b/src/diff_tform.c index da4bdb3f5..263a64d12 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -209,7 +209,7 @@ int git_diff_merge(git_diff *onto, const git_diff *from) git_pool_strdup_safe(&onto->pool, onto->opts.new_prefix); } - git_vector_free_all(&onto_new); + git_vector_free_deep(&onto_new); git_pool_clear(&onto_pool); return error; @@ -440,7 +440,7 @@ static int apply_splits_and_deletes( return 0; on_error: - git_vector_free_all(&onto); + git_vector_free_deep(&onto); return -1; } diff --git a/src/fileops.c b/src/fileops.c index 98dcd3269..a60689f3f 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -615,6 +615,8 @@ static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = { git_futils_guess_template_dirs, }; +static int git_futils__dirs_shutdown_set = 0; + void git_futils_dirs_global_shutdown(void) { int i; @@ -631,8 +633,6 @@ int git_futils_dirs_global_init(void) for (i = 0; !error && i < GIT_FUTILS_DIR__MAX; i++) error = git_futils_dirs_get(&path, i); - git__on_shutdown(git_futils_dirs_global_shutdown); - return error; } @@ -652,9 +652,16 @@ int git_futils_dirs_get(const git_buf **out, git_futils_dir_t which) GITERR_CHECK_ERROR(git_futils_check_selector(which)); - if (!git_buf_len(&git_futils__dirs[which])) + if (!git_buf_len(&git_futils__dirs[which])) { + /* prepare shutdown if we're going to need it */ + if (!git_futils__dirs_shutdown_set) { + git__on_shutdown(git_futils_dirs_global_shutdown); + git_futils__dirs_shutdown_set = 1; + } + GITERR_CHECK_ERROR( git_futils__dir_guess[which](&git_futils__dirs[which])); + } *out = &git_futils__dirs[which]; return 0; diff --git a/src/filter.c b/src/filter.c index 9f866fe88..ff81eb14e 100644 --- a/src/filter.c +++ b/src/filter.c @@ -69,7 +69,7 @@ static void filter_registry_shutdown(void) return; git_vector_foreach(®->filters, pos, fdef) { - if (fdef->initialized && fdef->filter && fdef->filter->shutdown) { + if (fdef->filter && fdef->filter->shutdown) { fdef->filter->shutdown(fdef->filter); fdef->initialized = false; } diff --git a/src/indexer.c b/src/indexer.c index 718c69814..6132571cc 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -1010,7 +1010,7 @@ void git_indexer_free(git_indexer *idx) if (idx == NULL) return; - git_vector_free_all(&idx->objects); + git_vector_free_deep(&idx->objects); if (idx->pack) { struct git_pack_entry *pentry; @@ -1020,7 +1020,7 @@ void git_indexer_free(git_indexer *idx) git_oidmap_free(idx->pack->idx_cache); } - git_vector_free_all(&idx->deltas); + git_vector_free_deep(&idx->deltas); git_packfile_free(idx->pack); git_filebuf_cleanup(&idx->pack_file); git__free(idx); diff --git a/src/iterator.c b/src/iterator.c index 118bbb880..0e7d0db85 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -920,7 +920,7 @@ static fs_iterator_frame *fs_iterator__alloc_frame(fs_iterator *fi) static void fs_iterator__free_frame(fs_iterator_frame *ff) { - git_vector_free_all(&ff->entries); + git_vector_free_deep(&ff->entries); git__free(ff); } diff --git a/src/merge.c b/src/merge.c index 5640be56b..d6db19243 100644 --- a/src/merge.c +++ b/src/merge.c @@ -2383,7 +2383,7 @@ done: git_index_set_caps(index_repo, index_repo_caps); git_index_free(index_repo); - git_vector_free_all(&paths); + git_vector_free_deep(&paths); return error; } diff --git a/src/pathspec.c b/src/pathspec.c index f16e19f47..bad8dacdb 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -102,7 +102,7 @@ int git_pathspec__vinit( /* free data from the pathspec vector */ void git_pathspec__vfree(git_vector *vspec) { - git_vector_free_all(vspec); + git_vector_free_deep(vspec); } struct pathspec_match_context { diff --git a/src/push.c b/src/push.c index adba880df..dd77864a5 100644 --- a/src/push.c +++ b/src/push.c @@ -541,7 +541,7 @@ static int queue_objects(git_push *push) error = 0; on_error: - git_vector_free_all(&commits); + git_vector_free_deep(&commits); return error; } diff --git a/src/remote.c b/src/remote.c index 689de230a..294a8709d 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1138,7 +1138,7 @@ int git_remote_list(git_strarray *remotes_list, git_repository *repo) cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list); if (error < 0) { - git_vector_free_all(&list); + git_vector_free_deep(&list); return error; } @@ -1617,7 +1617,7 @@ static int copy_refspecs(git_strarray *array, git_remote *remote, unsigned int p return 0; on_error: - git_vector_free_all(&refspecs); + git_vector_free_deep(&refspecs); return -1; } diff --git a/src/status.c b/src/status.c index 9bde8fb57..7a1472d8d 100644 --- a/src/status.c +++ b/src/status.c @@ -367,7 +367,7 @@ void git_status_list_free(git_status_list *status) git_diff_free(status->head2idx); git_diff_free(status->idx2wd); - git_vector_free_all(&status->paired); + git_vector_free_deep(&status->paired); git__memzero(status, sizeof(*status)); git__free(status); diff --git a/src/transports/http.c b/src/transports/http.c index 0e1bbf60d..c6aaeb9cf 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -404,7 +404,7 @@ static void clear_parser_state(http_subtransport *t) git__free(t->location); t->location = NULL; - git_vector_free_all(&t->www_authenticate); + git_vector_free_deep(&t->www_authenticate); } static int write_chunk(gitno_socket *socket, const char *buffer, size_t len) diff --git a/src/vector.c b/src/vector.c index b1ea89606..050e032a0 100644 --- a/src/vector.c +++ b/src/vector.c @@ -77,7 +77,7 @@ void git_vector_free(git_vector *v) v->_alloc_size = 0; } -void git_vector_free_all(git_vector *v) +void git_vector_free_deep(git_vector *v) { size_t i; diff --git a/src/vector.h b/src/vector.h index defe22466..d318463c6 100644 --- a/src/vector.h +++ b/src/vector.h @@ -23,7 +23,7 @@ typedef struct git_vector { int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp); void git_vector_free(git_vector *v); -void git_vector_free_all(git_vector *v); /* free each entry and self */ +void git_vector_free_deep(git_vector *v); /* free each entry and self */ void git_vector_clear(git_vector *v); int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp); void git_vector_swap(git_vector *a, git_vector *b); -- cgit v1.2.1 From 452c7de668568f75a97b0438daab9f33b68d605a Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 12 Dec 2013 14:16:40 -0800 Subject: Add git_treebuilder_insert test and clarify doc This wasn't being tested and since it has a callback, I fixed it even though the return value of this callback is not treated like any of the other callbacks in the API. --- include/git2/tree.h | 11 +++++-- tests/object/tree/write.c | 84 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 78 insertions(+), 17 deletions(-) diff --git a/include/git2/tree.h b/include/git2/tree.h index d94b446c2..422365674 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -332,11 +332,18 @@ GIT_EXTERN(int) git_treebuilder_insert( GIT_EXTERN(int) git_treebuilder_remove( git_treebuilder *bld, const char *filename); +/** + * Callback for git_treebuilder_filter + * + * The return value is treated as a boolean, with zero indicating that the + * entry should be left alone and any non-zero value meaning that the + * entry should be removed from the treebuilder list (i.e. filtered out). + */ typedef int (*git_treebuilder_filter_cb)( const git_tree_entry *entry, void *payload); /** - * Filter the entries in the tree + * Selectively remove entries in the tree * * The `filter` callback will be called for each entry in the tree with a * pointer to the entry and the provided `payload`; if the callback returns @@ -344,7 +351,7 @@ typedef int (*git_treebuilder_filter_cb)( * * @param bld Tree builder * @param filter Callback to filter entries - * @param payload Extra data to pass to filter + * @param payload Extra data to pass to filter callback */ GIT_EXTERN(void) git_treebuilder_filter( git_treebuilder *bld, diff --git a/tests/object/tree/write.c b/tests/object/tree/write.c index 468c0ccd1..3bea0ed4d 100644 --- a/tests/object/tree/write.c +++ b/tests/object/tree/write.c @@ -164,24 +164,25 @@ void test_object_tree_write__sorted_subtrees(void) git_treebuilder_free(builder); } +static struct { + unsigned int attr; + const char *filename; +} _entries[] = { + { GIT_FILEMODE_BLOB, "aardvark" }, + { GIT_FILEMODE_BLOB, ".first" }, + { GIT_FILEMODE_BLOB, "apple" }, + { GIT_FILEMODE_BLOB, "last"}, + { GIT_FILEMODE_BLOB, "apple_after"}, + { GIT_FILEMODE_BLOB, "after_aardvark"}, + { 0, NULL }, +}; + void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) { git_treebuilder *builder; - int i, aardvark_i, apple_i, apple_after_i, apple_extra_i, last_i; + int i, aardvark_i, apple_i, apple_after_i, apple_extra_i, last_i; git_oid blank_oid, tree_oid; git_tree *tree; - struct { - unsigned int attr; - const char *filename; - } entries[] = { - { GIT_FILEMODE_BLOB, "aardvark" }, - { GIT_FILEMODE_BLOB, ".first" }, - { GIT_FILEMODE_BLOB, "apple" }, - { GIT_FILEMODE_BLOB, "last"}, - { GIT_FILEMODE_BLOB, "apple_after"}, - { GIT_FILEMODE_BLOB, "after_aardvark"}, - { 0, NULL }, - }; memset(&blank_oid, 0x0, sizeof(blank_oid)); @@ -189,9 +190,9 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) cl_assert_equal_i(0, (int)git_treebuilder_entrycount(builder)); - for (i = 0; entries[i].filename; ++i) + for (i = 0; _entries[i].filename; ++i) cl_git_pass(git_treebuilder_insert(NULL, - builder, entries[i].filename, &blank_oid, entries[i].attr)); + builder, _entries[i].filename, &blank_oid, _entries[i].attr)); cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); @@ -260,3 +261,56 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) git_tree_free(tree); } + +static int treebuilder_filter_prefixed( + const git_tree_entry *entry, void *payload) +{ + return !git__prefixcmp(git_tree_entry_name(entry), payload); +} + +void test_object_tree_write__filtering(void) +{ + git_treebuilder *builder; + int i; + git_oid blank_oid, tree_oid; + git_tree *tree; + + memset(&blank_oid, 0x0, sizeof(blank_oid)); + + cl_git_pass(git_treebuilder_create(&builder, NULL)); + + for (i = 0; _entries[i].filename; ++i) + cl_git_pass(git_treebuilder_insert(NULL, + builder, _entries[i].filename, &blank_oid, _entries[i].attr)); + + cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); + + cl_assert(git_treebuilder_get(builder, "apple") != NULL); + cl_assert(git_treebuilder_get(builder, "aardvark") != NULL); + cl_assert(git_treebuilder_get(builder, "last") != NULL); + + git_treebuilder_filter(builder, treebuilder_filter_prefixed, "apple"); + + cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder)); + + cl_assert(git_treebuilder_get(builder, "apple") == NULL); + cl_assert(git_treebuilder_get(builder, "aardvark") != NULL); + cl_assert(git_treebuilder_get(builder, "last") != NULL); + + git_treebuilder_filter(builder, treebuilder_filter_prefixed, "a"); + + cl_assert_equal_i(2, (int)git_treebuilder_entrycount(builder)); + + cl_assert(git_treebuilder_get(builder, "aardvark") == NULL); + cl_assert(git_treebuilder_get(builder, "last") != NULL); + + cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder)); + + git_treebuilder_free(builder); + + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid)); + + cl_assert_equal_i(2, (int)git_tree_entrycount(tree)); + + git_tree_free(tree); +} -- cgit v1.2.1 From ce33645ff32d68dfd89867468f58fd9c245c26ff Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 13 Dec 2013 12:25:48 +0100 Subject: pool: Cleanup error handling in pool_strdup Note that `git_pool_strdup` cannot really return any error codes, because the pool doesn't set errors on OOM. The only place where `giterr_set_oom` is called is in `git_pool_strndup`, in a conditional check that is always optimized away. `n + 1` cannot be zero if `n` is unsigned because the compiler doesn't take wraparound into account. This check has been removed altogether because `size_t` is not particularly going to overflow. --- src/pool.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/pool.c b/src/pool.c index 4796d0a81..a23641145 100644 --- a/src/pool.c +++ b/src/pool.c @@ -190,19 +190,15 @@ void *git_pool_malloc(git_pool *pool, uint32_t items) char *git_pool_strndup(git_pool *pool, const char *str, size_t n) { - void *ptr = NULL; + char *ptr = NULL; assert(pool && str && pool->item_size == sizeof(char)); - if (n + 1 == 0) { - giterr_set_oom(); - return NULL; - } - if ((ptr = git_pool_malloc(pool, (uint32_t)(n + 1))) != NULL) { memcpy(ptr, str, n); - *(((char *)ptr) + n) = '\0'; + ptr[n] = '\0'; } + pool->has_string_alloc = 1; return ptr; @@ -217,14 +213,7 @@ char *git_pool_strdup(git_pool *pool, const char *str) char *git_pool_strdup_safe(git_pool *pool, const char *str) { - if (!str) - return NULL; - else { - char *result = git_pool_strdup(pool, str); - if (!result) - giterr_clear(); - return result; - } + return str ? git_pool_strdup(pool, str) : NULL; } char *git_pool_strcat(git_pool *pool, const char *a, const char *b) -- cgit v1.2.1 From 437f7d69b22324d20fe833aa53119885733029c2 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 13 Dec 2013 12:41:22 +0100 Subject: pool: Correct overflow checks Ok, scrap the previous commit. This is the right overflow check that takes care of 64 bit overflow **and** 32-bit overflow, which needs to be considered because the pool malloc can only allocate 32-bit elements in one go. --- src/pool.c | 3 +++ tests/core/pool.c | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pool.c b/src/pool.c index a23641145..146f118b4 100644 --- a/src/pool.c +++ b/src/pool.c @@ -194,6 +194,9 @@ char *git_pool_strndup(git_pool *pool, const char *str, size_t n) assert(pool && str && pool->item_size == sizeof(char)); + if ((uint32_t)(n + 1) < n) + return NULL; + if ((ptr = git_pool_malloc(pool, (uint32_t)(n + 1))) != NULL) { memcpy(ptr, str, n); ptr[n] = '\0'; diff --git a/tests/core/pool.c b/tests/core/pool.c index 3073c4a45..7a8b2dea6 100644 --- a/tests/core/pool.c +++ b/tests/core/pool.c @@ -139,7 +139,11 @@ void test_core_pool__strndup_limit(void) git_pool p; cl_git_pass(git_pool_init(&p, 1, 100)); - cl_assert(git_pool_strndup(&p, "foo", -1) == NULL); + /* ensure 64 bit doesn't overflow */ + cl_assert(git_pool_strndup(&p, "foo", (size_t)-1) == NULL); + + /* ensure 32 bit doesn't overflow */ + cl_assert(git_pool_strndup(&p, "bar", 0xfffffffful + 32) == NULL); git_pool_clear(&p); } -- cgit v1.2.1 From 7a16d54b5457aa9f60c25a204277ae0ce609ad2e Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 13 Dec 2013 12:47:51 +0100 Subject: pool: Agh, this test doesn't really apply in 32-bit machines The size_t is 32-bit already, so it overflows before going into the function. The `-1` test should handle this gracefully in both cases anyway. --- tests/core/pool.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/core/pool.c b/tests/core/pool.c index 7a8b2dea6..351d0c20f 100644 --- a/tests/core/pool.c +++ b/tests/core/pool.c @@ -141,9 +141,6 @@ void test_core_pool__strndup_limit(void) cl_git_pass(git_pool_init(&p, 1, 100)); /* ensure 64 bit doesn't overflow */ cl_assert(git_pool_strndup(&p, "foo", (size_t)-1) == NULL); - - /* ensure 32 bit doesn't overflow */ - cl_assert(git_pool_strndup(&p, "bar", 0xfffffffful + 32) == NULL); git_pool_clear(&p); } -- cgit v1.2.1