Merge branch 'development' into gsoc-push
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 08e7b47..a573946 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -40,8 +40,10 @@
 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 @@
 		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 f46eaa4..b31a06e 100644
--- a/Makefile.embed
+++ b/Makefile.embed
@@ -15,7 +15,7 @@
 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 a7d8974..67408f9 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 *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_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 @@
  *
  * 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 @@
  *
  * 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_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(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 @@
 /**
  * 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 @@
  * 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 @@
  * 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 @@
  * @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_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_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_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_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_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_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_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_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 964a108..e18615e 100644
--- a/include/git2/ignore.h
+++ b/include/git2/ignore.h
@@ -24,7 +24,7 @@
  *
  * 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 6137746..76d265f 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -255,11 +255,19 @@
 	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 @@
 	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 b89c16b..f9bd205 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 @@
 	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 @@
 	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 @@
 	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 @@
 	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_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_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_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_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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 
 	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_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_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 @@
 	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 @@
 
 	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 471b42d..16b8413 100644
--- a/src/config.h
+++ b/src/config.h
@@ -27,9 +27,4 @@
 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 4ba83d1..92fe132 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -22,15 +22,9 @@
 
 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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 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 @@
 				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 @@
 		}
 
 		/* 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 @@
 			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 @@
 
 	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 @@
 /*
  * 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 @@
 	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 @@
 	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 @@
 		/* and throw the callback only on the variables that
 		 * match the regex */
 		do {
-			if (regexec(&regex, var->value, 0, NULL, 0) == 0) {
+			if (regexec(&regex, 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 @@
 		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 @@
 	}
 
 	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 @@
 	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 @@
 
 	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 @@
 	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 @@
 
 			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 @@
 			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 @@
 				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 bf687b5..b500dd6 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(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_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_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 0a16a10..b4eddbd 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -86,6 +86,11 @@
 			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 @@
 
 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 @@
 			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 @@
 	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 db0888a..43e0eda 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -461,23 +461,23 @@
 		&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 dfa095b..560ae17 100644
--- a/src/reset.c
+++ b/src/reset.c
@@ -19,6 +19,45 @@
 	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 @@
 	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 @@
 
 	//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 @@
 	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 f0a16f2..41e8aba 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 @@
 #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
similarity index 100%
rename from src/sha1.c
rename to src/sha1/sha1.c
diff --git a/src/submodule.c b/src/submodule.c
index 1805286..e3657f9 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -72,7 +72,7 @@
 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_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 @@
 	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 @@
 	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 @@
 			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 f2f81e5..d36034c 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__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 0000000..d947856
--- /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 3b40cd0..26537e2 100644
--- a/tests-clar/config/multivar.c
+++ b/tests-clar/config/multivar.c
@@ -12,13 +12,11 @@
 	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 @@
 	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 fae3ce9..6bd719f 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 fcd2246..cf781e6 100644
--- a/tests-clar/config/read.c
+++ b/tests-clar/config/read.c
@@ -191,22 +191,24 @@
 	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 @@
 	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 @@
 	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 @@
 	git_config_free(cfg);
 }
 
-#if 0
-
-BEGIN_TEST(config10, "a repo's config overrides the global config")
-	git_repository *repo;
+void test_config_read__read_git_config_entry(void)
+{
 	git_config *cfg;
-	int32_t version;
+	const git_config_entry *entry;
 
-	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);
+	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);
-	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.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 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.dummy2"));
+	cl_assert_equal_i(28, i);
+
 	git_config_free(cfg);
-	git_repository_free(repo);
-END_TEST
-#endif
+
+	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);
+
+	git_config_free(cfg);
+}
+
+/*
+ * 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 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.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);
+}
diff --git a/tests-clar/config/stress.c b/tests-clar/config/stress.c
index 6e7db6e..317e877 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__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__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__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 13b669c..d98d1dd 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 @@
 	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 @@
 	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 c7ee863..91c3e87 100644
--- a/tests-clar/network/remotes.c
+++ b/tests-clar/network/remotes.c
@@ -225,3 +225,27 @@
 	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 99af44e..4e9c709 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_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_pointed_at_by_detached_HEAD(void)
 {
-	git_reference *master, *head, *branch;
+	git_reference *head, *branch;
+
+	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);
 
 	/* 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));
-	git_reference_free(head);
-	git_reference_free(master);
+	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 @@
 	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 0d57f00..ab17482 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 @@
 	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 4113289..58f525e 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__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 @@
 
 	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__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 @@
 {
 	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 0000000..35271fe
--- /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 0000000..e6aeb48
--- /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 3200c15..1872baf 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 @@
 	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 @@
 	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 0000000..6d34f81
--- /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 0000000..f25cdb7
--- /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 0000000..ca25a86
--- /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 0000000..cb6fd5e
--- /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 0000000..f3ae5a6
--- /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 0fd732c..bfb8c8a 100644
--- a/tests-clar/submodule/modify.c
+++ b/tests-clar/submodule/modify.c
@@ -73,12 +73,10 @@
 	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(