diff options
35 files changed, 1085 insertions, 390 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 08e7b47af..a57394640 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,8 +40,10 @@ ENDIF() IF (SHA1_TYPE STREQUAL "ppc") ADD_DEFINITIONS(-DPPC_SHA1) FILE(GLOB SRC_SHA1 src/ppc/*.c src/ppc/*.S) +ELSEIF (OPENSSL_FOUND) # libcrypto's implementation is faster than ours + ADD_DEFINITIONS(-DOPENSSL_SHA) ELSE () - SET (SRC_SHA1) + FILE(GLOB SRC_SHA1 src/sha1/*.c) ENDIF() IF (NOT WIN32) @@ -193,7 +195,7 @@ IF (BUILD_CLAR) DEPENDS ${CLAR_PATH}/clar ${SRC_TEST} WORKING_DIRECTORY ${CLAR_PATH} ) - ADD_EXECUTABLE(libgit2_clar ${SRC} ${CLAR_PATH}/clar_main.c ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX}) + ADD_EXECUTABLE(libgit2_clar ${SRC} ${CLAR_PATH}/clar_main.c ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1}) TARGET_LINK_LIBRARIES(libgit2_clar ${CMAKE_THREAD_LIBS_INIT} ${SSL_LIBRARIES}) IF (MSVC) diff --git a/Makefile.embed b/Makefile.embed index f46eaa4c1..b31a06e4b 100644 --- a/Makefile.embed +++ b/Makefile.embed @@ -15,7 +15,7 @@ INCLUDES= -I. -Isrc -Iinclude -Ideps/http-parser -Ideps/zlib DEFINES= $(INCLUDES) -DNO_VIZ -DSTDC -DNO_GZIP -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $(EXTRA_DEFINES) CFLAGS= -g $(DEFINES) -Wall -Wextra -O2 $(EXTRA_CFLAGS) -SRCS = $(wildcard src/*.c) $(wildcard src/transports/*.c) $(wildcard src/xdiff/*.c) $(wildcard deps/http-parser/*.c) $(wildcard deps/zlib/*.c) +SRCS = $(wildcard src/*.c) $(wildcard src/transports/*.c) $(wildcard src/xdiff/*.c) $(wildcard deps/http-parser/*.c) $(wildcard deps/zlib/*.c) $(wildcard src/sha1/*.c) ifeq ($(PLATFORM),Msys) SRCS += $(wildcard src/win32/*.c) $(wildcard src/compat/*.c) deps/regex/regex.c diff --git a/include/git2/config.h b/include/git2/config.h index a7d897443..67408f90f 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -20,6 +20,28 @@ GIT_BEGIN_DECL /** + * Priority level of a config file. + * These priority levels correspond to the natural escalation logic + * (from higher to lower) when searching for config entries in git.git. + * + * git_config_open_default() and git_repository_config() honor those + * priority levels as well. + */ +enum { + GIT_CONFIG_LEVEL_SYSTEM = 1, /**< System-wide configuration file. */ + GIT_CONFIG_LEVEL_XDG = 2, /**< XDG compatible configuration file (.config/git/config). */ + GIT_CONFIG_LEVEL_GLOBAL = 3, /**< User-specific configuration file, also called Global configuration file. */ + GIT_CONFIG_LEVEL_LOCAL = 4, /**< Repository specific configuration file. */ + GIT_CONFIG_HIGHEST_LEVEL = -1, /**< Represents the highest level of a config file. */ +}; + +typedef struct { + const char *name; + const char *value; + unsigned int level; +} git_config_entry; + +/** * Generic backend that implements the interface to * access a configuration file */ @@ -27,13 +49,13 @@ struct git_config_file { struct git_config *cfg; /* Open means open the file/database and parse if necessary */ - int (*open)(struct git_config_file *); - int (*get)(struct git_config_file *, const char *key, const char **value); - int (*get_multivar)(struct git_config_file *, const char *key, const char *regexp, int (*fn)(const char *, void *), void *data); + int (*open)(struct git_config_file *, unsigned int level); + int (*get)(struct git_config_file *, const char *key, const git_config_entry **entry); + int (*get_multivar)(struct git_config_file *, const char *key, const char *regexp, int (*fn)(const git_config_entry *, void *), void *data); int (*set)(struct git_config_file *, const char *key, const char *value); int (*set_multivar)(git_config_file *cfg, const char *name, const char *regexp, const char *value); int (*del)(struct git_config_file *, const char *key); - int (*foreach)(struct git_config_file *, const char *, int (*fn)(const char *, const char *, void *), void *data); + int (*foreach)(struct git_config_file *, const char *, int (*fn)(const git_config_entry *, void *), void *data); void (*free)(struct git_config_file *); }; @@ -100,9 +122,9 @@ GIT_EXTERN(int) git_config_find_xdg(char *xdg_config_path, size_t length); GIT_EXTERN(int) git_config_find_system(char *system_config_path, size_t length); /** - * Open the global and system configuration files + * Open the global, XDG and system configuration files * - * Utility wrapper that finds the global and system configuration files + * Utility wrapper that finds the global, XDG and system configuration files * and opens them into a single prioritized config object that can be * used when accessing default config data outside a repository. * @@ -143,14 +165,21 @@ GIT_EXTERN(int) git_config_new(git_config **out); * * Further queries on this config object will access each * of the config file instances in order (instances with - * a higher priority will be accessed first). + * a higher priority level will be accessed first). * * @param cfg the configuration to add the file to * @param file the configuration file (backend) to add - * @param priority the priority the backend should have - * @return 0 or an error code + * @param level the priority level of the backend + * @param force if a config file already exists for the given + * priority level, replace it + * @return 0 on success, GIT_EEXISTS when adding more than one file + * for a given priority level (and force_replace set to 0), or error code */ -GIT_EXTERN(int) git_config_add_file(git_config *cfg, git_config_file *file, int priority); +GIT_EXTERN(int) git_config_add_file( + git_config *cfg, + git_config_file *file, + unsigned int level, + int force); /** * Add an on-disk config file instance to an existing config @@ -164,14 +193,21 @@ GIT_EXTERN(int) git_config_add_file(git_config *cfg, git_config_file *file, int * * Further queries on this config object will access each * of the config file instances in order (instances with - * a higher priority will be accessed first). + * a higher priority level will be accessed first). * * @param cfg the configuration to add the file to * @param path path to the configuration file (backend) to add - * @param priority the priority the backend should have - * @return 0 or an error code + * @param level the priority level of the backend + * @param force if a config file already exists for the given + * priority level, replace it + * @return 0 on success, GIT_EEXISTS when adding more than one file + * for a given priority level (and force_replace set to 0), or error code */ -GIT_EXTERN(int) git_config_add_file_ondisk(git_config *cfg, const char *path, int priority); +GIT_EXTERN(int) git_config_add_file_ondisk( + git_config *cfg, + const char *path, + unsigned int level, + int force); /** @@ -189,6 +225,24 @@ GIT_EXTERN(int) git_config_add_file_ondisk(git_config *cfg, const char *path, in GIT_EXTERN(int) git_config_open_ondisk(git_config **cfg, const char *path); /** + * Build a single-level focused config object from a multi-level one. + * + * The returned config object can be used to perform get/set/delete operations + * on a single specific level. + * + * Getting several times the same level from the same parent multi-level config + * will return different config instances, but containing the same config_file + * instance. + * + * @return 0, GIT_ENOTFOUND if the passed level cannot be found in the + * multi-level parent config, or an error code + */ +GIT_EXTERN(int) git_config_open_level( + git_config **cfg_out, + git_config *cfg_parent, + unsigned int level); + +/** * Free the configuration and its associated memory and files * * @param cfg the configuration to free @@ -196,8 +250,25 @@ GIT_EXTERN(int) git_config_open_ondisk(git_config **cfg, const char *path); GIT_EXTERN(void) git_config_free(git_config *cfg); /** + * Get the git_config_entry of a config variable. + * + * The git_config_entry is owned by the config and should not be freed by the + * user. + + * @param out pointer to the variable git_config_entry + * @param cfg where to look for the variable + * @param name the variable's name + * @return 0 or an error code + */ +GIT_EXTERN(int) git_config_get_config_entry(const git_config_entry **out, git_config *cfg, const char *name); + +/** * Get the value of an integer config variable. * + * All config files will be looked into, in the order of their + * defined level. A higher level means a higher priority. The + * first occurence of the variable will be returned here. + * * @param out pointer to the variable where the value should be stored * @param cfg where to look for the variable * @param name the variable's name @@ -208,6 +279,10 @@ GIT_EXTERN(int) git_config_get_int32(int32_t *out, git_config *cfg, const char * /** * Get the value of a long integer config variable. * + * All config files will be looked into, in the order of their + * defined level. A higher level means a higher priority. The + * first occurence of the variable will be returned here. + * * @param out pointer to the variable where the value should be stored * @param cfg where to look for the variable * @param name the variable's name @@ -221,6 +296,10 @@ GIT_EXTERN(int) git_config_get_int64(int64_t *out, git_config *cfg, const char * * This function uses the usual C convention of 0 being false and * anything else true. * + * All config files will be looked into, in the order of their + * defined level. A higher level means a higher priority. The + * first occurence of the variable will be returned here. + * * @param out pointer to the variable where the value should be stored * @param cfg where to look for the variable * @param name the variable's name @@ -234,6 +313,10 @@ GIT_EXTERN(int) git_config_get_bool(int *out, git_config *cfg, const char *name) * The string is owned by the variable and should not be freed by the * user. * + * All config files will be looked into, in the order of their + * defined level. A higher level means a higher priority. The + * first occurence of the variable will be returned here. + * * @param out pointer to the variable's value * @param cfg where to look for the variable * @param name the variable's name @@ -253,10 +336,11 @@ GIT_EXTERN(int) git_config_get_string(const char **out, git_config *cfg, const c * @param fn the function to be called on each value of the variable * @param data opaque pointer to pass to the callback */ -GIT_EXTERN(int) git_config_get_multivar(git_config *cfg, const char *name, const char *regexp, int (*fn)(const char *, void *), void *data); +GIT_EXTERN(int) git_config_get_multivar(git_config *cfg, const char *name, const char *regexp, int (*fn)(const git_config_entry *, void *), void *data); /** - * Set the value of an integer config variable. + * Set the value of an integer config variable in the config file + * with the highest level (usually the local one). * * @param cfg where to look for the variable * @param name the variable's name @@ -266,7 +350,8 @@ GIT_EXTERN(int) git_config_get_multivar(git_config *cfg, const char *name, const GIT_EXTERN(int) git_config_set_int32(git_config *cfg, const char *name, int32_t value); /** - * Set the value of a long integer config variable. + * Set the value of a long integer config variable in the config file + * with the highest level (usually the local one). * * @param cfg where to look for the variable * @param name the variable's name @@ -276,7 +361,8 @@ GIT_EXTERN(int) git_config_set_int32(git_config *cfg, const char *name, int32_t GIT_EXTERN(int) git_config_set_int64(git_config *cfg, const char *name, int64_t value); /** - * Set the value of a boolean config variable. + * Set the value of a boolean config variable in the config file + * with the highest level (usually the local one). * * @param cfg where to look for the variable * @param name the variable's name @@ -286,7 +372,8 @@ GIT_EXTERN(int) git_config_set_int64(git_config *cfg, const char *name, int64_t GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value); /** - * Set the value of a string config variable. + * Set the value of a string config variable in the config file + * with the highest level (usually the local one). * * A copy of the string is made and the user is free to use it * afterwards. @@ -298,9 +385,8 @@ GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value */ GIT_EXTERN(int) git_config_set_string(git_config *cfg, const char *name, const char *value); - /** - * Set a multivar + * Set a multivar in the local config file. * * @param cfg where to look for the variable * @param name the variable's name @@ -310,7 +396,8 @@ GIT_EXTERN(int) git_config_set_string(git_config *cfg, const char *name, const c GIT_EXTERN(int) git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value); /** - * Delete a config variable + * Delete a config variable from the config file + * with the highest level (usually the local one). * * @param cfg the configuration * @param name the variable to delete @@ -332,7 +419,7 @@ GIT_EXTERN(int) git_config_delete(git_config *cfg, const char *name); */ GIT_EXTERN(int) git_config_foreach( git_config *cfg, - int (*callback)(const char *var_name, const char *value, void *payload), + int (*callback)(const git_config_entry *, void *payload), void *payload); /** @@ -351,7 +438,7 @@ GIT_EXTERN(int) git_config_foreach( GIT_EXTERN(int) git_config_foreach_match( git_config *cfg, const char *regexp, - int (*callback)(const char *var_name, const char *value, void *payload), + int (*callback)(const git_config_entry *entry, void *payload), void *payload); /** @@ -390,6 +477,57 @@ GIT_EXTERN(int) git_config_foreach_match( */ GIT_EXTERN(int) git_config_get_mapped(int *out, git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n); +/** + * Maps a string value to an integer constant + * + * @param out place to store the result of the parsing + * @param maps array of `git_cvar_map` objects specifying the possible mappings + * @param map_n number of mapping objects in `maps` + * @param value value to parse + */ +GIT_EXTERN(int) git_config_lookup_map_value( + int *out, + git_cvar_map *maps, + size_t map_n, + const char *value); + +/** + * Parse a string value as a bool. + * + * Valid values for true are: 'true', 'yes', 'on', 1 or any + * number different from 0 + * Valid values for false are: 'false', 'no', 'off', 0 + * + * @param out place to store the result of the parsing + * @param value value to parse + */ +GIT_EXTERN(int) git_config_parse_bool(int *out, const char *value); + +/** + * Parse a string value as an int64. + * + * An optional value suffix of 'k', 'm', or 'g' will + * cause the value to be multiplied by 1024, 1048576, + * or 1073741824 prior to output. + * + * @param out place to store the result of the parsing + * @param value value to parse + */ +GIT_EXTERN(int) git_config_parse_int64(int64_t *out, const char *value); + +/** + * Parse a string value as an int32. + * + * An optional value suffix of 'k', 'm', or 'g' will + * cause the value to be multiplied by 1024, 1048576, + * or 1073741824 prior to output. + * + * @param out place to store the result of the parsing + * @param value value to parse + */ +GIT_EXTERN(int) git_config_parse_int32(int32_t *out, const char *value); + + /** @} */ GIT_END_DECL #endif diff --git a/include/git2/ignore.h b/include/git2/ignore.h index 964a108ce..e18615edd 100644 --- a/include/git2/ignore.h +++ b/include/git2/ignore.h @@ -24,7 +24,7 @@ GIT_BEGIN_DECL * * Example usage: * - * error = git_ignore_add(myrepo, "*.c\ndir/\nFile with space\n"); + * error = git_ignore_add_rule(myrepo, "*.c\ndir/\nFile with space\n"); * * This would add three rules to the ignores. * diff --git a/src/blob.c b/src/blob.c index 6137746e1..76d265faa 100644 --- a/src/blob.c +++ b/src/blob.c @@ -255,11 +255,19 @@ int git_blob_create_fromchunks( int error = -1, read_bytes; char *content = NULL; git_filebuf file = GIT_FILEBUF_INIT; + git_buf path = GIT_BUF_INIT; + + if (git_buf_join_n( + &path, '/', 3, + 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, hintpath == NULL ? "streamed" : hintpath, GIT_FILEBUF_TEMPORARY) < 0) + if (git_filebuf_open(&file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY) < 0) goto cleanup; while (1) { @@ -283,6 +291,7 @@ int git_blob_create_fromchunks( error = blob_create_internal(oid, repo, file.path_lock, hintpath, hintpath != NULL); cleanup: + git_buf_free(&path); git_filebuf_cleanup(&file); git__free(content); return error; diff --git a/src/config.c b/src/config.c index b89c16b1c..f9bd205af 100644 --- a/src/config.c +++ b/src/config.c @@ -17,21 +17,29 @@ #include <ctype.h> typedef struct { + git_refcount rc; + git_config_file *file; - int priority; + unsigned int level; } file_internal; +static void file_internal_free(file_internal *internal) +{ + git_config_file *file; + + file = internal->file; + file->free(file); + git__free(internal); +} + static void config_free(git_config *cfg) { unsigned int i; - git_config_file *file; file_internal *internal; for(i = 0; i < cfg->files.length; ++i){ internal = git_vector_get(&cfg->files, i); - file = internal->file; - file->free(file); - git__free(internal); + GIT_REFCOUNT_DEC(internal, file_internal_free); } git_vector_free(&cfg->files); @@ -51,7 +59,7 @@ static int config_backend_cmp(const void *a, const void *b) const file_internal *bk_a = (const file_internal *)(a); const file_internal *bk_b = (const file_internal *)(b); - return bk_b->priority - bk_a->priority; + return bk_b->level - bk_a->level; } int git_config_new(git_config **out) @@ -73,20 +81,25 @@ int git_config_new(git_config **out) return 0; } -int git_config_add_file_ondisk(git_config *cfg, const char *path, int priority) +int git_config_add_file_ondisk( + git_config *cfg, + const char *path, + unsigned int level, + int force) { git_config_file *file = NULL; + int res; if (git_config_file__ondisk(&file, path) < 0) return -1; - if (git_config_add_file(cfg, file, priority) < 0) { + if ((res = git_config_add_file(cfg, file, level, force)) < 0) { /* * free manually; the file is not owned by the config * instance yet and will not be freed on cleanup */ file->free(file); - return -1; + return res; } return 0; @@ -97,7 +110,7 @@ int git_config_open_ondisk(git_config **cfg, const char *path) if (git_config_new(cfg) < 0) return -1; - if (git_config_add_file_ondisk(*cfg, path, 1) < 0) { + if (git_config_add_file_ondisk(*cfg, path, GIT_CONFIG_LEVEL_LOCAL, 0) < 0) { git_config_free(*cfg); return -1; } @@ -105,30 +118,152 @@ int git_config_open_ondisk(git_config **cfg, const char *path) return 0; } -int git_config_add_file(git_config *cfg, git_config_file *file, int priority) +static int find_internal_file_by_level( + file_internal **internal_out, + git_config *cfg, + int level) +{ + int pos = -1; + file_internal *internal; + unsigned int i; + + assert(cfg->files.length); + + /* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config file + * which has the highest level. As config files are stored in a vector + * sorted by decreasing order of level, getting the file at position 0 + * will do the job. + */ + if (level == GIT_CONFIG_HIGHEST_LEVEL) { + pos = 0; + } else { + git_vector_foreach(&cfg->files, i, internal) { + if (internal->level == (unsigned int)level) + pos = i; + } + } + + if (pos == -1) { + giterr_set(GITERR_CONFIG, + "No config file exists for the given level '%i'", level); + return GIT_ENOTFOUND; + } + + *internal_out = git_vector_get(&cfg->files, pos); + + return 0; +} + +static int duplicate_level(void **old_raw, void *new_raw) +{ + file_internal **old = (file_internal **)old_raw; + + GIT_UNUSED(new_raw); + + giterr_set(GITERR_CONFIG, "A file with the same level (%i) has already been added to the config", (*old)->level); + return GIT_EEXISTS; +} + +static void try_remove_existing_file_internal( + git_config *cfg, + unsigned int level) +{ + int pos = -1; + file_internal *internal; + unsigned int i; + + git_vector_foreach(&cfg->files, i, internal) { + if (internal->level == level) + pos = i; + } + + if (pos == -1) + return; + + internal = git_vector_get(&cfg->files, pos); + + if (git_vector_remove(&cfg->files, pos) < 0) + return; + + GIT_REFCOUNT_DEC(internal, file_internal_free); +} + +static int git_config__add_internal( + git_config *cfg, + file_internal *internal, + unsigned int level, + int force) +{ + int result; + + /* delete existing config file for level if it exists */ + if (force) + try_remove_existing_file_internal(cfg, level); + + if ((result = git_vector_insert_sorted(&cfg->files, + internal, &duplicate_level)) < 0) + return result; + + git_vector_sort(&cfg->files); + internal->file->cfg = cfg; + + GIT_REFCOUNT_INC(internal); + + return 0; +} + +int git_config_open_level( + git_config **cfg_out, + git_config *cfg_parent, + unsigned int level) +{ + git_config *cfg; + file_internal *internal; + int res; + + if ((res = find_internal_file_by_level(&internal, cfg_parent, level)) < 0) + return res; + + if ((res = git_config_new(&cfg)) < 0) + return res; + + if ((res = git_config__add_internal(cfg, internal, level, true)) < 0) { + git_config_free(cfg); + return res; + } + + *cfg_out = cfg; + + return 0; +} + +int git_config_add_file( + git_config *cfg, + git_config_file *file, + unsigned int level, + int force) { file_internal *internal; int result; assert(cfg && file); - if ((result = file->open(file)) < 0) + if ((result = file->open(file, level)) < 0) return result; internal = git__malloc(sizeof(file_internal)); GITERR_CHECK_ALLOC(internal); + memset(internal, 0x0, sizeof(file_internal)); + internal->file = file; - internal->priority = priority; + internal->level = level; - if (git_vector_insert(&cfg->files, internal) < 0) { + if ((result = git_config__add_internal(cfg, internal, level, force)) < 0) { git__free(internal); - return -1; + return result; } - git_vector_sort(&cfg->files); - internal->file->cfg = cfg; - return 0; } @@ -137,7 +272,7 @@ int git_config_add_file(git_config *cfg, git_config_file *file, int priority) */ int git_config_foreach( - git_config *cfg, int (*fn)(const char *, const char *, void *), void *data) + git_config *cfg, int (*fn)(const git_config_entry *, void *), void *data) { return git_config_foreach_match(cfg, NULL, fn, data); } @@ -145,7 +280,7 @@ int git_config_foreach( int git_config_foreach_match( git_config *cfg, const char *regexp, - int (*fn)(const char *, const char *, void *), + int (*fn)(const git_config_entry *, void *), void *data) { int ret = 0; @@ -164,10 +299,8 @@ int git_config_foreach_match( int git_config_delete(git_config *cfg, const char *name) { - file_internal *internal; git_config_file *file; - - assert(cfg->files.length); + file_internal *internal; internal = git_vector_get(&cfg->files, 0); file = internal->file; @@ -198,10 +331,8 @@ int git_config_set_bool(git_config *cfg, const char *name, int value) int git_config_set_string(git_config *cfg, const char *name, const char *value) { - file_internal *internal; git_config_file *file; - - assert(cfg->files.length); + file_internal *internal; internal = git_vector_get(&cfg->files, 0); file = internal->file; @@ -209,105 +340,9 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value) return file->set(file, name, value); } -static int parse_int64(int64_t *out, const char *value) -{ - const char *num_end; - int64_t num; - - if (git__strtol64(&num, value, &num_end, 0) < 0) - return -1; - - switch (*num_end) { - case 'g': - case 'G': - num *= 1024; - /* fallthrough */ - - case 'm': - case 'M': - num *= 1024; - /* fallthrough */ - - case 'k': - case 'K': - num *= 1024; - - /* check that that there are no more characters after the - * given modifier suffix */ - if (num_end[1] != '\0') - return -1; - - /* fallthrough */ - - case '\0': - *out = num; - return 0; - - default: - return -1; - } -} - -static int parse_int32(int32_t *out, const char *value) -{ - int64_t tmp; - int32_t truncate; - - if (parse_int64(&tmp, value) < 0) - return -1; - - truncate = tmp & 0xFFFFFFFF; - if (truncate != tmp) - return -1; - - *out = truncate; - return 0; -} - /*********** * Getters ***********/ -int git_config_lookup_map_value( - git_cvar_map *maps, size_t map_n, const char *value, int *out) -{ - size_t i; - - if (!value) - return GIT_ENOTFOUND; - - for (i = 0; i < map_n; ++i) { - git_cvar_map *m = maps + i; - - switch (m->cvar_type) { - case GIT_CVAR_FALSE: - case GIT_CVAR_TRUE: { - int bool_val; - - if (git__parse_bool(&bool_val, value) == 0 && - bool_val == (int)m->cvar_type) { - *out = m->map_value; - return 0; - } - break; - } - - case GIT_CVAR_INT32: - if (parse_int32(out, value) == 0) - return 0; - break; - - case GIT_CVAR_STRING: - if (strcasecmp(value, m->str_match) == 0) { - *out = m->map_value; - return 0; - } - break; - } - } - - return GIT_ENOTFOUND; -} - int git_config_get_mapped( int *out, git_config *cfg, @@ -318,16 +353,10 @@ int git_config_get_mapped( const char *value; int ret; - ret = git_config_get_string(&value, cfg, name); - if (ret < 0) + if ((ret = git_config_get_string(&value, cfg, name)) < 0) return ret; - if (!git_config_lookup_map_value(maps, map_n, value, out)) - return 0; - - giterr_set(GITERR_CONFIG, - "Failed to map the '%s' config variable with a valid value", name); - return -1; + return git_config_lookup_map_value(out, maps, map_n, value); } int git_config_get_int64(int64_t *out, git_config *cfg, const char *name) @@ -335,16 +364,10 @@ int git_config_get_int64(int64_t *out, git_config *cfg, const char *name) const char *value; int ret; - ret = git_config_get_string(&value, cfg, name); - if (ret < 0) + if ((ret = git_config_get_string(&value, cfg, name)) < 0) return ret; - if (parse_int64(out, value) < 0) { - giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value); - return -1; - } - - return 0; + return git_config_parse_int64(out, value); } int git_config_get_int32(int32_t *out, git_config *cfg, const char *name) @@ -352,16 +375,10 @@ int git_config_get_int32(int32_t *out, git_config *cfg, const char *name) const char *value; int ret; - ret = git_config_get_string(&value, cfg, name); - if (ret < 0) + if ((ret = git_config_get_string(&value, cfg, name)) < 0) return ret; - if (parse_int32(out, value) < 0) { - giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value); - return -1; - } - - return 0; + return git_config_parse_int32(out, value); } int git_config_get_bool(int *out, git_config *cfg, const char *name) @@ -369,20 +386,24 @@ int git_config_get_bool(int *out, git_config *cfg, const char *name) const char *value; int ret; - ret = git_config_get_string(&value, cfg, name); - if (ret < 0) + if ((ret = git_config_get_string(&value, cfg, name)) < 0) return ret; - if (git__parse_bool(out, value) == 0) - return 0; + return git_config_parse_bool(out, value); +} - if (parse_int32(out, value) == 0) { - *out = !!(*out); - return 0; - } +static int get_string_at_file(const char **out, git_config_file *file, const char *name) +{ + const git_config_entry *entry; + int res; - giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value); - return -1; + *out = NULL; + + res = file->get(file, name, &entry); + if (res != GIT_ENOTFOUND) + *out = entry->value; + + return res; } int git_config_get_string(const char **out, git_config *cfg, const char *name) @@ -392,6 +413,23 @@ int git_config_get_string(const char **out, git_config *cfg, const char *name) assert(cfg->files.length); + git_vector_foreach(&cfg->files, i, internal) { + int res = get_string_at_file(out, internal->file, name); + + if (res != GIT_ENOTFOUND) + return res; + } + + return GIT_ENOTFOUND; +} + +int git_config_get_config_entry(const git_config_entry **out, git_config *cfg, const char *name) +{ + file_internal *internal; + unsigned int i; + + assert(cfg->files.length); + *out = NULL; git_vector_foreach(&cfg->files, i, internal) { @@ -405,7 +443,7 @@ int git_config_get_string(const char **out, git_config *cfg, const char *name) } int git_config_get_multivar(git_config *cfg, const char *name, const char *regexp, - int (*fn)(const char *value, void *data), void *data) + int (*fn)(const git_config_entry *entry, void *data), void *data) { file_internal *internal; git_config_file *file; @@ -431,20 +469,13 @@ int git_config_get_multivar(git_config *cfg, const char *name, const char *regex int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value) { - file_internal *internal; git_config_file *file; - int ret = GIT_ENOTFOUND; - size_t i; + file_internal *internal; - for (i = cfg->files.length; i > 0; --i) { - internal = git_vector_get(&cfg->files, i - 1); - file = internal->file; - ret = file->set_multivar(file, name, regexp, value); - if (ret < 0 && ret != GIT_ENOTFOUND) - return ret; - } + internal = git_vector_get(&cfg->files, 0); + file = internal->file; - return 0; + return file->set_multivar(file, name, regexp, value); } int git_config_find_global_r(git_buf *path) @@ -541,13 +572,16 @@ int git_config_open_default(git_config **out) error = git_config_new(&cfg); if (!error && !git_config_find_global_r(&buf)) - error = git_config_add_file_ondisk(cfg, buf.ptr, 3); + error = git_config_add_file_ondisk(cfg, buf.ptr, + GIT_CONFIG_LEVEL_GLOBAL, 0); if (!error && !git_config_find_xdg_r(&buf)) - error = git_config_add_file_ondisk(cfg, buf.ptr, 2); + error = git_config_add_file_ondisk(cfg, buf.ptr, + GIT_CONFIG_LEVEL_XDG, 0); if (!error && !git_config_find_system_r(&buf)) - error = git_config_add_file_ondisk(cfg, buf.ptr, 1); + error = git_config_add_file_ondisk(cfg, buf.ptr, + GIT_CONFIG_LEVEL_SYSTEM, 0); git_buf_free(&buf); @@ -560,3 +594,129 @@ int git_config_open_default(git_config **out) return error; } + +/*********** + * Parsers + ***********/ +int git_config_lookup_map_value( + int *out, + git_cvar_map *maps, + size_t map_n, + const char *value) +{ + size_t i; + + if (!value) + goto fail_parse; + + for (i = 0; i < map_n; ++i) { + git_cvar_map *m = maps + i; + + switch (m->cvar_type) { + case GIT_CVAR_FALSE: + case GIT_CVAR_TRUE: { + int bool_val; + + if (git__parse_bool(&bool_val, value) == 0 && + bool_val == (int)m->cvar_type) { + *out = m->map_value; + return 0; + } + break; + } + + case GIT_CVAR_INT32: + if (git_config_parse_int32(out, value) == 0) + return 0; + break; + + case GIT_CVAR_STRING: + if (strcasecmp(value, m->str_match) == 0) { + *out = m->map_value; + return 0; + } + break; + } + } + +fail_parse: + giterr_set(GITERR_CONFIG, "Failed to map '%s'", value); + return -1; +} + +int git_config_parse_bool(int *out, const char *value) +{ + if (git__parse_bool(out, value) == 0) + return 0; + + if (git_config_parse_int32(out, value) == 0) { + *out = !!(*out); + return 0; + } + + giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value); + return -1; +} + +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) + goto fail_parse; + + switch (*num_end) { + case 'g': + case 'G': + num *= 1024; + /* fallthrough */ + + case 'm': + case 'M': + num *= 1024; + /* fallthrough */ + + case 'k': + case 'K': + num *= 1024; + + /* check that that there are no more characters after the + * given modifier suffix */ + if (num_end[1] != '\0') + return -1; + + /* fallthrough */ + + case '\0': + *out = num; + return 0; + + default: + goto fail_parse; + } + +fail_parse: + giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value); + return -1; +} + +int git_config_parse_int32(int32_t *out, const char *value) +{ + int64_t tmp; + int32_t truncate; + + if (git_config_parse_int64(&tmp, value) < 0) + goto fail_parse; + + truncate = tmp & 0xFFFFFFFF; + if (truncate != tmp) + goto fail_parse; + + *out = truncate; + return 0; + +fail_parse: + giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value); + return -1; +} diff --git a/src/config.h b/src/config.h index 471b42dad..16b8413b7 100644 --- a/src/config.h +++ b/src/config.h @@ -27,9 +27,4 @@ extern int git_config_find_global_r(git_buf *global_config_path); extern int git_config_find_xdg_r(git_buf *system_config_path); extern int git_config_find_system_r(git_buf *system_config_path); -extern int git_config_parse_bool(int *out, const char *bool_string); - -extern int git_config_lookup_map_value( - git_cvar_map *maps, size_t map_n, const char *value, int *out); - #endif diff --git a/src/config_file.c b/src/config_file.c index 4ba83d1d9..92fe13296 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -22,15 +22,9 @@ GIT__USE_STRMAP; typedef struct cvar_t { struct cvar_t *next; - char *key; /* TODO: we might be able to get rid of this */ - char *value; + git_config_entry *entry; } cvar_t; -typedef struct { - struct cvar_t *head; - struct cvar_t *tail; -} cvar_t_list; - #define CVAR_LIST_HEAD(list) ((list)->head) #define CVAR_LIST_TAIL(list) ((list)->tail) @@ -84,7 +78,7 @@ typedef struct { char *file_path; } diskfile_backend; -static int config_parse(diskfile_backend *cfg_file); +static int config_parse(diskfile_backend *cfg_file, unsigned int level); static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value); static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value); static char *escape_value(const char *ptr); @@ -100,8 +94,9 @@ static void cvar_free(cvar_t *var) if (var == NULL) return; - git__free(var->key); - git__free(var->value); + git__free((char*)var->entry->name); + git__free((char *)var->entry->value); + git__free(var->entry); git__free(var); } @@ -150,7 +145,7 @@ static void free_vars(git_strmap *values) git_strmap_free(values); } -static int config_open(git_config_file *cfg) +static int config_open(git_config_file *cfg, unsigned int level) { int res; diskfile_backend *b = (diskfile_backend *)cfg; @@ -165,7 +160,7 @@ static int config_open(git_config_file *cfg) if (res == GIT_ENOTFOUND) return 0; - if (res < 0 || config_parse(b) < 0) { + if (res < 0 || config_parse(b, level) < 0) { free_vars(b->values); b->values = NULL; git_buf_free(&b->reader.buffer); @@ -191,7 +186,7 @@ static void backend_free(git_config_file *_backend) static int file_foreach( git_config_file *backend, const char *regexp, - int (*fn)(const char *, const char *, void *), + int (*fn)(const git_config_entry *, void *), void *data) { diskfile_backend *b = (diskfile_backend *)backend; @@ -220,7 +215,7 @@ static int file_foreach( continue; /* abort iterator on non-zero return value */ - if (fn(key, var->value, data)) { + if (fn(var->entry, data)) { giterr_clear(); result = GIT_EUSER; goto cleanup; @@ -263,8 +258,8 @@ static int config_set(git_config_file *cfg, const char *name, const char *value) } /* don't update if old and new values already match */ - if ((!existing->value && !value) || - (existing->value && value && !strcmp(existing->value, value))) + if ((!existing->entry->value && !value) || + (existing->entry->value && value && !strcmp(existing->entry->value, value))) return 0; if (value) { @@ -274,10 +269,10 @@ static int config_set(git_config_file *cfg, const char *name, const char *value) GITERR_CHECK_ALLOC(esc_value); } - git__free(existing->value); - existing->value = tmp; + git__free((void *)existing->entry->value); + existing->entry->value = tmp; - ret = config_write(b, existing->key, NULL, esc_value); + ret = config_write(b, existing->entry->name, NULL, esc_value); git__free(esc_value); return ret; @@ -285,15 +280,17 @@ static int config_set(git_config_file *cfg, const char *name, const char *value) var = git__malloc(sizeof(cvar_t)); GITERR_CHECK_ALLOC(var); - memset(var, 0x0, sizeof(cvar_t)); + var->entry = git__malloc(sizeof(git_config_entry)); + GITERR_CHECK_ALLOC(var->entry); + memset(var->entry, 0x0, sizeof(git_config_entry)); - var->key = key; - var->value = NULL; + var->entry->name = key; + var->entry->value = NULL; if (value) { - var->value = git__strdup(value); - GITERR_CHECK_ALLOC(var->value); + var->entry->value = git__strdup(value); + GITERR_CHECK_ALLOC(var->entry->value); esc_value = escape_value(value); GITERR_CHECK_ALLOC(esc_value); } @@ -317,7 +314,7 @@ static int config_set(git_config_file *cfg, const char *name, const char *value) /* * Internal function that actually gets the value in string form */ -static int config_get(git_config_file *cfg, const char *name, const char **out) +static int config_get(git_config_file *cfg, const char *name, const git_config_entry **out) { diskfile_backend *b = (diskfile_backend *)cfg; char *key; @@ -333,7 +330,7 @@ static int config_get(git_config_file *cfg, const char *name, const char **out) if (!git_strmap_valid_index(b->values, pos)) return GIT_ENOTFOUND; - *out = ((cvar_t *)git_strmap_value_at(b->values, pos))->value; + *out = ((cvar_t *)git_strmap_value_at(b->values, pos))->entry; return 0; } @@ -342,7 +339,7 @@ static int config_get_multivar( git_config_file *cfg, const char *name, const char *regex_str, - int (*fn)(const char *, void *), + int (*fn)(const git_config_entry *, void *), void *data) { cvar_t *var; @@ -376,10 +373,10 @@ static int config_get_multivar( /* and throw the callback only on the variables that * match the regex */ do { - if (regexec(®ex, var->value, 0, NULL, 0) == 0) { + if (regexec(®ex, var->entry->value, 0, NULL, 0) == 0) { /* early termination by the user is not an error; * just break and return successfully */ - if (fn(var->value, data) < 0) + if (fn(var->entry, data) < 0) break; } @@ -391,7 +388,7 @@ static int config_get_multivar( do { /* early termination by the user is not an error; * just break and return successfully */ - if (fn(var->value, data) < 0) + if (fn(var->entry, data) < 0) break; var = var->next; @@ -434,12 +431,12 @@ static int config_set_multivar( } for (;;) { - if (regexec(&preg, var->value, 0, NULL, 0) == 0) { + if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) { char *tmp = git__strdup(value); GITERR_CHECK_ALLOC(tmp); - git__free(var->value); - var->value = tmp; + git__free((void *)var->entry->value); + var->entry->value = tmp; replaced = 1; } @@ -453,14 +450,18 @@ static int config_set_multivar( if (!replaced) { newvar = git__malloc(sizeof(cvar_t)); GITERR_CHECK_ALLOC(newvar); - memset(newvar, 0x0, sizeof(cvar_t)); + newvar->entry = git__malloc(sizeof(git_config_entry)); + GITERR_CHECK_ALLOC(newvar->entry); + memset(newvar->entry, 0x0, sizeof(git_config_entry)); - newvar->key = git__strdup(var->key); - GITERR_CHECK_ALLOC(newvar->key); + newvar->entry->name = git__strdup(var->entry->name); + GITERR_CHECK_ALLOC(newvar->entry->name); - newvar->value = git__strdup(value); - GITERR_CHECK_ALLOC(newvar->value); + newvar->entry->value = git__strdup(value); + GITERR_CHECK_ALLOC(newvar->entry->value); + + newvar->entry->level = var->entry->level; var->next = newvar; } @@ -501,7 +502,7 @@ static int config_delete(git_config_file *cfg, const char *name) git_strmap_delete_at(b->values, pos); - result = config_write(b, var->key, NULL, NULL); + result = config_write(b, var->entry->name, NULL, NULL); cvar_free(var); return result; @@ -898,7 +899,7 @@ static int strip_comments(char *line, int in_quotes) return quote_count; } -static int config_parse(diskfile_backend *cfg_file) +static int config_parse(diskfile_backend *cfg_file, unsigned int level) { int c; char *current_section = NULL; @@ -946,8 +947,10 @@ static int config_parse(diskfile_backend *cfg_file) var = git__malloc(sizeof(cvar_t)); GITERR_CHECK_ALLOC(var); - memset(var, 0x0, sizeof(cvar_t)); + var->entry = git__malloc(sizeof(git_config_entry)); + GITERR_CHECK_ALLOC(var->entry); + memset(var->entry, 0x0, sizeof(git_config_entry)); git__strtolower(var_name); git_buf_printf(&buf, "%s.%s", current_section, var_name); @@ -956,13 +959,14 @@ static int config_parse(diskfile_backend *cfg_file) if (git_buf_oom(&buf)) return -1; - var->key = git_buf_detach(&buf); - var->value = var_value; + var->entry->name = git_buf_detach(&buf); + var->entry->value = var_value; + var->entry->level = level; /* Add or append the new config option */ - pos = git_strmap_lookup_index(cfg_file->values, var->key); + pos = git_strmap_lookup_index(cfg_file->values, var->entry->name); if (!git_strmap_valid_index(cfg_file->values, pos)) { - git_strmap_insert(cfg_file->values, var->key, var, result); + git_strmap_insert(cfg_file->values, var->entry->name, var, result); if (result < 0) break; result = 0; @@ -1182,6 +1186,10 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p goto rewrite_fail; } + /* If we are here, there is at least a section line */ + if (*(cfg->reader.buffer.ptr + cfg->reader.buffer.size - 1) != '\n') + git_filebuf_write(&file, "\n", 1); + git_filebuf_printf(&file, "\t%s = %s\n", name, value); } } diff --git a/src/config_file.h b/src/config_file.h index bf687b516..b500dd64f 100644 --- a/src/config_file.h +++ b/src/config_file.h @@ -9,9 +9,9 @@ #include "git2/config.h" -GIT_INLINE(int) git_config_file_open(git_config_file *cfg) +GIT_INLINE(int) git_config_file_open(git_config_file *cfg, unsigned int level) { - return cfg->open(cfg); + return cfg->open(cfg, level); } GIT_INLINE(void) git_config_file_free(git_config_file *cfg) @@ -20,7 +20,7 @@ GIT_INLINE(void) git_config_file_free(git_config_file *cfg) } GIT_INLINE(int) git_config_file_get_string( - const char **out, git_config_file *cfg, const char *name) + const git_config_entry **out, git_config_file *cfg, const char *name) { return cfg->get(cfg, name, out); } @@ -39,7 +39,7 @@ GIT_INLINE(int) git_config_file_delete( GIT_INLINE(int) git_config_file_foreach( git_config_file *cfg, - int (*fn)(const char *key, const char *value, void *data), + int (*fn)(const git_config_entry *entry, void *data), void *data) { return cfg->foreach(cfg, NULL, fn, data); @@ -48,7 +48,7 @@ GIT_INLINE(int) git_config_file_foreach( GIT_INLINE(int) git_config_file_foreach_match( git_config_file *cfg, const char *regexp, - int (*fn)(const char *key, const char *value, void *data), + int (*fn)(const git_config_entry *entry, void *data), void *data) { return cfg->foreach(cfg, regexp, fn, data); diff --git a/src/remote.c b/src/remote.c index 0a16a105c..b4eddbd18 100644 --- a/src/remote.c +++ b/src/remote.c @@ -86,6 +86,11 @@ int git_remote_new(git_remote **out, git_repository *repo, const char *name, con goto on_error; } + /* A remote without a name doesn't download tags */ + if (!name) { + remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; + } + *out = remote; return 0; @@ -198,7 +203,9 @@ cleanup: int git_remote_save(const git_remote *remote) { + int error; git_config *config; + const char *tagopt = NULL; git_buf buf = GIT_BUF_INIT, value = GIT_BUF_INIT; if (git_repository_config__weakptr(&config, remote->repo) < 0) @@ -260,6 +267,38 @@ int git_remote_save(const git_remote *remote) goto on_error; } + /* + * What action to take depends on the old and new values. This + * is describes by the table below. tagopt means whether the + * is already a value set in the config + * + * AUTO ALL or NONE + * +-----------------------+ + * tagopt | remove | set | + * +---------+-------------| + * !tagopt | nothing | set | + * +---------+-------------+ + */ + + git_buf_clear(&buf); + if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) + goto on_error; + + error = git_config_get_string(&tagopt, config, git_buf_cstr(&buf)); + if (error < 0 && error != GIT_ENOTFOUND) + goto on_error; + + if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { + if (git_config_set_string(config, git_buf_cstr(&buf), "--tags") < 0) + goto on_error; + } else if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_NONE) { + if (git_config_set_string(config, git_buf_cstr(&buf), "--no-tags") < 0) + goto on_error; + } else if (tagopt) { + if (git_config_delete(config, git_buf_cstr(&buf)) < 0) + goto on_error; + } + git_buf_free(&buf); git_buf_free(&value); @@ -603,12 +642,12 @@ struct cb_data { regex_t *preg; }; -static int remote_list_cb(const char *name, const char *value, void *data_) +static int remote_list_cb(const git_config_entry *entry, void *data_) { struct cb_data *data = (struct cb_data *)data_; size_t nmatch = 2; regmatch_t pmatch[2]; - GIT_UNUSED(value); + const char *name = entry->name; if (!regexec(data->preg, name, nmatch, pmatch, 0)) { char *remote_name = git__strndup(&name[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so); diff --git a/src/repository.c b/src/repository.c index db0888a89..43e0eda8f 100644 --- a/src/repository.c +++ b/src/repository.c @@ -461,23 +461,23 @@ static int load_config( &config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO) < 0) goto on_error; - if (git_config_add_file_ondisk(cfg, config_path.ptr, 4) < 0) + if (git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, 0) < 0) goto on_error; git_buf_free(&config_path); if (global_config_path != NULL) { - if (git_config_add_file_ondisk(cfg, global_config_path, 3) < 0) + if (git_config_add_file_ondisk(cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, 0) < 0) goto on_error; } if (xdg_config_path != NULL) { - if (git_config_add_file_ondisk(cfg, xdg_config_path, 2) < 0) + if (git_config_add_file_ondisk(cfg, xdg_config_path, GIT_CONFIG_LEVEL_XDG, 0) < 0) goto on_error; } if (system_config_path != NULL) { - if (git_config_add_file_ondisk(cfg, system_config_path, 1) < 0) + if (git_config_add_file_ondisk(cfg, system_config_path, GIT_CONFIG_LEVEL_SYSTEM, 0) < 0) goto on_error; } diff --git a/src/reset.c b/src/reset.c index dfa095be4..560ae17b1 100644 --- a/src/reset.c +++ b/src/reset.c @@ -19,6 +19,45 @@ static int reset_error_invalid(const char *msg) return -1; } +static int update_head(git_repository *repo, git_object *commit) +{ + int error; + git_reference *head = NULL, *target = NULL; + + error = git_repository_head(&head, repo); + + if (error < 0 && error != GIT_EORPHANEDHEAD) + return error; + + if (error == GIT_EORPHANEDHEAD) { + giterr_clear(); + + /* + * TODO: This is a bit weak as this doesn't support chained + * symbolic references. yet. + */ + if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0) + goto cleanup; + + if ((error = git_reference_create_oid( + &target, + repo, + git_reference_target(head), + git_object_id(commit), 0)) < 0) + goto cleanup; + } else { + if ((error = git_reference_set_oid(head, git_object_id(commit))) < 0) + goto cleanup; + } + + error = 0; + +cleanup: + git_reference_free(head); + git_reference_free(target); + return error; +} + int git_reset( git_repository *repo, git_object *target, @@ -29,7 +68,6 @@ int git_reset( git_tree *tree = NULL; int error = -1; git_checkout_opts opts; - git_reference *head = NULL; assert(repo && target); assert(reset_type == GIT_RESET_SOFT @@ -52,10 +90,7 @@ int git_reset( //TODO: Check for unmerged entries - if (git_repository_head(&head, repo) < 0) - goto cleanup; - - if (git_reference_set_oid(head, git_object_id(commit)) < 0) + if (update_head(repo, commit) < 0) goto cleanup; if (reset_type == GIT_RESET_SOFT) { @@ -102,7 +137,6 @@ int git_reset( error = 0; cleanup: - git_reference_free(head); git_object_free(commit); git_index_free(index); git_tree_free(tree); diff --git a/src/sha1.h b/src/sha1.h index f0a16f2cf..41e8abad6 100644 --- a/src/sha1.h +++ b/src/sha1.h @@ -8,12 +8,17 @@ #ifndef INCLUDE_sha1_h__ #define INCLUDE_sha1_h__ +#ifdef OPENSSL_SHA +# include <openssl/sha.h> + +#else typedef struct { unsigned long long size; unsigned int H[5]; unsigned int W[16]; } blk_SHA_CTX; + void git__blk_SHA1_Init(blk_SHA_CTX *ctx); void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, size_t len); void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx); @@ -23,4 +28,6 @@ void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx); #define SHA1_Update git__blk_SHA1_Update #define SHA1_Final git__blk_SHA1_Final +#endif // OPENSSL_SHA + #endif diff --git a/src/sha1.c b/src/sha1/sha1.c index 8aaedeb8f..8aaedeb8f 100644 --- a/src/sha1.c +++ b/src/sha1/sha1.c diff --git a/src/submodule.c b/src/submodule.c index 180528641..e3657f9ad 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -72,7 +72,7 @@ static int submodule_get(git_submodule **, git_repository *, const char *, const static void submodule_release(git_submodule *sm, int decr); static int submodule_load_from_index(git_repository *, const git_index_entry *); static int submodule_load_from_head(git_repository*, const char*, const git_oid*); -static int submodule_load_from_config(const char *, const char *, void *); +static int submodule_load_from_config(const git_config_entry *, void *); static int submodule_load_from_wd_lite(git_submodule *, const char *, void *); static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool); static void submodule_mode_mismatch(git_repository *, const char *, unsigned int); @@ -974,11 +974,12 @@ static int submodule_config_error(const char *property, const char *value) } static int submodule_load_from_config( - const char *key, const char *value, void *data) + const git_config_entry *entry, void *data) { git_repository *repo = data; git_strmap *smcfg = repo->submodules; const char *namestart, *property, *alternate = NULL; + const char *key = entry->name, *value = entry->value; git_buf name = GIT_BUF_INIT; git_submodule *sm; bool is_path; @@ -1055,7 +1056,7 @@ static int submodule_load_from_config( else if (strcasecmp(property, "update") == 0) { int val; if (git_config_lookup_map_value( - _sm_update_map, ARRAY_SIZE(_sm_update_map), value, &val) < 0) + &val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0) return submodule_config_error("update", value); sm->update_default = sm->update = (git_submodule_update_t)val; } @@ -1066,7 +1067,7 @@ static int submodule_load_from_config( else if (strcasecmp(property, "ignore") == 0) { int val; if (git_config_lookup_map_value( - _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value, &val) < 0) + &val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0) return submodule_config_error("ignore", value); sm->ignore_default = sm->ignore = (git_submodule_ignore_t)val; } @@ -1204,7 +1205,7 @@ static git_config_file *open_gitmodules( if (git_config_file__ondisk(&mods, path.ptr) < 0) mods = NULL; /* open should only fail here if the file is malformed */ - else if (git_config_file_open(mods) < 0) { + else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL) < 0) { git_config_file_free(mods); mods = NULL; } diff --git a/tests-clar/checkout/head.c b/tests-clar/checkout/head.c index f2f81e5e2..d36034c52 100644 --- a/tests-clar/checkout/head.c +++ b/tests-clar/checkout/head.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "refs.h" +#include "repo/repo_helpers.h" static git_repository *g_repo; @@ -15,10 +16,7 @@ void test_checkout_head__cleanup(void) void test_checkout_head__checking_out_an_orphaned_head_returns_GIT_EORPHANEDHEAD(void) { - git_reference *head; - - cl_git_pass(git_reference_create_symbolic(&head, g_repo, GIT_HEAD_FILE, "refs/heads/hide/and/seek", 1)); - git_reference_free(head); + make_head_orphaned(g_repo, NON_EXISTING_HEAD); cl_assert_equal_i(GIT_EORPHANEDHEAD, git_checkout_head(g_repo, NULL, NULL)); } diff --git a/tests-clar/config/configlevel.c b/tests-clar/config/configlevel.c new file mode 100644 index 000000000..d947856fa --- /dev/null +++ b/tests-clar/config/configlevel.c @@ -0,0 +1,59 @@ +#include "clar_libgit2.h" + +void test_config_configlevel__adding_the_same_level_twice_returns_EEXISTS(void) +{ + int error; + git_config *cfg; + + cl_git_pass(git_config_new(&cfg)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"), + GIT_CONFIG_LEVEL_LOCAL, 0)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"), + GIT_CONFIG_LEVEL_GLOBAL, 0)); + error = git_config_add_file_ondisk(cfg, cl_fixture("config/config16"), + GIT_CONFIG_LEVEL_GLOBAL, 0); + + cl_git_fail(error); + cl_assert_equal_i(GIT_EEXISTS, error); + + git_config_free(cfg); +} + +void test_config_configlevel__can_replace_a_config_file_at_an_existing_level(void) +{ + git_config *cfg; + const char *s; + + cl_git_pass(git_config_new(&cfg)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"), + GIT_CONFIG_LEVEL_LOCAL, 1)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"), + GIT_CONFIG_LEVEL_LOCAL, 1)); + + cl_git_pass(git_config_get_string(&s, cfg, "core.stringglobal")); + cl_assert_equal_s("don't find me!", s); + + git_config_free(cfg); +} + +void test_config_configlevel__can_read_from_a_single_level_focused_file_after_parent_config_has_been_freed(void) +{ + git_config *cfg; + git_config *single_level_cfg; + const char *s; + + cl_git_pass(git_config_new(&cfg)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"), + GIT_CONFIG_LEVEL_GLOBAL, 0)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"), + GIT_CONFIG_LEVEL_LOCAL, 0)); + + cl_git_pass(git_config_open_level(&single_level_cfg, cfg, GIT_CONFIG_LEVEL_LOCAL)); + + git_config_free(cfg); + + cl_git_pass(git_config_get_string(&s, single_level_cfg, "core.stringglobal")); + cl_assert_equal_s("don't find me!", s); + + git_config_free(single_level_cfg); +} diff --git a/tests-clar/config/multivar.c b/tests-clar/config/multivar.c index 3b40cd09a..26537e20a 100644 --- a/tests-clar/config/multivar.c +++ b/tests-clar/config/multivar.c @@ -12,13 +12,11 @@ void test_config_multivar__cleanup(void) cl_fixture_cleanup("config"); } -static int mv_read_cb(const char *name, const char *value, void *data) +static int mv_read_cb(const git_config_entry *entry, void *data) { int *n = (int *) data; - GIT_UNUSED(value); - - if (!strcmp(name, _name)) + if (!strcmp(entry->name, _name)) (*n)++; return 0; @@ -37,11 +35,11 @@ void test_config_multivar__foreach(void) git_config_free(cfg); } -static int cb(const char *val, void *data) +static int cb(const git_config_entry *entry, void *data) { int *n = (int *) data; - GIT_UNUSED(val); + GIT_UNUSED(entry); (*n)++; diff --git a/tests-clar/config/new.c b/tests-clar/config/new.c index fae3ce941..6bd719fba 100644 --- a/tests-clar/config/new.c +++ b/tests-clar/config/new.c @@ -9,21 +9,16 @@ void test_config_new__write_new_config(void) { const char *out; - struct git_config_file *file; git_config *config; - cl_git_pass(git_config_file__ondisk(&file, TEST_CONFIG)); - cl_git_pass(git_config_new(&config)); - cl_git_pass(git_config_add_file(config, file, 0)); + cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG)); cl_git_pass(git_config_set_string(config, "color.ui", "auto")); cl_git_pass(git_config_set_string(config, "core.editor", "ed")); git_config_free(config); - cl_git_pass(git_config_file__ondisk(&file, TEST_CONFIG)); - cl_git_pass(git_config_new(&config)); - cl_git_pass(git_config_add_file(config, file, 0)); + cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG)); cl_git_pass(git_config_get_string(&out, config, "color.ui")); cl_assert_equal_s(out, "auto"); diff --git a/tests-clar/config/read.c b/tests-clar/config/read.c index fcd22463d..cf781e6bf 100644 --- a/tests-clar/config/read.c +++ b/tests-clar/config/read.c @@ -191,22 +191,24 @@ void test_config_read__escaping_quotes(void) git_config_free(cfg); } -static int count_cfg_entries( - const char *var_name, const char *value, void *payload) +static int count_cfg_entries_and_compare_levels( + const git_config_entry *entry, void *payload) { int *count = payload; - GIT_UNUSED(var_name); - GIT_UNUSED(value); + + if (!strcmp(entry->value, "7") || !strcmp(entry->value, "17")) + cl_assert(entry->level == GIT_CONFIG_LEVEL_GLOBAL); + else + cl_assert(entry->level == GIT_CONFIG_LEVEL_SYSTEM); + (*count)++; return 0; } -static int cfg_callback_countdown( - const char *var_name, const char *value, void *payload) +static int cfg_callback_countdown(const git_config_entry *entry, void *payload) { int *count = payload; - GIT_UNUSED(var_name); - GIT_UNUSED(value); + GIT_UNUSED(entry); (*count)--; if (*count == 0) return -100; @@ -218,11 +220,15 @@ void test_config_read__foreach(void) git_config *cfg; int count, ret; - cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9"))); + cl_git_pass(git_config_new(&cfg)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"), + GIT_CONFIG_LEVEL_SYSTEM, 0)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"), + GIT_CONFIG_LEVEL_GLOBAL, 0)); count = 0; - cl_git_pass(git_config_foreach(cfg, count_cfg_entries, &count)); - cl_assert_equal_i(5, count); + cl_git_pass(git_config_foreach(cfg, count_cfg_entries_and_compare_levels, &count)); + cl_assert_equal_i(7, count); count = 3; cl_git_fail(ret = git_config_foreach(cfg, cfg_callback_countdown, &count)); @@ -231,6 +237,14 @@ void test_config_read__foreach(void) git_config_free(cfg); } +static int count_cfg_entries(const git_config_entry *entry, void *payload) +{ + int *count = payload; + GIT_UNUSED(entry); + (*count)++; + return 0; +} + void test_config_read__foreach_match(void) { git_config *cfg; @@ -282,31 +296,129 @@ void test_config_read__whitespace_not_required_around_assignment(void) git_config_free(cfg); } -#if 0 +void test_config_read__read_git_config_entry(void) +{ + git_config *cfg; + const git_config_entry *entry; -BEGIN_TEST(config10, "a repo's config overrides the global config") - git_repository *repo; + cl_git_pass(git_config_new(&cfg)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"), + GIT_CONFIG_LEVEL_SYSTEM, 0)); + + cl_git_pass(git_config_get_config_entry(&entry, cfg, "core.dummy2")); + cl_assert_equal_s("core.dummy2", entry->name); + cl_assert_equal_s("42", entry->value); + cl_assert_equal_i(GIT_CONFIG_LEVEL_SYSTEM, entry->level); + + git_config_free(cfg); +} + +/* + * At the beginning of the test: + * - config9 has: core.dummy2=42 + * - config15 has: core.dummy2=7 + * - config16 has: core.dummy2=28 + */ +void test_config_read__local_config_overrides_global_config_overrides_system_config(void) +{ git_config *cfg; - int32_t version; + int32_t i; + + cl_git_pass(git_config_new(&cfg)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"), + GIT_CONFIG_LEVEL_SYSTEM, 0)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"), + GIT_CONFIG_LEVEL_GLOBAL, 0)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"), + GIT_CONFIG_LEVEL_LOCAL, 0)); + + cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2")); + cl_assert_equal_i(28, i); + + git_config_free(cfg); + + cl_git_pass(git_config_new(&cfg)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"), + GIT_CONFIG_LEVEL_SYSTEM, 0)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"), + GIT_CONFIG_LEVEL_GLOBAL, 0)); + + cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2")); + cl_assert_equal_i(7, i); - cl_git_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - cl_git_pass(git_repository_config(&cfg, repo, GLOBAL_CONFIG, NULL)); - cl_git_pass(git_config_get_int32(cfg, "core.repositoryformatversion", &version)); - cl_assert(version == 0); git_config_free(cfg); - git_repository_free(repo); -END_TEST +} -BEGIN_TEST(config11, "fall back to the global config") - git_repository *repo; +/* + * At the beginning of the test: + * - config9 has: core.global does not exist + * - config15 has: core.global=17 + * - config16 has: core.global=29 + * + * And also: + * - config9 has: core.system does not exist + * - config15 has: core.system does not exist + * - config16 has: core.system=11 + */ +void test_config_read__fallback_from_local_to_global_and_from_global_to_system(void) +{ git_config *cfg; - int32_t num; + int32_t i; - cl_git_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - cl_git_pass(git_repository_config(&cfg, repo, GLOBAL_CONFIG, NULL)); - cl_git_pass(git_config_get_int32(cfg, "core.something", &num)); - cl_assert(num == 2); + cl_git_pass(git_config_new(&cfg)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"), + GIT_CONFIG_LEVEL_SYSTEM, 0)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"), + GIT_CONFIG_LEVEL_GLOBAL, 0)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"), + GIT_CONFIG_LEVEL_LOCAL, 0)); + + cl_git_pass(git_config_get_int32(&i, cfg, "core.global")); + cl_assert_equal_i(17, i); + cl_git_pass(git_config_get_int32(&i, cfg, "core.system")); + cl_assert_equal_i(11, i); + + git_config_free(cfg); +} + +/* + * At the beginning of the test, config18 has: + * int32global = 28 + * int64global = 9223372036854775803 + * boolglobal = true + * stringglobal = I'm a global config value! + * + * And config19 has: + * int32global = -1 + * int64global = -2 + * boolglobal = false + * stringglobal = don't find me! + * + */ +void test_config_read__simple_read_from_specific_level(void) +{ + git_config *cfg, *cfg_specific; + int i; + int64_t l, expected = +9223372036854775803; + const char *s; + + cl_git_pass(git_config_new(&cfg)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"), + GIT_CONFIG_LEVEL_GLOBAL, 0)); + cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"), + GIT_CONFIG_LEVEL_SYSTEM, 0)); + + cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL)); + + cl_git_pass(git_config_get_int32(&i, cfg_specific, "core.int32global")); + cl_assert_equal_i(28, i); + cl_git_pass(git_config_get_int64(&l, cfg_specific, "core.int64global")); + cl_assert(l == expected); + cl_git_pass(git_config_get_bool(&i, cfg_specific, "core.boolglobal")); + cl_assert_equal_b(true, i); + cl_git_pass(git_config_get_string(&s, cfg_specific, "core.stringglobal")); + cl_assert_equal_s("I'm a global config value!", s); + + git_config_free(cfg_specific); git_config_free(cfg); - git_repository_free(repo); -END_TEST -#endif +} diff --git a/tests-clar/config/stress.c b/tests-clar/config/stress.c index 6e7db6e8f..317e877f7 100644 --- a/tests-clar/config/stress.c +++ b/tests-clar/config/stress.c @@ -4,11 +4,13 @@ #include "fileops.h" #include "posix.h" +#define TEST_CONFIG "git-test-config" + void test_config_stress__initialize(void) { git_filebuf file = GIT_FILEBUF_INIT; - cl_git_pass(git_filebuf_open(&file, "git-test-config", 0)); + cl_git_pass(git_filebuf_open(&file, TEST_CONFIG, 0)); git_filebuf_printf(&file, "[color]\n\tui = auto\n"); git_filebuf_printf(&file, "[core]\n\teditor = \n"); @@ -18,19 +20,16 @@ void test_config_stress__initialize(void) void test_config_stress__cleanup(void) { - p_unlink("git-test-config"); + p_unlink(TEST_CONFIG); } void test_config_stress__dont_break_on_invalid_input(void) { const char *editor, *color; - struct git_config_file *file; git_config *config; - cl_assert(git_path_exists("git-test-config")); - cl_git_pass(git_config_file__ondisk(&file, "git-test-config")); - cl_git_pass(git_config_new(&config)); - cl_git_pass(git_config_add_file(config, file, 0)); + cl_assert(git_path_exists(TEST_CONFIG)); + cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG)); cl_git_pass(git_config_get_string(&color, config, "color.ui")); cl_git_pass(git_config_get_string(&editor, config, "core.editor")); @@ -40,13 +39,10 @@ void test_config_stress__dont_break_on_invalid_input(void) void test_config_stress__comments(void) { - struct git_config_file *file; git_config *config; const char *str; - cl_git_pass(git_config_file__ondisk(&file, cl_fixture("config/config12"))); - cl_git_pass(git_config_new(&config)); - cl_git_pass(git_config_add_file(config, file, 0)); + cl_git_pass(git_config_open_ondisk(&config, cl_fixture("config/config12"))); cl_git_pass(git_config_get_string(&str, config, "some.section.other")); cl_assert(!strcmp(str, "hello! \" ; ; ; ")); @@ -62,21 +58,16 @@ void test_config_stress__comments(void) void test_config_stress__escape_subsection_names(void) { - struct git_config_file *file; git_config *config; const char *str; cl_assert(git_path_exists("git-test-config")); - cl_git_pass(git_config_file__ondisk(&file, "git-test-config")); - cl_git_pass(git_config_new(&config)); - cl_git_pass(git_config_add_file(config, file, 0)); + cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG)); cl_git_pass(git_config_set_string(config, "some.sec\\tion.other", "foo")); git_config_free(config); - cl_git_pass(git_config_file__ondisk(&file, "git-test-config")); - cl_git_pass(git_config_new(&config)); - cl_git_pass(git_config_add_file(config, file, 0)); + cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG)); cl_git_pass(git_config_get_string(&str, config, "some.sec\\tion.other")); cl_assert(!strcmp("foo", str)); diff --git a/tests-clar/config/write.c b/tests-clar/config/write.c index 13b669cb2..d98d1dd53 100644 --- a/tests-clar/config/write.c +++ b/tests-clar/config/write.c @@ -3,11 +3,15 @@ void test_config_write__initialize(void) { cl_fixture_sandbox("config/config9"); + cl_fixture_sandbox("config/config15"); + cl_fixture_sandbox("config/config17"); } void test_config_write__cleanup(void) { cl_fixture_cleanup("config9"); + cl_fixture_cleanup("config15"); + cl_fixture_cleanup("config17"); } void test_config_write__replace_value(void) @@ -67,6 +71,40 @@ void test_config_write__delete_value(void) git_config_free(cfg); } +/* + * At the beginning of the test: + * - config9 has: core.dummy2=42 + * - config15 has: core.dummy2=7 + */ +void test_config_write__delete_value_at_specific_level(void) +{ + git_config *cfg, *cfg_specific; + int32_t i; + + cl_git_pass(git_config_open_ondisk(&cfg, "config15")); + cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2")); + cl_assert(i == 7); + git_config_free(cfg); + + cl_git_pass(git_config_new(&cfg)); + cl_git_pass(git_config_add_file_ondisk(cfg, "config9", + GIT_CONFIG_LEVEL_LOCAL, 0)); + cl_git_pass(git_config_add_file_ondisk(cfg, "config15", + GIT_CONFIG_LEVEL_GLOBAL, 0)); + + cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL)); + + cl_git_pass(git_config_delete(cfg_specific, "core.dummy2")); + git_config_free(cfg); + + cl_git_pass(git_config_open_ondisk(&cfg, "config15")); + cl_assert(git_config_get_int32(&i, cfg, "core.dummy2") == GIT_ENOTFOUND); + cl_git_pass(git_config_set_int32(cfg, "core.dummy2", 7)); + + git_config_free(cfg_specific); + git_config_free(cfg); +} + void test_config_write__write_subsection(void) { git_config *cfg; @@ -136,3 +174,57 @@ void test_config_write__escape_value(void) cl_assert_equal_s(str, "this \"has\" quotes and \t"); git_config_free(cfg); } + +void test_config_write__add_value_at_specific_level(void) +{ + git_config *cfg, *cfg_specific; + int i; + int64_t l, expected = +9223372036854775803; + const char *s; + + // open config15 as global level config file + cl_git_pass(git_config_new(&cfg)); + cl_git_pass(git_config_add_file_ondisk(cfg, "config9", + GIT_CONFIG_LEVEL_LOCAL, 0)); + cl_git_pass(git_config_add_file_ondisk(cfg, "config15", + GIT_CONFIG_LEVEL_GLOBAL, 0)); + + cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL)); + + cl_git_pass(git_config_set_int32(cfg_specific, "core.int32global", 28)); + cl_git_pass(git_config_set_int64(cfg_specific, "core.int64global", expected)); + cl_git_pass(git_config_set_bool(cfg_specific, "core.boolglobal", true)); + cl_git_pass(git_config_set_string(cfg_specific, "core.stringglobal", "I'm a global config value!")); + git_config_free(cfg_specific); + git_config_free(cfg); + + // open config15 as local level config file + cl_git_pass(git_config_open_ondisk(&cfg, "config15")); + + cl_git_pass(git_config_get_int32(&i, cfg, "core.int32global")); + cl_assert_equal_i(28, i); + cl_git_pass(git_config_get_int64(&l, cfg, "core.int64global")); + cl_assert(l == expected); + cl_git_pass(git_config_get_bool(&i, cfg, "core.boolglobal")); + cl_assert_equal_b(true, i); + cl_git_pass(git_config_get_string(&s, cfg, "core.stringglobal")); + cl_assert_equal_s("I'm a global config value!", s); + + git_config_free(cfg); +} + +void test_config_write__add_value_at_file_with_no_clrf_at_the_end(void) +{ + git_config *cfg; + int i; + + cl_git_pass(git_config_open_ondisk(&cfg, "config17")); + cl_git_pass(git_config_set_int32(cfg, "core.newline", 7)); + git_config_free(cfg); + + cl_git_pass(git_config_open_ondisk(&cfg, "config17")); + cl_git_pass(git_config_get_int32(&i, cfg, "core.newline")); + cl_assert_equal_i(7, i); + + git_config_free(cfg); +} diff --git a/tests-clar/network/remotes.c b/tests-clar/network/remotes.c index c7ee863e7..91c3e879d 100644 --- a/tests-clar/network/remotes.c +++ b/tests-clar/network/remotes.c @@ -225,3 +225,27 @@ void test_network_remotes__add(void) cl_assert(!strcmp(git_refspec_dst(_refspec), "refs/remotes/addtest/*")); cl_assert_equal_s(git_remote_url(_remote), "http://github.com/libgit2/libgit2"); } + +void test_network_remotes__tagopt(void) +{ + const char *opt; + git_config *cfg; + + cl_git_pass(git_repository_config(&cfg, _repo)); + + git_remote_set_autotag(_remote, GIT_REMOTE_DOWNLOAD_TAGS_ALL); + cl_git_pass(git_remote_save(_remote)); + cl_git_pass(git_config_get_string(&opt, cfg, "remote.test.tagopt")); + cl_assert(!strcmp(opt, "--tags")); + + git_remote_set_autotag(_remote, GIT_REMOTE_DOWNLOAD_TAGS_NONE); + cl_git_pass(git_remote_save(_remote)); + cl_git_pass(git_config_get_string(&opt, cfg, "remote.test.tagopt")); + cl_assert(!strcmp(opt, "--no-tags")); + + git_remote_set_autotag(_remote, GIT_REMOTE_DOWNLOAD_TAGS_AUTO); + cl_git_pass(git_remote_save(_remote)); + cl_assert(git_config_get_string(&opt, cfg, "remote.test.tagopt") == GIT_ENOTFOUND); + + git_config_free(cfg); +} diff --git a/tests-clar/refs/branches/delete.c b/tests-clar/refs/branches/delete.c index 99af44ef4..4e9c70904 100644 --- a/tests-clar/refs/branches/delete.c +++ b/tests-clar/refs/branches/delete.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "refs.h" +#include "repo/repo_helpers.h" static git_repository *repo; static git_reference *fake_remote; @@ -52,11 +53,9 @@ void test_refs_branches_delete__can_delete_a_branch_even_if_HEAD_is_missing(void void test_refs_branches_delete__can_delete_a_branch_when_HEAD_is_orphaned(void) { - git_reference *head; git_reference *branch; - cl_git_pass(git_reference_create_symbolic(&head, repo, GIT_HEAD_FILE, "refs/heads/hide/and/seek", 1)); - git_reference_free(head); + make_head_orphaned(repo, NON_EXISTING_HEAD); cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL)); cl_git_pass(git_branch_delete(branch)); @@ -64,13 +63,15 @@ void test_refs_branches_delete__can_delete_a_branch_when_HEAD_is_orphaned(void) void test_refs_branches_delete__can_delete_a_branch_pointed_at_by_detached_HEAD(void) { - git_reference *master, *head, *branch; + git_reference *head, *branch; - /* Detach HEAD and make it target the commit that "master" points to */ - cl_git_pass(git_reference_lookup(&master, repo, "refs/heads/master")); - cl_git_pass(git_reference_create_oid(&head, repo, "HEAD", git_reference_oid(master), 1)); + cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE)); + cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head)); + cl_assert_equal_s("refs/heads/master", git_reference_target(head)); git_reference_free(head); - git_reference_free(master); + + /* Detach HEAD and make it target the commit that "master" points to */ + git_repository_detach_head(repo); cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL)); cl_git_pass(git_branch_delete(branch)); @@ -89,4 +90,3 @@ void test_refs_branches_delete__can_delete_a_remote_branch(void) cl_git_pass(git_branch_lookup(&branch, repo, "nulltoken/master", GIT_BRANCH_REMOTE)); cl_git_pass(git_branch_delete(branch)); } - diff --git a/tests-clar/refs/branches/ishead.c b/tests-clar/refs/branches/ishead.c index 0d57f00a3..ab17482b8 100644 --- a/tests-clar/refs/branches/ishead.c +++ b/tests-clar/refs/branches/ishead.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "refs.h" +#include "repo/repo_helpers.h" static git_repository *repo; static git_reference *branch; @@ -22,21 +23,13 @@ void test_refs_branches_ishead__can_tell_if_a_branch_is_pointed_at_by_HEAD(void) cl_assert_equal_i(true, git_branch_is_head(branch)); } -static void make_head_orphaned(void) -{ - git_reference *head; - - cl_git_pass(git_reference_create_symbolic(&head, repo, GIT_HEAD_FILE, "refs/heads/hide/and/seek", 1)); - git_reference_free(head); -} - void test_refs_branches_ishead__can_properly_handle_orphaned_HEAD(void) { git_repository_free(repo); repo = cl_git_sandbox_init("testrepo.git"); - make_head_orphaned(); + make_head_orphaned(repo, NON_EXISTING_HEAD); cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master")); diff --git a/tests-clar/repo/head.c b/tests-clar/repo/head.c index 4113289f0..58f525e2b 100644 --- a/tests-clar/repo/head.c +++ b/tests-clar/repo/head.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "refs.h" +#include "repo_helpers.h" git_repository *repo; @@ -16,29 +17,18 @@ void test_repo_head__cleanup(void) void test_repo_head__head_detached(void) { git_reference *ref; - git_oid oid; cl_assert(git_repository_head_detached(repo) == 0); - /* detach the HEAD */ - git_oid_fromstr(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd"); - cl_git_pass(git_reference_create_oid(&ref, repo, "HEAD", &oid, 1)); - cl_assert(git_repository_head_detached(repo) == 1); - git_reference_free(ref); + git_repository_detach_head(repo); + + cl_assert_equal_i(true, git_repository_head_detached(repo)); /* take the reop back to it's original state */ cl_git_pass(git_reference_create_symbolic(&ref, repo, "HEAD", "refs/heads/master", 1)); - cl_assert(git_repository_head_detached(repo) == 0); - git_reference_free(ref); -} -static void make_head_orphaned(void) -{ - git_reference *head; - - cl_git_pass(git_reference_create_symbolic(&head, repo, GIT_HEAD_FILE, "refs/heads/hide/and/seek", 1)); - git_reference_free(head); + cl_assert_equal_i(false, git_repository_head_detached(repo)); } void test_repo_head__head_orphan(void) @@ -47,7 +37,7 @@ void test_repo_head__head_orphan(void) cl_assert(git_repository_head_orphan(repo) == 0); - make_head_orphaned(); + make_head_orphaned(repo, NON_EXISTING_HEAD); cl_assert(git_repository_head_orphan(repo) == 1); @@ -174,7 +164,7 @@ void test_repo_head__detach_head_Fails_if_HEAD_and_point_to_a_non_commitish(void void test_repo_head__detaching_an_orphaned_head_returns_GIT_EORPHANEDHEAD(void) { - make_head_orphaned(); + make_head_orphaned(repo, NON_EXISTING_HEAD); cl_assert_equal_i(GIT_EORPHANEDHEAD, git_repository_detach_head(repo)); } @@ -183,7 +173,14 @@ void test_repo_head__retrieving_an_orphaned_head_returns_GIT_EORPHANEDHEAD(void) { git_reference *head; - make_head_orphaned(); + make_head_orphaned(repo, NON_EXISTING_HEAD); cl_assert_equal_i(GIT_EORPHANEDHEAD, git_repository_head(&head, repo)); } + +void test_repo_head__can_tell_if_an_orphaned_head_is_detached(void) +{ + make_head_orphaned(repo, NON_EXISTING_HEAD); + + cl_assert_equal_i(false, git_repository_head_detached(repo)); +} diff --git a/tests-clar/repo/repo_helpers.c b/tests-clar/repo/repo_helpers.c new file mode 100644 index 000000000..35271feaa --- /dev/null +++ b/tests-clar/repo/repo_helpers.c @@ -0,0 +1,11 @@ +#include "clar_libgit2.h" +#include "refs.h" +#include "repo_helpers.h" + +void make_head_orphaned(git_repository* repo, const char *target) +{ + git_reference *head; + + cl_git_pass(git_reference_create_symbolic(&head, repo, GIT_HEAD_FILE, target, 1)); + git_reference_free(head); +} diff --git a/tests-clar/repo/repo_helpers.h b/tests-clar/repo/repo_helpers.h new file mode 100644 index 000000000..e6aeb4873 --- /dev/null +++ b/tests-clar/repo/repo_helpers.h @@ -0,0 +1,5 @@ +#include "common.h" + +#define NON_EXISTING_HEAD "refs/heads/hide/and/seek" + +extern void make_head_orphaned(git_repository* repo, const char *target); diff --git a/tests-clar/reset/soft.c b/tests-clar/reset/soft.c index 3200c1591..1872baf3b 100644 --- a/tests-clar/reset/soft.c +++ b/tests-clar/reset/soft.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "reset_helpers.h" +#include "repo/repo_helpers.h" static git_repository *repo; static git_object *target; @@ -39,20 +40,9 @@ void test_reset_soft__can_reset_the_non_detached_Head_to_the_specified_commit(vo assert_reset_soft(false); } -static void detach_head(void) -{ - git_reference *head; - git_oid oid; - - cl_git_pass(git_reference_name_to_oid(&oid, repo, "HEAD")); - - cl_git_pass(git_reference_create_oid(&head, repo, "HEAD", &oid, true)); - git_reference_free(head); -} - void test_reset_soft__can_reset_the_detached_Head_to_the_specified_commit(void) { - detach_head(); + git_repository_detach_head(repo); assert_reset_soft(true); } @@ -100,3 +90,23 @@ void test_reset_soft__cannot_reset_to_a_tag_not_pointing_at_a_commit(void) retrieve_target_from_oid(&target, repo, "521d87c1ec3aef9824daf6d96cc0ae3710766d91"); cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT)); } + +void test_reset_soft__resetting_against_an_orphaned_head_repo_makes_the_head_no_longer_orphaned(void) +{ + git_reference *head; + + retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO); + + make_head_orphaned(repo, NON_EXISTING_HEAD); + + cl_assert_equal_i(true, git_repository_head_orphan(repo)); + + cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT)); + + cl_assert_equal_i(false, git_repository_head_orphan(repo)); + + cl_git_pass(git_reference_lookup(&head, repo, NON_EXISTING_HEAD)); + cl_assert_equal_i(0, git_oid_streq(git_reference_oid(head), KNOWN_COMMIT_IN_BARE_REPO)); + + git_reference_free(head); +} diff --git a/tests-clar/resources/config/config15 b/tests-clar/resources/config/config15 new file mode 100644 index 000000000..6d34f817b --- /dev/null +++ b/tests-clar/resources/config/config15 @@ -0,0 +1,3 @@ +[core] + dummy2 = 7 + global = 17 diff --git a/tests-clar/resources/config/config16 b/tests-clar/resources/config/config16 new file mode 100644 index 000000000..f25cdb728 --- /dev/null +++ b/tests-clar/resources/config/config16 @@ -0,0 +1,3 @@ +[core] + dummy2 = 28 + system = 11 diff --git a/tests-clar/resources/config/config17 b/tests-clar/resources/config/config17 new file mode 100644 index 000000000..ca25a86af --- /dev/null +++ b/tests-clar/resources/config/config17 @@ -0,0 +1,3 @@ +[core] + dummy2 = 7 + global = 17
\ No newline at end of file diff --git a/tests-clar/resources/config/config18 b/tests-clar/resources/config/config18 new file mode 100644 index 000000000..cb6fd5ebc --- /dev/null +++ b/tests-clar/resources/config/config18 @@ -0,0 +1,5 @@ +[core] + int32global = 28 + int64global = 9223372036854775803 + boolglobal = true + stringglobal = I'm a global config value!
\ No newline at end of file diff --git a/tests-clar/resources/config/config19 b/tests-clar/resources/config/config19 new file mode 100644 index 000000000..f3ae5a640 --- /dev/null +++ b/tests-clar/resources/config/config19 @@ -0,0 +1,5 @@ +[core] + int32global = -1 + int64global = -2 + boolglobal = false + stringglobal = don't find me!
\ No newline at end of file diff --git a/tests-clar/submodule/modify.c b/tests-clar/submodule/modify.c index 0fd732cc3..bfb8c8aaf 100644 --- a/tests-clar/submodule/modify.c +++ b/tests-clar/submodule/modify.c @@ -73,12 +73,10 @@ void test_submodule_modify__add(void) git_config_free(cfg); } -static int delete_one_config( - const char *var_name, const char *value, void *payload) +static int delete_one_config(const git_config_entry *entry, void *payload) { git_config *cfg = payload; - GIT_UNUSED(value); - return git_config_delete(cfg, var_name); + return git_config_delete(cfg, entry->name); } static int init_one_submodule( |