Merge pull request #3739 from ethomson/0.24.1

Backport bug fixes to 0.24
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f4e56e6..c79b263 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -412,7 +412,7 @@
 	# /MTd - Statically link the multithreaded debug version of the CRT
 	# /MDd - Dynamically link the multithreaded debug version of the CRT
 	# /RTC1 - Run time checks
-	SET(CMAKE_C_FLAGS_DEBUG "/Zi /Od /D_DEBUG /RTC1 ${CRT_FLAG_DEBUG}")
+	SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Zi /Od /D_DEBUG /RTC1 ${CRT_FLAG_DEBUG}")
 
 	# /DNDEBUG - Disables asserts
 	# /MT - Statically link the multithreaded release version of the CRT
@@ -464,7 +464,7 @@
 	ENDIF()
 
 	IF (WIN32 AND NOT CYGWIN)
-		SET(CMAKE_C_FLAGS_DEBUG "-D_DEBUG")
+		SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
 	ENDIF ()
 
 	IF (MINGW) # MinGW always does PIC and complains if we tell it to
@@ -608,6 +608,8 @@
 	IF (LIBGIT2_FILENAME)
 		ADD_DEFINITIONS(-DLIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
 		SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
+	ELSEIF (DEFINED LIBGIT2_PREFIX)
+		SET_TARGET_PROPERTIES(git2 PROPERTIES PREFIX "${LIBGIT2_PREFIX}")
 	ENDIF()
 ENDIF()
 STRING(REPLACE ";" " " LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS}")
diff --git a/deps/http-parser/http_parser.c b/deps/http-parser/http_parser.c
index 2035302..27bdd20 100644
--- a/deps/http-parser/http_parser.c
+++ b/deps/http-parser/http_parser.c
@@ -99,7 +99,7 @@
     FOR##_mark = NULL;                                               \
   }                                                                  \
 } while (0)
-  
+
 /* Run the data callback FOR and consume the current byte */
 #define CALLBACK_DATA(FOR)                                           \
     CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
@@ -444,6 +444,9 @@
         return s_req_path;
       }
 
+      /* The schema must start with an alpha character. After that, it may
+       * consist of digits, '+', '-' or '.', followed by a ':'.
+       */
       if (IS_ALPHA(ch)) {
         return s_req_schema;
       }
@@ -451,7 +454,7 @@
       break;
 
     case s_req_schema:
-      if (IS_ALPHA(ch)) {
+      if (IS_ALPHANUM(ch) || ch == '+' || ch == '-' || ch == '.') {
         return s;
       }
 
diff --git a/include/git2/common.h b/include/git2/common.h
index 4f43185..d7428d8 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -24,10 +24,19 @@
  GIT_BEGIN_DECL
 # include "inttypes.h"
  GIT_END_DECL
-#else
+/** This check is needed for importing this file in an iOS/OS X framework throws an error in Xcode otherwise.*/
+#elif !defined(__CLANG_INTTYPES_H)
 # include <inttypes.h>
 #endif
 
+#ifdef DOCURIUM
+/*
+ * This is so clang's doc parser acknowledges comments on functions
+ * with size_t parameters.
+ */
+typedef size_t size_t;
+#endif
+
 /** Declare a public function exported for application use. */
 #if __GNUC__ >= 4
 # define GIT_EXTERN(type) extern \
@@ -148,6 +157,7 @@
 	GIT_OPT_SET_SSL_CERT_LOCATIONS,
 	GIT_OPT_SET_USER_AGENT,
 	GIT_OPT_ENABLE_STRICT_OBJECT_CREATION,
+	GIT_OPT_SET_SSL_CIPHERS,
 } git_libgit2_opt_t;
 
 /**
@@ -259,6 +269,11 @@
  *		> example, when this is enabled, the parent(s) and tree inputs
  *		> will be validated when creating a new commit.  This defaults
  *		> to disabled.
+ *	* opts(GIT_OPT_SET_SSL_CIPHERS, const char *ciphers)
+ *
+ *		> Set the SSL ciphers use for HTTPS connections.
+ *		>
+ *		> - `ciphers` is the list of ciphers that are eanbled.
  *
  * @param option Option key
  * @param ... value to set the option
diff --git a/script/coverity.sh b/script/coverity.sh
index 8c82689..7fe9eb4 100755
--- a/script/coverity.sh
+++ b/script/coverity.sh
@@ -49,10 +49,24 @@
 # Upload results
 tar czf libgit2.tgz cov-int
 SHA=$(git rev-parse --short HEAD)
-curl \
+
+HTML="$(curl \
+	--silent \
+	--write-out "\n%{http_code}" \
 	--form token="$COVERITY_TOKEN" \
 	--form email=bs@github.com \
 	--form file=@libgit2.tgz \
 	--form version="$SHA" \
 	--form description="Travis build" \
-	https://scan.coverity.com/builds?project=libgit2
+	https://scan.coverity.com/builds?project=libgit2)"
+# Body is everything up to the last line
+BODY="$(echo "$HTML" | head -n-1)"
+# Status code is the last line
+STATUS_CODE="$(echo "$HTML" | tail -n1)"
+
+echo "${BODY}"
+
+if [ "${STATUS_CODE}" != "201" ]; then
+	echo "Received error code ${STATUS_CODE} from Coverity"
+	exit 1
+fi
diff --git a/src/array.h b/src/array.h
index 7cd9b71..490e6be 100644
--- a/src/array.h
+++ b/src/array.h
@@ -82,4 +82,44 @@
 
 #define git_array_valid_index(a, i) ((i) < (a).size)
 
+#define git_array_foreach(a, i, element) \
+	for ((i) = 0; (i) < (a).size && ((element) = &(a).ptr[(i)]); (i)++)
+
+
+GIT_INLINE(int) git_array__search(
+	size_t *out,
+	void *array_ptr,
+	size_t item_size,
+	size_t array_len,
+	int (*compare)(const void *, const void *),
+	const void *key)
+{
+	size_t lim;
+	unsigned char *part, *array = array_ptr, *base = array_ptr;
+	int cmp;
+
+	for (lim = array_len; lim != 0; lim >>= 1) {
+		part = base + (lim >> 1) * item_size;
+		cmp = (*compare)(key, part);
+
+		if (cmp == 0) {
+			base = part;
+			break;
+		}
+		if (cmp > 0) { /* key > p; take right partition */
+			base = part + 1 * item_size;
+			lim--;
+		} /* else take left partition */
+	}
+
+	if (out)
+		*out = (base - array) / item_size;
+
+	return (cmp == 0) ? 0 : GIT_ENOTFOUND;
+}
+
+#define git_array_search(out, a, cmp, key) \
+	git_array__search(out, (a).ptr, sizeof(*(a).ptr), (a).size, \
+		(cmp), (key))
+
 #endif
diff --git a/src/blame.c b/src/blame.c
index 2daf915..2c8584b 100644
--- a/src/blame.c
+++ b/src/blame.c
@@ -178,7 +178,7 @@
 	return NULL;
 }
 
-static void normalize_options(
+static int normalize_options(
 		git_blame_options *out,
 		const git_blame_options *in,
 		git_repository *repo)
@@ -190,7 +190,9 @@
 
 	/* No newest_commit => HEAD */
 	if (git_oid_iszero(&out->newest_commit)) {
-		git_reference_name_to_id(&out->newest_commit, repo, "HEAD");
+		if (git_reference_name_to_id(&out->newest_commit, repo, "HEAD") < 0) {
+			return -1;
+		}
 	}
 
 	/* min_line 0 really means 1 */
@@ -204,6 +206,8 @@
 		out->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
 	if (out->flags & GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES)
 		out->flags |= GIT_BLAME_TRACK_COPIES_SAME_FILE;
+
+	return 0;
 }
 
 static git_blame_hunk *split_hunk_in_vector(
@@ -362,7 +366,8 @@
 	git_blame *blame = NULL;
 
 	assert(out && repo && path);
-	normalize_options(&normOptions, options, repo);
+	if ((error = normalize_options(&normOptions, options, repo)) < 0)
+		goto on_error;
 
 	blame = git_blame__alloc(repo, normOptions, path);
 	GITERR_CHECK_ALLOC(blame);
diff --git a/src/blame_git.c b/src/blame_git.c
index b8b5682..700207e 100644
--- a/src/blame_git.c
+++ b/src/blame_git.c
@@ -525,7 +525,8 @@
 		if (sg_origin[i])
 			continue;
 
-		git_commit_parent(&p, origin->commit, i);
+		if ((error = git_commit_parent(&p, origin->commit, i)) < 0)
+			goto finish;
 		porigin = find_origin(blame, p, origin);
 
 		if (!porigin)
diff --git a/src/commit.c b/src/commit.c
index 685c642..5ed9c47 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -676,7 +676,7 @@
 
 	buf = git_odb_object_data(obj);
 
-	while ((h = strchr(buf, '\n')) && h[1] != '\0' && h[1] != '\n') {
+	while ((h = strchr(buf, '\n')) && h[1] != '\0') {
 		h++;
 		if (git__prefixcmp(buf, field)) {
 			if (git_buf_put(signed_data, buf, h - buf) < 0)
diff --git a/src/config_cache.c b/src/config_cache.c
index c859ec1..dbea871 100644
--- a/src/config_cache.c
+++ b/src/config_cache.c
@@ -86,7 +86,8 @@
 	struct map_data *data = &_cvar_maps[(int)cvar];
 	git_config_entry *entry;
 
-	git_config__lookup_entry(&entry, config, data->cvar_name, false);
+	if ((error = git_config__lookup_entry(&entry, config, data->cvar_name, false)) < 0)
+		return error;
 
 	if (!entry)
 		*out = data->default_value;
diff --git a/src/config_file.c b/src/config_file.c
index 5f5e309..ca4345c 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -553,30 +553,15 @@
 	git_config_backend *cfg, const char *name, const char *regexp, const char *value)
 {
 	diskfile_backend *b = (diskfile_backend *)cfg;
-	refcounted_strmap *map;
-	git_strmap *values;
 	char *key;
 	regex_t preg;
 	int result;
-	khiter_t pos;
 
 	assert(regexp);
 
 	if ((result = git_config__normalize_name(name, &key)) < 0)
 		return result;
 
-	map = refcounted_strmap_take(&b->header);
-	values = b->header.values->values;
-
-	pos = git_strmap_lookup_index(values, key);
-	if (!git_strmap_valid_index(values, pos)) {
-		/* If we don't have it, behave like a normal set */
-		result = config_set(cfg, name, value);
-		refcounted_strmap_free(map);
-		git__free(key);
-		return result;
-	}
-
 	result = regcomp(&preg, regexp, REG_EXTENDED);
 	if (result != 0) {
 		giterr_set_regex(&preg, result);
@@ -591,7 +576,6 @@
 	result = config_refresh(cfg);
 
 out:
-	refcounted_strmap_free(map);
 	git__free(key);
 	regfree(&preg);
 
@@ -1032,6 +1016,11 @@
 	 */
 
 	first_quote = strchr(line, '"');
+	if (first_quote == NULL) {
+		set_parse_error(reader, 0, "Missing quotation marks in section header");
+		return -1;
+	}
+
 	last_quote = strrchr(line, '"');
 	quoted_len = last_quote - first_quote;
 
@@ -1483,7 +1472,7 @@
 	int (*on_section)(struct reader **reader, const char *current_section, const char *line, size_t line_len, void *data),
 	int (*on_variable)(struct reader **reader, const char *current_section, char *var_name, char *var_value, const char *line, size_t line_len, void *data),
 	int (*on_comment)(struct reader **reader, const char *line, size_t line_len, void *data),
-	int (*on_eof)(struct reader **reader, void *data),
+	int (*on_eof)(struct reader **reader, const char *current_section, void *data),
 	void *data)
 {
 	char *current_section = NULL, *var_name, *var_value, *line_start;
@@ -1534,7 +1523,7 @@
 	}
 
 	if (on_eof)
-		result = on_eof(&reader, data);
+		result = on_eof(&reader, current_section, data);
 
 	git__free(current_section);
 	return result;
@@ -1850,7 +1839,8 @@
 	return write_line_to(&write_data->buffered_comment, line, line_len);
 }
 
-static int write_on_eof(struct reader **reader, void *data)
+static int write_on_eof(
+	struct reader **reader, const char *current_section, void *data)
 {
 	struct write_data *write_data = (struct write_data *)data;
 	int result = 0;
@@ -1869,7 +1859,11 @@
 	 * value.
 	 */
 	if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
-		if ((result = write_section(write_data->buf, write_data->section)) == 0)
+		/* write the section header unless we're already in it */
+		if (!current_section || strcmp(current_section, write_data->section))
+			result = write_section(write_data->buf, write_data->section);
+
+		if (!result)
 			result = write_value(write_data);
 	}
 
diff --git a/src/describe.c b/src/describe.c
index 48f04e8..13ddad5 100644
--- a/src/describe.c
+++ b/src/describe.c
@@ -582,7 +582,8 @@
 	best = (struct possible_tag *)git_vector_get(&all_matches, 0);
 
 	if (gave_up_on) {
-		git_pqueue_insert(&list, gave_up_on);
+		if ((error = git_pqueue_insert(&list, gave_up_on)) < 0)
+			goto cleanup;
 		seen_commits--;
 	}
 	if ((error = finish_depth_computation(
diff --git a/src/diff_tform.c b/src/diff_tform.c
index 8577f06..6a6a628 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -261,7 +261,7 @@
 	if (!given ||
 		 (given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG)
 	{
-		if (diff->repo) {
+		if (cfg) {
 			char *rule =
 				git_config__get_string_force(cfg, "diff.renames", "true");
 			int boolval;
@@ -318,8 +318,10 @@
 #undef USE_DEFAULT
 
 	if (!opts->rename_limit) {
-		opts->rename_limit = git_config__get_int_force(
-			cfg, "diff.renamelimit", DEFAULT_RENAME_LIMIT);
+		if (cfg) {
+			opts->rename_limit = git_config__get_int_force(
+				cfg, "diff.renamelimit", DEFAULT_RENAME_LIMIT);
+		}
 
 		if (opts->rename_limit <= 0)
 			opts->rename_limit = DEFAULT_RENAME_LIMIT;
diff --git a/src/filebuf.c b/src/filebuf.c
index 17efe87..101d508 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -70,6 +70,7 @@
 		git_file source;
 		char buffer[FILEIO_BUFSIZE];
 		ssize_t read_bytes;
+		int error;
 
 		source = p_open(file->path_original, O_RDONLY);
 		if (source < 0) {
@@ -80,7 +81,8 @@
 		}
 
 		while ((read_bytes = p_read(source, buffer, sizeof(buffer))) > 0) {
-			p_write(file->fd, buffer, read_bytes);
+			if ((error = p_write(file->fd, buffer, read_bytes)) < 0)
+				break;
 			if (file->compute_digest)
 				git_hash_update(&file->digest, buffer, read_bytes);
 		}
@@ -90,6 +92,9 @@
 		if (read_bytes < 0) {
 			giterr_set(GITERR_OS, "Failed to read file '%s'", file->path_original);
 			return -1;
+		} else if (error < 0) {
+			giterr_set(GITERR_OS, "Failed to write file '%s'", file->path_lock);
+			return -1;
 		}
 	}
 
diff --git a/src/global.c b/src/global.c
index 0bfde1e..adf353d 100644
--- a/src/global.c
+++ b/src/global.c
@@ -27,6 +27,7 @@
 static git_atomic git__n_shutdown_callbacks;
 static git_atomic git__n_inits;
 char *git__user_agent;
+char *git__ssl_ciphers;
 
 void git__on_shutdown(git_global_shutdown_fn callback)
 {
@@ -83,6 +84,7 @@
 	}
 
 	git__free(git__user_agent);
+	git__free(git__ssl_ciphers);
 
 #if defined(GIT_MSVC_CRTDBG)
 	git_win32__crtdbg_stacktrace_cleanup();
@@ -222,6 +224,20 @@
 	TlsSetValue(_tls_index, NULL);
 }
 
+BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved)
+{
+	/* This is how Windows lets us know our thread is being shut down */
+	if (fdwReason == DLL_THREAD_DETACH) {
+		git__free_tls_data();
+	}
+
+	/*
+	 * Windows pays attention to this during library loading. We don't do anything
+	 * so we trivially succeed.
+	 */
+	return TRUE;
+}
+
 #elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
 
 static pthread_key_t _tls_key;
diff --git a/src/global.h b/src/global.h
index 9fdcee5..2199515 100644
--- a/src/global.h
+++ b/src/global.h
@@ -36,5 +36,6 @@
 extern void git__free_tls_data(void);
 
 extern const char *git_libgit2__user_agent(void);
+extern const char *git_libgit2__ssl_ciphers(void);
 
 #endif
diff --git a/src/ignore.c b/src/ignore.c
index aedc140..ac2af4f 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -263,10 +263,18 @@
 		goto cleanup;
 
 	/* given a unrooted path in a non-bare repo, resolve it */
-	if (workdir && git_path_root(path) < 0)
-		error = git_path_find_dir(&ignores->dir, path, workdir);
-	else
+	if (workdir && git_path_root(path) < 0) {
+		git_buf local = GIT_BUF_INIT;
+
+		if ((error = git_path_dirname_r(&local, path)) < 0 ||
+		    (error = git_path_resolve_relative(&local, 0)) < 0 ||
+		    (error = git_path_to_dir(&local)) < 0 ||
+		    (error = git_buf_joinpath(&ignores->dir, workdir, local.ptr)) < 0)
+		{;} /* Nothing, we just want to stop on the first error */
+		git_buf_free(&local);
+	} else {
 		error = git_buf_joinpath(&ignores->dir, path, "");
+	}
 	if (error < 0)
 		goto cleanup;
 
diff --git a/src/index.c b/src/index.c
index b97f809..63e4796 100644
--- a/src/index.c
+++ b/src/index.c
@@ -963,14 +963,20 @@
 	*reuc_out = reuc = reuc_entry_alloc(path);
 	GITERR_CHECK_ALLOC(reuc);
 
-	if ((reuc->mode[0] = ancestor_mode) > 0)
+	if ((reuc->mode[0] = ancestor_mode) > 0) {
+		assert(ancestor_oid);
 		git_oid_cpy(&reuc->oid[0], ancestor_oid);
+	}
 
-	if ((reuc->mode[1] = our_mode) > 0)
+	if ((reuc->mode[1] = our_mode) > 0) {
+		assert(our_oid);
 		git_oid_cpy(&reuc->oid[1], our_oid);
+	}
 
-	if ((reuc->mode[2] = their_mode) > 0)
+	if ((reuc->mode[2] = their_mode) > 0) {
+		assert(their_oid);
 		git_oid_cpy(&reuc->oid[2], their_oid);
+	}
 
 	return 0;
 }
@@ -2830,7 +2836,7 @@
 		return -1;
 
 	entry->mode = tentry->attr;
-	entry->id = tentry->oid;
+	git_oid_cpy(&entry->id, git_tree_entry_id(tentry));
 
 	/* look for corresponding old entry and copy data to new entry */
 	if (data->old_entries != NULL &&
diff --git a/src/indexer.c b/src/indexer.c
index 9aa0925..a3a8669 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -449,7 +449,7 @@
 static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t size)
 {
 	git_file fd = idx->pack->mwf.fd;
-	size_t page_size;
+	size_t mmap_alignment;
 	size_t page_offset;
 	git_off_t page_start;
 	unsigned char *map_data;
@@ -458,11 +458,11 @@
 
 	assert(data && size);
 
-	if ((error = git__page_size(&page_size)) < 0)
+	if ((error = git__mmap_alignment(&mmap_alignment)) < 0)
 		return error;
 
-	/* the offset needs to be at the beginning of the a page boundary */
-	page_offset = offset % page_size;
+	/* the offset needs to be at the mmap boundary for the platform */
+	page_offset = offset % mmap_alignment;
 	page_start = offset - page_offset;
 
 	if ((error = p_mmap(&map, page_offset + size, GIT_PROT_WRITE, GIT_MAP_SHARED, fd, page_start)) < 0)
@@ -777,7 +777,6 @@
 
 		curpos = delta->delta_off;
 		error = git_packfile_unpack_header(&size, &type, &idx->pack->mwf, &w, &curpos);
-		git_mwindow_close(&w);
 		if (error < 0)
 			return error;
 
@@ -914,12 +913,17 @@
 	git_filebuf index_file = {0};
 	void *packfile_trailer;
 
+	if (!idx->parsed_header) {
+		giterr_set(GITERR_INDEXER, "incomplete pack header");
+		return -1;
+	}
+
 	if (git_hash_ctx_init(&ctx) < 0)
 		return -1;
 
 	/* Test for this before resolve_deltas(), as it plays with idx->off */
-	if (idx->off < idx->pack->mwf.size - 20) {
-		giterr_set(GITERR_INDEXER, "Unexpected data at the end of the pack");
+	if (idx->off + 20 < idx->pack->mwf.size) {
+		giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack");
 		return -1;
 	}
 
diff --git a/src/iterator.c b/src/iterator.c
index 024a975..cb1ea6a 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -458,7 +458,7 @@
 		/* try to load trees for items in [current,next) range */
 		if (!error && git_tree_entry__is_tree(te))
 			error = git_tree_lookup(
-				&tf->entries[tf->next]->tree, ti->base.repo, &te->oid);
+				&tf->entries[tf->next]->tree, ti->base.repo, te->oid);
 	}
 
 	if (tf->next > tf->current + 1)
@@ -603,7 +603,7 @@
     te = tf->entries[tf->current]->te;
 
 	ti->entry.mode = te->attr;
-	git_oid_cpy(&ti->entry.id, &te->oid);
+	git_oid_cpy(&ti->entry.id, te->oid);
 
 	ti->entry.path = tree_iterator__current_filename(ti, te);
 	GITERR_CHECK_ALLOC(ti->entry.path);
diff --git a/src/mwindow.c b/src/mwindow.c
index 55c8d89..d3e9be7 100644
--- a/src/mwindow.c
+++ b/src/mwindow.c
@@ -296,8 +296,18 @@
 	 */
 
 	if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
-		git__free(w);
-		return NULL;
+		/*
+		 * The first error might be down to memory fragmentation even if
+		 * we're below our soft limits, so free up what we can and try again.
+		 */
+
+		while (git_mwindow_close_lru(mwf) == 0)
+			/* nop */;
+
+		if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
+			git__free(w);
+			return NULL;
+		}
 	}
 
 	ctl->mmap_calls++;
diff --git a/src/object.c b/src/object.c
index ebf77fb..1d45f9f 100644
--- a/src/object.c
+++ b/src/object.c
@@ -12,6 +12,7 @@
 #include "commit.h"
 #include "tree.h"
 #include "blob.h"
+#include "oid.h"
 #include "tag.h"
 
 bool git_object__strict_input_validation = true;
@@ -166,13 +167,9 @@
 			error = git_odb_read(&odb_obj, odb, id);
 		}
 	} else {
-		git_oid short_oid;
+		git_oid short_oid = {{ 0 }};
 
-		/* We copy the first len*4 bits from id and fill the remaining with 0s */
-		memcpy(short_oid.id, id->id, (len + 1) / 2);
-		if (len % 2)
-			short_oid.id[len / 2] &= 0xF0;
-		memset(short_oid.id + (len + 1) / 2, 0, (GIT_OID_HEXSZ - len) / 2);
+		git_oid__cpy_prefix(&short_oid, id, len);
 
 		/* If len < GIT_OID_HEXSZ (a strict short oid was given), we have
 		 * 2 options :
diff --git a/src/odb.c b/src/odb.c
index 1c877c9..cb0f706 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -725,7 +725,8 @@
 				git_oid_cpy(out, short_id);
 			return 0;
 		} else {
-			return git_odb__error_notfound("no match for id prefix", short_id);
+			return git_odb__error_notfound(
+				"no match for id prefix", short_id, len);
 		}
 	}
 
@@ -740,7 +741,7 @@
 		error = odb_exists_prefix_1(out, db, &key, len, true);
 
 	if (error == GIT_ENOTFOUND)
-		return git_odb__error_notfound("no match for id prefix", &key);
+		return git_odb__error_notfound("no match for id prefix", &key, len);
 
 	return error;
 }
@@ -881,7 +882,7 @@
 		error = odb_read_1(out, db, id, true);
 
 	if (error == GIT_ENOTFOUND)
-		return git_odb__error_notfound("no match for id", id);
+		return git_odb__error_notfound("no match for id", id, GIT_OID_HEXSZ);
 
 	return error;
 }
@@ -967,7 +968,7 @@
 		error = read_prefix_1(out, db, &key, len, true);
 
 	if (error == GIT_ENOTFOUND)
-		return git_odb__error_notfound("no match for prefix", &key);
+		return git_odb__error_notfound("no match for prefix", &key, len);
 
 	return error;
 }
@@ -1223,12 +1224,14 @@
 	return 0;
 }
 
-int git_odb__error_notfound(const char *message, const git_oid *oid)
+int git_odb__error_notfound(
+	const char *message, const git_oid *oid, size_t oid_len)
 {
 	if (oid != NULL) {
 		char oid_str[GIT_OID_HEXSZ + 1];
-		git_oid_tostr(oid_str, sizeof(oid_str), oid);
-		giterr_set(GITERR_ODB, "Object not found - %s (%s)", message, oid_str);
+		git_oid_tostr(oid_str, oid_len, oid);
+		giterr_set(GITERR_ODB, "Object not found - %s (%.*s)",
+			message, oid_len, oid_str);
 	} else
 		giterr_set(GITERR_ODB, "Object not found - %s", message);
 
diff --git a/src/odb.h b/src/odb.h
index 281bd3a..31a9fd1 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -82,7 +82,8 @@
 /*
  * Generate a GIT_ENOTFOUND error for the ODB.
  */
-int git_odb__error_notfound(const char *message, const git_oid *oid);
+int git_odb__error_notfound(
+	const char *message, const git_oid *oid, size_t oid_len);
 
 /*
  * Generate a GIT_EAMBIGUOUS error for the ODB.
diff --git a/src/odb_loose.c b/src/odb_loose.c
index 730c4b1..9d9bffd 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -547,7 +547,8 @@
 
 	/* Check that directory exists */
 	if (git_path_isdir(object_location->ptr) == false)
-		return git_odb__error_notfound("no matching loose object for prefix", short_oid);
+		return git_odb__error_notfound("no matching loose object for prefix",
+			short_oid, len);
 
 	state.dir_len = git_buf_len(object_location);
 	state.short_oid_len = len;
@@ -560,7 +561,8 @@
 		return error;
 
 	if (!state.found)
-		return git_odb__error_notfound("no matching loose object for prefix", short_oid);
+		return git_odb__error_notfound("no matching loose object for prefix",
+			short_oid, len);
 
 	if (state.found > 1)
 		return git_odb__error_ambiguous("multiple matches in loose objects");
@@ -613,9 +615,10 @@
 	raw.len = 0;
 	raw.type = GIT_OBJ_BAD;
 
-	if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
-		error = git_odb__error_notfound("no matching loose object", oid);
-	else if ((error = read_header_loose(&raw, &object_path)) == 0) {
+	if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
+		error = git_odb__error_notfound("no matching loose object",
+			oid, GIT_OID_HEXSZ);
+	} else if ((error = read_header_loose(&raw, &object_path)) == 0) {
 		*len_p = raw.len;
 		*type_p = raw.type;
 	}
@@ -633,9 +636,10 @@
 
 	assert(backend && oid);
 
-	if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
-		error = git_odb__error_notfound("no matching loose object", oid);
-	else if ((error = read_loose(&raw, &object_path)) == 0) {
+	if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
+		error = git_odb__error_notfound("no matching loose object",
+			oid, GIT_OID_HEXSZ);
+	} else if ((error = read_loose(&raw, &object_path)) == 0) {
 		*buffer_p = raw.data;
 		*len_p = raw.len;
 		*type_p = raw.type;
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 77d2c75..5a57864 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -264,7 +264,8 @@
 	if (!pack_entry_find_inner(e, backend, oid, last_found))
 		return 0;
 
-	return git_odb__error_notfound("failed to find pack entry", oid);
+	return git_odb__error_notfound(
+		"failed to find pack entry", oid, GIT_OID_HEXSZ);
 }
 
 static int pack_entry_find_prefix(
@@ -309,7 +310,8 @@
 	}
 
 	if (!found)
-		return git_odb__error_notfound("no matching pack entry for prefix", short_oid);
+		return git_odb__error_notfound("no matching pack entry for prefix",
+			short_oid, len);
 	else
 		return 0;
 }
@@ -333,7 +335,7 @@
 		return 0;
 
 	if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
-		return git_odb__error_notfound("failed to refresh packfiles", NULL);
+		return git_odb__error_notfound("failed to refresh packfiles", NULL, 0);
 
 	git_buf_sets(&path, backend->pack_folder);
 
diff --git a/src/oid.h b/src/oid.h
index aa1f0bf..922a2a3 100644
--- a/src/oid.h
+++ b/src/oid.h
@@ -44,4 +44,13 @@
 	return git_oid__hashcmp(a->id, b->id);
 }
 
+GIT_INLINE(void) git_oid__cpy_prefix(
+	git_oid *out, const git_oid *id, size_t len)
+{
+	memcpy(&out->id, id->id, (len + 1) / 2);
+
+	if (len & 1)
+		out->id[len / 2] &= 0xF0;
+}
+
 #endif
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
index 97736b7..a65f558 100644
--- a/src/openssl_stream.c
+++ b/src/openssl_stream.c
@@ -34,6 +34,8 @@
 
 SSL_CTX *git__ssl_ctx;
 
+#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
+
 #ifdef GIT_THREADS
 
 static git_mutex *openssl_locks;
@@ -85,6 +87,7 @@
 {
 #ifdef GIT_OPENSSL
 	long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+	const char *ciphers = git_libgit2__ssl_ciphers();
 
 	/* Older OpenSSL and MacOS OpenSSL doesn't have this */
 #ifdef SSL_OP_NO_COMPRESSION
@@ -108,6 +111,16 @@
 		git__ssl_ctx = NULL;
 		return -1;
 	}
+
+	if (!ciphers) {
+		ciphers = GIT_SSL_DEFAULT_CIPHERS;
+	}
+
+	if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) {
+		SSL_CTX_free(git__ssl_ctx);
+		git__ssl_ctx = NULL;
+		return -1;
+	}
 #endif
 
 	git__on_shutdown(shutdown_ssl);
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 46fe8f3..11e13f7 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -848,8 +848,10 @@
 
 		git_packbuilder__cache_unlock(pb);
 
-		if (overflow)
+		if (overflow) {
+			git__free(delta_buf);
 			return -1;
+		}
 
 		trg_object->delta_data = git__realloc(delta_buf, delta_size);
 		GITERR_CHECK_ALLOC(trg_object->delta_data);
diff --git a/src/pack.c b/src/pack.c
index 52c6521..e7003e6 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -494,7 +494,6 @@
 	int error;
 
 	error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
-	git_mwindow_close(&w_curs);
 	if (error < 0)
 		return error;
 
@@ -517,7 +516,6 @@
 	while (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) {
 		curpos = base_offset;
 		error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
-		git_mwindow_close(&w_curs);
 		if (error < 0)
 			return error;
 		if (type != GIT_OBJ_OFS_DELTA && type != GIT_OBJ_REF_DELTA)
@@ -585,7 +583,6 @@
 		elem->base_key = obj_offset;
 
 		error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
-		git_mwindow_close(&w_curs);
 
 		if (error < 0)
 			goto on_error;
@@ -1018,7 +1015,7 @@
 	unsigned char *idx_sha1;
 
 	if (p->index_version == -1 && pack_index_open(p) < 0)
-		return git_odb__error_notfound("failed to open packfile", NULL);
+		return git_odb__error_notfound("failed to open packfile", NULL, 0);
 
 	/* if mwf opened by another thread, return now */
 	if (git_mutex_lock(&p->lock) < 0)
@@ -1099,7 +1096,7 @@
 	path_len = strlen(path);
 
 	if (path_len < strlen(".idx"))
-		return git_odb__error_notfound("invalid packfile path", NULL);
+		return git_odb__error_notfound("invalid packfile path", NULL, 0);
 
 	if (git_buf_printf(&buf, "%.*s.pack", (int)(path_len - strlen(".idx")), path) < 0)
 		return -1;
@@ -1117,7 +1114,7 @@
 	*pack_out = NULL;
 
 	if (path_len < strlen(".idx"))
-		return git_odb__error_notfound("invalid packfile path", NULL);
+		return git_odb__error_notfound("invalid packfile path", NULL, 0);
 
 	GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*p), path_len);
 	GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
@@ -1143,7 +1140,7 @@
 
 	if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) {
 		git__free(p);
-		return git_odb__error_notfound("packfile not found", NULL);
+		return git_odb__error_notfound("packfile not found", NULL, 0);
 	}
 
 	/* ok, it looks sane as far as we can check without
@@ -1344,7 +1341,7 @@
 	}
 
 	if (!found)
-		return git_odb__error_notfound("failed to find offset for pack entry", short_oid);
+		return git_odb__error_notfound("failed to find offset for pack entry", short_oid, len);
 	if (found > 1)
 		return git_odb__error_ambiguous("found multiple offsets for pack entry");
 
diff --git a/src/posix.c b/src/posix.c
index c7201ba..b3f1a1c 100644
--- a/src/posix.c
+++ b/src/posix.c
@@ -224,6 +224,13 @@
 	return 0;
 }
 
+int git__mmap_alignment(size_t *alignment)
+{
+	/* dummy; here we don't need any alignment anyway */
+	*alignment = 4096;
+	return 0;
+}
+
 
 int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
 {
diff --git a/src/posix.h b/src/posix.h
index 8785a4c..f204751 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -109,6 +109,7 @@
 extern int p_rename(const char *from, const char *to);
 
 extern int git__page_size(size_t *page_size);
+extern int git__mmap_alignment(size_t *page_size);
 
 /**
  * Platform-dependent methods
diff --git a/src/push.c b/src/push.c
index 3c9fa2f..0747259 100644
--- a/src/push.c
+++ b/src/push.c
@@ -374,9 +374,9 @@
 		case GIT_OBJ_COMMIT:
 			return 0;
 		case GIT_OBJ_TREE:
-			return git_packbuilder_insert_tree(pb, &entry->oid);
+			return git_packbuilder_insert_tree(pb, entry->oid);
 		default:
-			return git_packbuilder_insert(pb, &entry->oid, entry->filename);
+			return git_packbuilder_insert(pb, entry->oid, entry->filename);
 	}
 }
 
@@ -396,7 +396,7 @@
 		const git_tree_entry *d_entry = git_tree_entry_byindex(delta, j);
 		int cmp = 0;
 
-		if (!git_oid__cmp(&b_entry->oid, &d_entry->oid))
+		if (!git_oid__cmp(b_entry->oid, d_entry->oid))
 			goto loop;
 
 		cmp = strcmp(b_entry->filename, d_entry->filename);
@@ -407,15 +407,15 @@
 			git_tree_entry__is_tree(b_entry) &&
 			git_tree_entry__is_tree(d_entry)) {
 			/* Add the right-hand entry */
-			if ((error = git_packbuilder_insert(pb, &d_entry->oid,
+			if ((error = git_packbuilder_insert(pb, d_entry->oid,
 				d_entry->filename)) < 0)
 				goto on_error;
 
 			/* Acquire the subtrees and recurse */
 			if ((error = git_tree_lookup(&b_child,
-					git_tree_owner(base), &b_entry->oid)) < 0 ||
+					git_tree_owner(base), b_entry->oid)) < 0 ||
 				(error = git_tree_lookup(&d_child,
-					git_tree_owner(delta), &d_entry->oid)) < 0 ||
+					git_tree_owner(delta), d_entry->oid)) < 0 ||
 				(error = queue_differences(b_child, d_child, pb)) < 0)
 				goto on_error;
 
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index f6ed720..f978038 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -962,6 +962,7 @@
 
 	for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) {
 		struct packref *ref = git_sortedcache_entry(refcache, i);
+		assert(ref);
 
 		if (packed_find_peel(backend, ref) < 0)
 			goto fail;
diff --git a/src/refs.c b/src/refs.c
index a15e31b..26c8002 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -289,6 +289,9 @@
 			"Could not use '%s' as valid reference name", git_buf_cstr(&name));
 	}
 
+	if (error == GIT_ENOTFOUND)
+		giterr_set(GITERR_REFERENCE, "no reference found for shorthand '%s'", refname);
+
 	git_buf_free(&name);
 	git_buf_free(&refnamebuf);
 	return error;
diff --git a/src/settings.c b/src/settings.c
index 88602ba..0da19ea 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -71,12 +71,18 @@
 }
 
 extern char *git__user_agent;
+extern char *git__ssl_ciphers;
 
 const char *git_libgit2__user_agent()
 {
 	return git__user_agent;
 }
 
+const char *git_libgit2__ssl_ciphers()
+{
+	return git__ssl_ciphers;
+}
+
 int git_libgit2_opts(int key, ...)
 {
 	int error = 0;
@@ -169,7 +175,7 @@
 			}
 		}
 #else
-		giterr_set(GITERR_NET, "Cannot set certificate locations: OpenSSL is not enabled");
+		giterr_set(GITERR_NET, "cannot set certificate locations: OpenSSL is not enabled");
 		error = -1;
 #endif
 		break;
@@ -187,6 +193,22 @@
 		git_object__strict_input_validation = (va_arg(ap, int) != 0);
 		break;
 
+	case GIT_OPT_SET_SSL_CIPHERS:
+#ifdef GIT_OPENSSL
+		{
+			git__free(git__ssl_ciphers);
+			git__ssl_ciphers = git__strdup(va_arg(ap, const char *));
+			if (!git__ssl_ciphers) {
+				giterr_set_oom();
+				error = -1;
+			}
+		}
+#else
+		giterr_set(GITERR_NET, "cannot set custom ciphers: OpenSSL is not enabled");
+		error = -1;
+#endif
+		break;
+
 	default:
 		giterr_set(GITERR_INVALID, "invalid option key");
 		error = -1;
diff --git a/src/submodule.c b/src/submodule.c
index 38db415..c903cf9 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -80,7 +80,8 @@
 	if (blen > 0 && b[blen - 1] == '/')
 		blen--;
 
-	return (alen == blen && strncmp(a, b, alen) == 0);
+	return (alen == 0 && blen == 0) ||
+		(alen == blen && strncmp(a, b, alen) == 0);
 }
 
 __KHASH_IMPL(
@@ -1416,7 +1417,7 @@
 		git_tree_entry_bypath(&te, head, submodule->path) < 0)
 		giterr_clear();
 	else
-		submodule_update_from_head_data(submodule, te->attr, &te->oid);
+		submodule_update_from_head_data(submodule, te->attr, git_tree_entry_id(te));
 
 	git_tree_entry_free(te);
 	git_tree_free(head);
diff --git a/src/transport.c b/src/transport.c
index 5c65c7c..327052f 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -35,6 +35,8 @@
 	{ "file://",  git_transport_local, NULL },
 #ifdef GIT_SSH
 	{ "ssh://",   git_transport_smart, &ssh_subtransport_definition },
+	{ "ssh+git://",   git_transport_smart, &ssh_subtransport_definition },
+	{ "git+ssh://",   git_transport_smart, &ssh_subtransport_definition },
 #endif
 	{ NULL, 0, 0 }
 };
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 6363378..02e1ecf 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -721,18 +721,39 @@
 	return 0;
 }
 
-static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt)
+static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, git_buf *data_pkt_buf)
 {
 	git_pkt *pkt;
-	const char *line = data_pkt->data, *line_end;
-	size_t line_len = data_pkt->len;
+	const char *line, *line_end;
+	size_t line_len;
 	int error;
+	int reading_from_buf = data_pkt_buf->size > 0;
+
+	if (reading_from_buf) {
+		/* We had an existing partial packet, so add the new
+		 * packet to the buffer and parse the whole thing */
+		git_buf_put(data_pkt_buf, data_pkt->data, data_pkt->len);
+		line = data_pkt_buf->ptr;
+		line_len = data_pkt_buf->size;
+	}
+	else {
+		line = data_pkt->data;
+		line_len = data_pkt->len;
+	}
 
 	while (line_len > 0) {
 		error = git_pkt_parse_line(&pkt, line, &line_end, line_len);
 
-		if (error < 0)
-			return error;
+		if (error == GIT_EBUFS) {
+			/* Buffer the data when the inner packet is split
+			 * across multiple sideband packets */
+			if (!reading_from_buf)
+				git_buf_put(data_pkt_buf, line, line_len);
+			error = 0;
+			goto done;
+		}
+		else if (error < 0)
+			goto done;
 
 		/* Advance in the buffer */
 		line_len -= (line_end - line);
@@ -743,10 +764,15 @@
 		git_pkt_free(pkt);
 
 		if (error < 0 && error != GIT_ITEROVER)
-			return error;
+			goto done;
 	}
 
-	return 0;
+	error = 0;
+
+done:
+	if (reading_from_buf)
+		git_buf_consume(data_pkt_buf, line_end);
+	return error;
 }
 
 static int parse_report(transport_smart *transport, git_push *push)
@@ -755,6 +781,7 @@
 	const char *line_end = NULL;
 	gitno_buffer *buf = &transport->buffer;
 	int error, recvd;
+	git_buf data_pkt_buf = GIT_BUF_INIT;
 
 	for (;;) {
 		if (buf->offset > 0)
@@ -763,16 +790,21 @@
 		else
 			error = GIT_EBUFS;
 
-		if (error < 0 && error != GIT_EBUFS)
-			return -1;
+		if (error < 0 && error != GIT_EBUFS) {
+			error = -1;
+			goto done;
+		}
 
 		if (error == GIT_EBUFS) {
-			if ((recvd = gitno_recv(buf)) < 0)
-				return recvd;
+			if ((recvd = gitno_recv(buf)) < 0) {
+				error = recvd;
+				goto done;
+			}
 
 			if (recvd == 0) {
 				giterr_set(GITERR_NET, "early EOF");
-				return GIT_EEOF;
+				error = GIT_EEOF;
+				goto done;
 			}
 			continue;
 		}
@@ -784,7 +816,7 @@
 		switch (pkt->type) {
 			case GIT_PKT_DATA:
 				/* This is a sideband packet which contains other packets */
-				error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt);
+				error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt, &data_pkt_buf);
 				break;
 			case GIT_PKT_ERR:
 				giterr_set(GITERR_NET, "report-status: Error reported: %s",
@@ -805,12 +837,24 @@
 		git_pkt_free(pkt);
 
 		/* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
-		if (error == GIT_ITEROVER)
-			return 0;
+		if (error == GIT_ITEROVER) {
+			error = 0;
+			if (data_pkt_buf.size > 0) {
+				/* If there was data remaining in the pack data buffer,
+				 * then the server sent a partial pkt-line */
+				giterr_set(GITERR_NET, "Incomplete pack data pkt-line");
+				error = GIT_ERROR;
+			}
+			goto done;
+		}
 
-		if (error < 0)
-			return error;
+		if (error < 0) {
+			goto done;
+		}
 	}
+done:
+	git_buf_free(&data_pkt_buf);
+	return error;
 }
 
 static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec)
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 35739ab..cfd5736 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -21,7 +21,8 @@
 
 #define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
 
-static const char prefix_ssh[] = "ssh://";
+static const char *ssh_prefixes[] = { "ssh://", "ssh+git://", "git+ssh://" };
+
 static const char cmd_uploadpack[] = "git-upload-pack";
 static const char cmd_receivepack[] = "git-receive-pack";
 
@@ -63,17 +64,24 @@
 {
 	char *repo;
 	int len;
+	size_t i;
 
-	if (!git__prefixcmp(url, prefix_ssh)) {
-		url = url + strlen(prefix_ssh);
-		repo = strchr(url, '/');
-		if (repo && repo[1] == '~')
-			++repo;
-	} else {
-		repo = strchr(url, ':');
-		if (repo) repo++;
+	for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) {
+		const char *p = ssh_prefixes[i];
+
+		if (!git__prefixcmp(url, p)) {
+			url = url + strlen(p);
+			repo = strchr(url, '/');
+			if (repo && repo[1] == '~')
+				++repo;
+
+			goto done;
+		}
 	}
+	repo = strchr(url, ':');
+	if (repo) repo++;
 
+done:
 	if (!repo) {
 		giterr_set(GITERR_NET, "Malformed git protocol URL");
 		return -1;
@@ -500,6 +508,7 @@
 	char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
 	const char *default_port="22";
 	int auth_methods, error = 0;
+	size_t i;
 	ssh_stream *s;
 	git_cred *cred = NULL;
 	LIBSSH2_SESSION* session=NULL;
@@ -515,16 +524,22 @@
 	s->session = NULL;
 	s->channel = NULL;
 
-	if (!git__prefixcmp(url, prefix_ssh)) {
-		if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port)) < 0)
-			goto done;
-	} else {
-		if ((error = git_ssh_extract_url_parts(&host, &user, url)) < 0)
-			goto done;
-		port = git__strdup(default_port);
-		GITERR_CHECK_ALLOC(port);
-	}
+	for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) {
+		const char *p = ssh_prefixes[i];
 
+		if (!git__prefixcmp(url, p)) {
+			if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port)) < 0)
+				goto done;
+
+			goto post_extract;
+		}
+	}
+	if ((error = git_ssh_extract_url_parts(&host, &user, url)) < 0)
+		goto done;
+	port = git__strdup(default_port);
+	GITERR_CHECK_ALLOC(port);
+
+post_extract:
 	if ((error = git_socket_stream_new(&s->io, host, port)) < 0 ||
 	    (error = git_stream_connect(s->io)) < 0)
 		goto done;
diff --git a/src/tree.c b/src/tree.c
index 48b9f12..6ce460c 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -85,9 +85,10 @@
 }
 
 /**
- * Allocate either from the pool or from the system allocator
+ * Allocate a new self-contained entry, with enough space after it to
+ * store the filename and the id.
  */
-static git_tree_entry *alloc_entry_base(git_pool *pool, const char *filename, size_t filename_len)
+static git_tree_entry *alloc_entry(const char *filename, size_t filename_len, const git_oid *id)
 {
 	git_tree_entry *entry = NULL;
 	size_t tree_len;
@@ -95,44 +96,32 @@
 	TREE_ENTRY_CHECK_NAMELEN(filename_len);
 
 	if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) ||
-	    GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1))
+	    GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1) ||
+	    GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, GIT_OID_RAWSZ))
 		return NULL;
 
-	entry = pool ? git_pool_malloc(pool, tree_len) :
-		       git__malloc(tree_len);
+	entry = git__calloc(1, tree_len);
 	if (!entry)
 		return NULL;
 
-	memset(entry, 0x0, sizeof(git_tree_entry));
-	memcpy(entry->filename, filename, filename_len);
-	entry->filename[filename_len] = 0;
+	{
+		char *filename_ptr;
+		void *id_ptr;
+
+		filename_ptr = ((char *) entry) + sizeof(git_tree_entry);
+		memcpy(filename_ptr, filename, filename_len);
+		entry->filename = filename_ptr;
+
+		id_ptr = filename_ptr + filename_len + 1;
+		git_oid_cpy(id_ptr, id);
+		entry->oid = id_ptr;
+	}
+
 	entry->filename_len = (uint16_t)filename_len;
 
 	return entry;
 }
 
-/**
- * Allocate a tree entry, using the poolin the tree which owns
- * it. This is useful when reading trees, so we don't allocate a ton
- * of small strings but can use the pool.
- */
-static git_tree_entry *alloc_entry_pooled(git_pool *pool, const char *filename, size_t filename_len)
-{
-	git_tree_entry *entry = NULL;
-
-	if (!(entry = alloc_entry_base(pool, filename, filename_len)))
-		return NULL;
-
-	entry->pooled = true;
-
-	return entry;
-}
-
-static git_tree_entry *alloc_entry(const char *filename)
-{
-	return alloc_entry_base(NULL, filename, strlen(filename));
-}
-
 struct tree_key_search {
 	const char *filename;
 	uint16_t filename_len;
@@ -174,7 +163,10 @@
  * around the area for our target file.
  */
 static int tree_key_search(
-	size_t *at_pos, git_vector *entries, const char *filename, size_t filename_len)
+	size_t *at_pos,
+	const git_tree *tree,
+	const char *filename,
+	size_t filename_len)
 {
 	struct tree_key_search ksearch;
 	const git_tree_entry *entry;
@@ -187,13 +179,15 @@
 
 	/* Initial homing search; find an entry on the tree with
 	 * the same prefix as the filename we're looking for */
-	if (git_vector_bsearch2(&homing, entries, &homing_search_cmp, &ksearch) < 0)
+
+	if (git_array_search(&homing,
+		tree->entries, &homing_search_cmp, &ksearch) < 0)
 		return GIT_ENOTFOUND; /* just a signal error; not passed back to user */
 
 	/* We found a common prefix. Look forward as long as
 	 * there are entries that share the common prefix */
-	for (i = homing; i < entries->length; ++i) {
-		entry = entries->contents[i];
+	for (i = homing; i < tree->entries.size; ++i) {
+		entry = git_array_get(tree->entries, i);
 
 		if (homing_search_cmp(&ksearch, entry) < 0)
 			break;
@@ -213,7 +207,7 @@
 		i = homing - 1;
 
 		do {
-			entry = entries->contents[i];
+			entry = git_array_get(tree->entries, i);
 
 			if (homing_search_cmp(&ksearch, entry) > 0)
 				break;
@@ -234,7 +228,7 @@
 
 void git_tree_entry_free(git_tree_entry *entry)
 {
-	if (entry == NULL || entry->pooled)
+	if (entry == NULL)
 		return;
 
 	git__free(entry);
@@ -242,36 +236,26 @@
 
 int git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source)
 {
-	size_t total_size;
-	git_tree_entry *copy;
+	git_tree_entry *cpy;
 
 	assert(source);
 
-	GITERR_CHECK_ALLOC_ADD(&total_size, sizeof(git_tree_entry), source->filename_len);
-	GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
+	cpy = alloc_entry(source->filename, source->filename_len, source->oid);
+	if (cpy == NULL)
+		return -1;
 
-	copy = git__malloc(total_size);
-	GITERR_CHECK_ALLOC(copy);
+	cpy->attr = source->attr;
 
-	memcpy(copy, source, total_size);
-
-	copy->pooled = 0;
-
-	*dest = copy;
+	*dest = cpy;
 	return 0;
 }
 
 void git_tree__free(void *_tree)
 {
 	git_tree *tree = _tree;
-	size_t i;
-	git_tree_entry *e;
 
-	git_vector_foreach(&tree->entries, i, e)
-		git_tree_entry_free(e);
-
-	git_vector_free(&tree->entries);
-	git_pool_clear(&tree->pool);
+	git_odb_object_free(tree->odb_obj);
+	git_array_clear(tree->entries);
 	git__free(tree);
 }
 
@@ -294,7 +278,7 @@
 const git_oid *git_tree_entry_id(const git_tree_entry *entry)
 {
 	assert(entry);
-	return &entry->oid;
+	return entry->oid;
 }
 
 git_otype git_tree_entry_type(const git_tree_entry *entry)
@@ -315,7 +299,7 @@
 	const git_tree_entry *entry)
 {
 	assert(entry && object_out);
-	return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY);
+	return git_object_lookup(object_out, repo, entry->oid, GIT_OBJ_ANY);
 }
 
 static const git_tree_entry *entry_fromname(
@@ -323,13 +307,10 @@
 {
 	size_t idx;
 
-	/* be safe when we cast away constness - i.e. don't trigger a sort */
-	assert(git_vector_is_sorted(&tree->entries));
-
-	if (tree_key_search(&idx, (git_vector *)&tree->entries, name, name_len) < 0)
+	if (tree_key_search(&idx, tree, name, name_len) < 0)
 		return NULL;
 
-	return git_vector_get(&tree->entries, idx);
+	return git_array_get(tree->entries, idx);
 }
 
 const git_tree_entry *git_tree_entry_byname(
@@ -344,7 +325,7 @@
 	const git_tree *tree, size_t idx)
 {
 	assert(tree);
-	return git_vector_get(&tree->entries, idx);
+	return git_array_get(tree->entries, idx);
 }
 
 const git_tree_entry *git_tree_entry_byid(
@@ -355,8 +336,8 @@
 
 	assert(tree);
 
-	git_vector_foreach(&tree->entries, i, e) {
-		if (memcmp(&e->oid.id, &id->id, sizeof(id->id)) == 0)
+	git_array_foreach(tree->entries, i, e) {
+		if (memcmp(&e->oid->id, &id->id, sizeof(id->id)) == 0)
 			return e;
 	}
 
@@ -365,7 +346,6 @@
 
 int git_tree__prefix_position(const git_tree *tree, const char *path)
 {
-	const git_vector *entries = &tree->entries;
 	struct tree_key_search ksearch;
 	size_t at_pos, path_len;
 
@@ -378,21 +358,20 @@
 	ksearch.filename = path;
 	ksearch.filename_len = (uint16_t)path_len;
 
-	/* be safe when we cast away constness - i.e. don't trigger a sort */
-	assert(git_vector_is_sorted(&tree->entries));
-
 	/* Find tree entry with appropriate prefix */
-	git_vector_bsearch2(
-		&at_pos, (git_vector *)entries, &homing_search_cmp, &ksearch);
+	git_array_search(
+		&at_pos, tree->entries, &homing_search_cmp, &ksearch);
 
-	for (; at_pos < entries->length; ++at_pos) {
-		const git_tree_entry *entry = entries->contents[at_pos];
+	for (; at_pos < tree->entries.size; ++at_pos) {
+		const git_tree_entry *entry = git_array_get(tree->entries, at_pos);
 		if (homing_search_cmp(&ksearch, entry) < 0)
 			break;
 	}
 
 	for (; at_pos > 0; --at_pos) {
-		const git_tree_entry *entry = entries->contents[at_pos - 1];
+		const git_tree_entry *entry =
+			git_array_get(tree->entries, at_pos - 1);
+
 		if (homing_search_cmp(&ksearch, entry) > 0)
 			break;
 	}
@@ -403,7 +382,7 @@
 size_t git_tree_entrycount(const git_tree *tree)
 {
 	assert(tree);
-	return tree->entries.length;
+	return tree->entries.size;
 }
 
 unsigned int git_treebuilder_entrycount(git_treebuilder *bld)
@@ -444,13 +423,18 @@
 int git_tree__parse(void *_tree, git_odb_object *odb_obj)
 {
 	git_tree *tree = _tree;
-	const char *buffer = git_odb_object_data(odb_obj);
-	const char *buffer_end = buffer + git_odb_object_size(odb_obj);
+	const char *buffer;
+	const char *buffer_end;
 
-	git_pool_init(&tree->pool, 1);
-	if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0)
+	if (git_odb_object_dup(&tree->odb_obj, odb_obj) < 0)
 		return -1;
 
+	buffer = git_odb_object_data(tree->odb_obj);
+	buffer_end = buffer + git_odb_object_size(tree->odb_obj);
+
+	git_array_init_to_size(tree->entries, DEFAULT_TREE_SIZE);
+	GITERR_CHECK_ARRAY(tree->entries);
+
 	while (buffer < buffer_end) {
 		git_tree_entry *entry;
 		size_t filename_len;
@@ -464,27 +448,21 @@
 			return tree_error("Failed to parse tree. Object is corrupted", NULL);
 
 		filename_len = nul - buffer;
-		/** Allocate the entry and store it in the entries vector */
+		/* Allocate the entry */
 		{
-			entry = alloc_entry_pooled(&tree->pool, buffer, filename_len);
+			entry = git_array_alloc(tree->entries);
 			GITERR_CHECK_ALLOC(entry);
 
-			if (git_vector_insert(&tree->entries, entry) < 0)
-				return -1;
-
 			entry->attr = attr;
+			entry->filename_len = filename_len;
+			entry->filename = buffer;
+			entry->oid = (git_oid *) ((char *) buffer + filename_len + 1);
 		}
 
-		/* Advance to the ID just after the path */
 		buffer += filename_len + 1;
-
-		git_oid_fromraw(&entry->oid, (const unsigned char *)buffer);
 		buffer += GIT_OID_RAWSZ;
 	}
 
-	/* The tree is sorted by definition. Bad inputs give bad outputs */
-	tree->entries.flags |= GIT_VECTOR_SORTED;
-
 	return 0;
 }
 
@@ -517,10 +495,9 @@
 	if (!valid_entry_name(bld->repo, filename))
 		return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
 
-	entry = alloc_entry(filename);
+	entry = alloc_entry(filename, strlen(filename), id);
 	GITERR_CHECK_ALLOC(entry);
 
-	git_oid_cpy(&entry->oid, id);
 	entry->attr = (uint16_t)filemode;
 
 	git_strmap_insert(bld->map, entry->filename, entry, error);
@@ -709,10 +686,10 @@
 	if (source != NULL) {
 		git_tree_entry *entry_src;
 
-		git_vector_foreach(&source->entries, i, entry_src) {
+		git_array_foreach(source->entries, i, entry_src) {
 			if (append_entry(
 				bld, entry_src->filename,
-				&entry_src->oid,
+				entry_src->oid,
 				entry_src->attr) < 0)
 				goto on_error;
 		}
@@ -764,8 +741,9 @@
 	pos = git_strmap_lookup_index(bld->map, filename);
 	if (git_strmap_valid_index(bld->map, pos)) {
 		entry = git_strmap_value_at(bld->map, pos);
+		git_oid_cpy((git_oid *) entry->oid, id);
 	} else {
-		entry = alloc_entry(filename);
+		entry = alloc_entry(filename, strlen(filename), id);
 		GITERR_CHECK_ALLOC(entry);
 
 		git_strmap_insert(bld->map, entry->filename, entry, error);
@@ -777,7 +755,6 @@
 		}
 	}
 
-	git_oid_cpy(&entry->oid, id);
 	entry->attr = filemode;
 
 	if (entry_out)
@@ -848,19 +825,20 @@
 
 		git_buf_printf(&tree, "%o ", entry->attr);
 		git_buf_put(&tree, entry->filename, entry->filename_len + 1);
-		git_buf_put(&tree, (char *)entry->oid.id, GIT_OID_RAWSZ);
+		git_buf_put(&tree, (char *)entry->oid->id, GIT_OID_RAWSZ);
 
 		if (git_buf_oom(&tree))
 			error = -1;
 	}
 
-	git_vector_free(&entries);
 
 	if (!error &&
 		!(error = git_repository_odb__weakptr(&odb, bld->repo)))
 		error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
 
 	git_buf_free(&tree);
+	git_vector_free(&entries);
+
 	return error;
 }
 
@@ -960,7 +938,7 @@
 		return git_tree_entry_dup(entry_out, entry);
 	}
 
-	if (git_tree_lookup(&subtree, root->object.repo, &entry->oid) < 0)
+	if (git_tree_lookup(&subtree, root->object.repo, entry->oid) < 0)
 		return -1;
 
 	error = git_tree_entry_bypath(
@@ -984,7 +962,7 @@
 	size_t i;
 	const git_tree_entry *entry;
 
-	git_vector_foreach(&tree->entries, i, entry) {
+	git_array_foreach(tree->entries, i, entry) {
 		if (preorder) {
 			error = callback(path->ptr, entry, payload);
 			if (error < 0) { /* negative value stops iteration */
@@ -1001,7 +979,7 @@
 			git_tree *subtree;
 			size_t path_len = git_buf_len(path);
 
-			error = git_tree_lookup(&subtree, tree->object.repo, &entry->oid);
+			error = git_tree_lookup(&subtree, tree->object.repo, entry->oid);
 			if (error < 0)
 				break;
 
diff --git a/src/tree.h b/src/tree.h
index 914d788..5e7a66e 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -17,15 +17,14 @@
 struct git_tree_entry {
 	uint16_t attr;
 	uint16_t filename_len;
-	git_oid oid;
-	bool pooled;
-	char filename[GIT_FLEX_ARRAY];
+	const git_oid *oid;
+	const char *filename;
 };
 
 struct git_tree {
 	git_object object;
-	git_vector entries;
-	git_pool pool;
+	git_odb_object *odb_obj;
+	git_array_t(git_tree_entry) entries;
 };
 
 struct git_treebuilder {
diff --git a/src/unix/map.c b/src/unix/map.c
index 72abb34..c55ad1a 100644
--- a/src/unix/map.c
+++ b/src/unix/map.c
@@ -24,6 +24,11 @@
 	return 0;
 }
 
+int git__mmap_alignment(size_t *alignment)
+{
+  return git__page_size(alignment);
+}
+
 int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
 {
 	int mprot = PROT_READ;
diff --git a/src/win32/map.c b/src/win32/map.c
index a99c30f..03a3646 100644
--- a/src/win32/map.c
+++ b/src/win32/map.c
@@ -17,22 +17,41 @@
 
 	if (!page_size) {
 		GetSystemInfo(&sys);
-		page_size = sys.dwAllocationGranularity;
+		page_size = sys.dwPageSize;
 	}
 
 	return page_size;
 }
 
+static DWORD get_allocation_granularity(void)
+{
+	static DWORD granularity;
+	SYSTEM_INFO sys;
+
+	if (!granularity) {
+		GetSystemInfo(&sys);
+		granularity = sys.dwAllocationGranularity;
+	}
+
+	return granularity;
+}
+
 int git__page_size(size_t *page_size)
 {
 	*page_size = get_page_size();
 	return 0;
 }
 
+int git__mmap_alignment(size_t *page_size)
+{
+	*page_size = get_allocation_granularity();
+	return 0;
+}
+
 int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
 {
 	HANDLE fh = (HANDLE)_get_osfhandle(fd);
-	DWORD page_size = get_page_size();
+	DWORD alignment = get_allocation_granularity();
 	DWORD fmap_prot = 0;
 	DWORD view_prot = 0;
 	DWORD off_low = 0;
@@ -62,12 +81,12 @@
 	if (prot & GIT_PROT_READ)
 		view_prot |= FILE_MAP_READ;
 
-	page_start = (offset / page_size) * page_size;
+	page_start = (offset / alignment) * alignment;
 	page_offset = offset - page_start;
 
-	if (page_offset != 0) { /* offset must be multiple of page size */
+	if (page_offset != 0) { /* offset must be multiple of the allocation granularity */
 		errno = EINVAL;
-		giterr_set(GITERR_OS, "Failed to mmap. Offset must be multiple of page size");
+		giterr_set(GITERR_OS, "Failed to mmap. Offset must be multiple of allocation granularity");
 		return -1;
 	}
 
diff --git a/src/xdiff/xprepare.c b/src/xdiff/xprepare.c
index 63a22c6..13b55ab 100644
--- a/src/xdiff/xprepare.c
+++ b/src/xdiff/xprepare.c
@@ -301,10 +301,11 @@
 
 		xdl_free_ctx(&xe->xdf2);
 		xdl_free_ctx(&xe->xdf1);
+		xdl_free_classifier(&cf);
 		return -1;
 	}
 
-	if (!(xpp->flags & XDF_HISTOGRAM_DIFF))
+	if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)
 		xdl_free_classifier(&cf);
 
 	return 0;
diff --git a/tests/attr/ignore.c b/tests/attr/ignore.c
index 27fed25..91bf984 100644
--- a/tests/attr/ignore.c
+++ b/tests/attr/ignore.c
@@ -252,3 +252,16 @@
 	if (cl_repo_get_bool(g_repo, "core.ignorecase"))
 		assert_is_ignored(false, "dir/TeSt");
 }
+
+void test_attr_ignore__symlink_to_outside(void)
+{
+#ifdef GIT_WIN32
+	cl_skip();
+#endif
+
+	cl_git_rewritefile("attr/.gitignore", "symlink\n");
+	cl_git_mkfile("target", "target");
+	cl_git_pass(p_symlink("../target", "attr/symlink"));
+	assert_is_ignored(true, "symlink");
+	assert_is_ignored(true, "lala/../symlink");
+}
diff --git a/tests/commit/parse.c b/tests/commit/parse.c
index 838cfb4..297fccc 100644
--- a/tests/commit/parse.c
+++ b/tests/commit/parse.c
@@ -498,6 +498,21 @@
 \n\
 a simple commit which works\n";
 
+	const char *oneline_signature = "tree 51832e6397b30309c8bcad9c55fa6ae67778f378\n\
+parent a1b6decaaac768b5e01e1b5dbf5b2cc081bed1eb\n\
+author Some User <someuser@gmail.com> 1454537944 -0700\n\
+committer Some User <someuser@gmail.com> 1454537944 -0700\n\
+gpgsig bad\n\
+\n\
+corrupt signature\n";
+
+	const char *oneline_data = "tree 51832e6397b30309c8bcad9c55fa6ae67778f378\n\
+parent a1b6decaaac768b5e01e1b5dbf5b2cc081bed1eb\n\
+author Some User <someuser@gmail.com> 1454537944 -0700\n\
+committer Some User <someuser@gmail.com> 1454537944 -0700\n\
+\n\
+corrupt signature\n";
+
 
 	cl_git_pass(git_repository_odb__weakptr(&odb, g_repo));
 	cl_git_pass(git_odb_write(&commit_id, odb, passing_commit_cases[4], strlen(passing_commit_cases[4]), GIT_OBJ_COMMIT));
@@ -523,6 +538,15 @@
 	cl_git_fail_with(GIT_ENOTFOUND, git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL));
 	cl_assert_equal_i(GITERR_OBJECT, giterr_last()->klass);
 
+	/* Parse the commit with a single-line signature */
+	git_buf_clear(&signature);
+	git_buf_clear(&signed_data);
+	cl_git_pass(git_odb_write(&commit_id, odb, oneline_signature, strlen(oneline_signature), GIT_OBJ_COMMIT));
+	cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL));
+	cl_assert_equal_s("bad", signature.ptr);
+	cl_assert_equal_s(oneline_data, signed_data.ptr);
+
+
 	git_buf_free(&signature);
 	git_buf_free(&signed_data);
 
diff --git a/tests/config/multivar.c b/tests/config/multivar.c
index 0150089..d1b8c4c 100644
--- a/tests/config/multivar.c
+++ b/tests/config/multivar.c
@@ -163,7 +163,7 @@
 
 	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
 
-	cl_git_pass(git_config_set_multivar(cfg, var, "", "variable"));
+	cl_git_pass(git_config_set_multivar(cfg, var, "$^", "variable"));
 	n = 0;
 	cl_git_pass(git_config_get_multivar_foreach(cfg, var, NULL, cb, &n));
 	cl_assert_equal_i(n, 1);
diff --git a/tests/config/write.c b/tests/config/write.c
index e634aa3..56ef2e9 100644
--- a/tests/config/write.c
+++ b/tests/config/write.c
@@ -695,3 +695,30 @@
 
 	git_config_free(cfg);
 }
+
+void test_config_write__repeated(void)
+{
+	const char *filename = "config-repeated";
+	git_config *cfg;
+	git_buf result = GIT_BUF_INIT;
+	const char *expected = "[sample \"prefix\"]\n\
+\tsetting1 = someValue1\n\
+\tsetting2 = someValue2\n\
+\tsetting3 = someValue3\n\
+\tsetting4 = someValue4\n\
+";
+	cl_git_pass(git_config_open_ondisk(&cfg, filename));
+	cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting1", "someValue1"));
+	cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting2", "someValue2"));
+	cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting3", "someValue3"));
+	cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting4", "someValue4"));
+	git_config_free(cfg);
+
+	cl_git_pass(git_config_open_ondisk(&cfg, filename));
+
+	cl_git_pass(git_futils_readbuffer(&result, filename));
+	cl_assert_equal_s(expected, result.ptr);
+	git_buf_free(&result);
+
+	git_config_free(cfg);
+}
diff --git a/tests/core/array.c b/tests/core/array.c
new file mode 100644
index 0000000..8e626a5
--- /dev/null
+++ b/tests/core/array.c
@@ -0,0 +1,57 @@
+#include "clar_libgit2.h"
+#include "array.h"
+
+static int int_lookup(const void *k, const void *a)
+{
+	const int *one = (const int *)k;
+	int *two = (int *)a;
+
+	return *one - *two;
+}
+
+#define expect_pos(k, n, ret) \
+	key = (k); \
+	cl_assert_equal_i((ret), \
+		git_array_search(&p, integers, int_lookup, &key)); \
+	cl_assert_equal_i((n), p);
+
+void test_core_array__bsearch2(void)
+{
+	git_array_t(int) integers = GIT_ARRAY_INIT;
+	int *i, key;
+	size_t p;
+
+	i = git_array_alloc(integers); *i = 2;
+	i = git_array_alloc(integers); *i = 3;
+	i = git_array_alloc(integers); *i = 5;
+	i = git_array_alloc(integers); *i = 7;
+	i = git_array_alloc(integers); *i = 7;
+	i = git_array_alloc(integers); *i = 8;
+	i = git_array_alloc(integers); *i = 13;
+	i = git_array_alloc(integers); *i = 21;
+	i = git_array_alloc(integers); *i = 25;
+	i = git_array_alloc(integers); *i = 42;
+	i = git_array_alloc(integers); *i = 69;
+	i = git_array_alloc(integers); *i = 121;
+	i = git_array_alloc(integers); *i = 256;
+	i = git_array_alloc(integers); *i = 512;
+	i = git_array_alloc(integers); *i = 513;
+	i = git_array_alloc(integers); *i = 514;
+	i = git_array_alloc(integers); *i = 516;
+	i = git_array_alloc(integers); *i = 516;
+	i = git_array_alloc(integers); *i = 517;
+
+	/* value to search for, expected position, return code */
+	expect_pos(3, 1, GIT_OK);
+	expect_pos(2, 0, GIT_OK);
+	expect_pos(1, 0, GIT_ENOTFOUND);
+	expect_pos(25, 8, GIT_OK);
+	expect_pos(26, 9, GIT_ENOTFOUND);
+	expect_pos(42, 9, GIT_OK);
+	expect_pos(50, 10, GIT_ENOTFOUND);
+	expect_pos(68, 10, GIT_ENOTFOUND);
+	expect_pos(256, 12, GIT_OK);
+
+	git_array_clear(integers);
+}
+
diff --git a/tests/core/stream.c b/tests/core/stream.c
index ace6a05..0cbf442 100644
--- a/tests/core/stream.c
+++ b/tests/core/stream.c
@@ -33,8 +33,12 @@
 	cl_git_pass(git_stream_register_tls(NULL));
 	error = git_tls_stream_new(&stream, "localhost", "443");
 
-	/* We don't have arbitrary TLS stream support on Windows */
-#if GIT_WIN32
+	/* We don't have arbitrary TLS stream support on Windows
+	 * or when openssl support is disabled (except on OSX
+	 * with Security framework).
+	 */
+#if defined(GIT_WIN32) || \
+	(!defined(GIT_SECURE_TRANSPORT) && !defined(GIT_OPENSSL))
 	cl_git_fail_with(-1, error);
 #else
 	cl_git_pass(error);
diff --git a/tests/diff/iterator.c b/tests/diff/iterator.c
index 8417e8e..25a23ed 100644
--- a/tests/diff/iterator.c
+++ b/tests/diff/iterator.c
@@ -268,7 +268,7 @@
 
 	cl_git_pass(git_iterator_current_tree_entry(&te, i));
 	cl_assert(te);
-	cl_assert(git_oid_streq(&te->oid, oid) == 0);
+	cl_assert(git_oid_streq(te->oid, oid) == 0);
 
 	cl_git_pass(git_iterator_current(&ie, i));
 	cl_git_pass(git_buf_sets(&path, ie->path));
diff --git a/tests/index/racy.c b/tests/index/racy.c
index 68aa460..1768f5e 100644
--- a/tests/index/racy.c
+++ b/tests/index/racy.c
@@ -105,8 +105,8 @@
 {
 	git_buf path = GIT_BUF_INIT;
 	git_index *index;
-	const git_index_entry *entry;
-	int i, found_race = 0;
+	git_index_entry *entry;
+	struct stat st;
 
 	/* Make sure we do have a timestamp */
 	cl_git_pass(git_repository_index__weakptr(&index, g_repo));
@@ -114,27 +114,20 @@
 
 	cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "A"));
 
-	/* Make sure writing the file, adding and rewriting happen in the same second */
-	for (i = 0; i < 10; i++) {
-		struct stat st;
-		cl_git_mkfile(path.ptr, "A");
+	cl_git_mkfile(path.ptr, "A");
+	cl_git_pass(git_index_add_bypath(index, "A"));
 
-		cl_git_pass(git_index_add_bypath(index, "A"));
-		cl_git_mkfile(path.ptr, "B");
-		cl_git_pass(git_index_write(index));
+	cl_git_mkfile(path.ptr, "B");
+	cl_git_pass(git_index_write(index));
 
-		cl_git_mkfile(path.ptr, "");
+	cl_git_mkfile(path.ptr, "");
 
-		cl_git_pass(p_stat(path.ptr, &st));
-		cl_assert(entry = git_index_get_bypath(index, "A", 0));
-		if (entry->mtime.seconds == (int32_t) st.st_mtime) {
-			found_race = 1;
-			break;
-		}
-	}
+	cl_git_pass(p_stat(path.ptr, &st));
+	cl_assert(entry = (git_index_entry *)git_index_get_bypath(index, "A", 0));
 
-	if (!found_race)
-		cl_fail("failed to find race after 10 attempts");
+	/* force a race */
+	entry->mtime.seconds = st.st_mtime;
+	entry->mtime.nanoseconds = st.st_mtime_nsec;
 
 	git_buf_free(&path);
 }
diff --git a/tests/object/tree/attributes.c b/tests/object/tree/attributes.c
index 413514c..8654dfa 100644
--- a/tests/object/tree/attributes.c
+++ b/tests/object/tree/attributes.c
@@ -82,6 +82,7 @@
 	cl_git_pass(git_treebuilder_new(&builder, repo, tree));
 	
 	entry = git_treebuilder_get(builder, "old_mode.txt");
+	cl_assert(entry != NULL);
 	cl_assert_equal_i(
 		GIT_FILEMODE_BLOB,
 		git_tree_entry_filemode(entry));
@@ -92,6 +93,7 @@
 
 	cl_git_pass(git_tree_lookup(&tree, repo, &tid2));
 	entry = git_tree_entry_byname(tree, "old_mode.txt");
+	cl_assert(entry != NULL);
 	cl_assert_equal_i(
 		GIT_FILEMODE_BLOB,
 		git_tree_entry_filemode(entry));
diff --git a/tests/online/badssl.c b/tests/online/badssl.c
index 12badbd..66b090d 100644
--- a/tests/online/badssl.c
+++ b/tests/online/badssl.c
@@ -36,3 +36,11 @@
 	cl_git_fail_with(GIT_ECERTIFICATE,
 			 git_clone(&g_repo, "https://self-signed.badssl.com/fake.git", "./fake", NULL));
 }
+
+void test_online_badssl__old_cipher(void)
+{
+	if (!g_has_ssl)
+		cl_skip();
+
+	cl_git_fail(git_clone(&g_repo, "https://rc4.badssl.com/fake.git", "./fake", NULL));
+}
diff --git a/tests/refs/create.c b/tests/refs/create.c
index b96d0c9..6d5a5f1 100644
--- a/tests/refs/create.c
+++ b/tests/refs/create.c
@@ -19,7 +19,7 @@
 {
    cl_git_sandbox_cleanup();
 
-	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0));
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
 }
 
 void test_refs_create__symbolic(void)
@@ -122,7 +122,7 @@
 }
 
 /* Can by default create a reference that targets at an unknown id */
-void test_refs_create__oid_unknown_succeeds_by_default(void)
+void test_refs_create__oid_unknown_succeeds_without_strict(void)
 {
 	git_reference *new_reference, *looked_up_ref;
 	git_oid id;
@@ -131,6 +131,8 @@
 
 	git_oid_fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644");
 
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0));
+
 	/* Create and write the new object id reference */
 	cl_git_pass(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL));
 	git_reference_free(new_reference);
@@ -141,7 +143,7 @@
 }
 
 /* Strict object enforcement enforces valid object id */
-void test_refs_create__oid_unknown_fails_strict_mode(void)
+void test_refs_create__oid_unknown_fails_by_default(void)
 {
 	git_reference *new_reference, *looked_up_ref;
 	git_oid id;
@@ -150,8 +152,6 @@
 
 	git_oid_fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644");
 
-	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
-
 	/* Create and write the new object id reference */
 	cl_git_fail(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL));
 
diff --git a/tests/refs/lookup.c b/tests/refs/lookup.c
index d076e49..456d0d2 100644
--- a/tests/refs/lookup.c
+++ b/tests/refs/lookup.c
@@ -58,3 +58,11 @@
 	error = git_reference_lookup(&ref, g_repo, "refs/heads/");
 	cl_assert_equal_i(error, GIT_EINVALIDSPEC);
 }
+
+void test_refs_lookup__dwim_notfound(void)
+{
+	git_reference *ref;
+
+	cl_git_fail_with(GIT_ENOTFOUND, git_reference_dwim(&ref, g_repo, "idontexist"));
+	cl_assert_equal_s("no reference found for shorthand 'idontexist'", giterr_last()->message);
+}
diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c
index c18e24a..6b5795b 100644
--- a/tests/repo/iterator.c
+++ b/tests/repo/iterator.c
@@ -1020,6 +1020,11 @@
 	if (!cl_is_chmod_supported())
 		return;
 
+#ifndef GIT_WIN32
+	if (geteuid() == 0)
+		cl_skip();
+#endif
+
 	g_repo = cl_git_sandbox_init("empty_standard_repo");
 
 	cl_must_pass(p_mkdir("empty_standard_repo/r", 0777));
diff --git a/tests/reset/hard.c b/tests/reset/hard.c
index 0c0af91..e461f80 100644
--- a/tests/reset/hard.c
+++ b/tests/reset/hard.c
@@ -122,9 +122,9 @@
 	int write_theirs = 4;
 	git_oid ancestor, ours, theirs;
 
-	git_oid_fromstr(&ancestor, "6bb0d9f700543ba3d318ba7075fc3bd696b4287b");
-	git_oid_fromstr(&ours, "b19a1e93bec1317dc6097229e12afaffbfa74dc2");
-	git_oid_fromstr(&theirs, "950b81b7eee953d050aa05a641f8e056c85dd1bd");
+	git_oid_fromstr(&ancestor, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
+	git_oid_fromstr(&ours, "32504b727382542f9f089e24fddac5e78533e96c");
+	git_oid_fromstr(&theirs, "061d42a44cacde5726057b67558821d95db96f19");
 
 	cl_git_rewritefile("status/conflicting_file", "conflicting file\n");
 
diff --git a/tests/status/worktree.c b/tests/status/worktree.c
index fc4afc6..5d3b4d5 100644
--- a/tests/status/worktree.c
+++ b/tests/status/worktree.c
@@ -657,7 +657,7 @@
 
 	entry.mode = 0100644;
 	entry.path = "modified_file";
-	git_oid_fromstr(&entry.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
+	git_oid_fromstr(&entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
 
 	cl_git_pass(git_repository_index(&index, repo));
 	cl_git_pass(git_index_conflict_add(index, &entry, &entry, &entry));
@@ -1006,6 +1006,9 @@
 	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
 	status_entry_counts counts = {0};
 
+	if (geteuid() == 0)
+		cl_skip();
+
 	/* Create directory with no read permission */
 	cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", 0777));
 	cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy");
diff --git a/tests/transport/register.c b/tests/transport/register.c
index ea917d5..97aae6b 100644
--- a/tests/transport/register.c
+++ b/tests/transport/register.c
@@ -40,15 +40,23 @@
 
 void test_transport_register__custom_transport_ssh(void)
 {
+	const char *urls[] = {
+		"ssh://somehost:somepath",
+		"ssh+git://somehost:somepath",
+		"git+ssh://somehost:somepath",
+		"git@somehost:somepath",
+	};
 	git_transport *transport;
+	unsigned i;
 
+	for (i = 0; i < ARRAY_SIZE(urls); i++) {
 #ifndef GIT_SSH
-	cl_git_fail_with(git_transport_new(&transport, NULL, "ssh://somehost:somepath"), -1);
-	cl_git_fail_with(git_transport_new(&transport, NULL, "git@somehost:somepath"), -1);
+		cl_git_fail_with(git_transport_new(&transport, NULL, urls[i]), -1);
 #else
-	cl_git_pass(git_transport_new(&transport, NULL, "git@somehost:somepath"));
-	transport->free(transport);
+		cl_git_pass(git_transport_new(&transport, NULL, urls[i]));
+		transport->free(transport);
 #endif
+	}
 
 	cl_git_pass(git_transport_register("ssh", dummy_transport, NULL));
 
@@ -58,11 +66,12 @@
 
 	cl_git_pass(git_transport_unregister("ssh"));
 
+	for (i = 0; i < ARRAY_SIZE(urls); i++) {
 #ifndef GIT_SSH
-	cl_git_fail_with(git_transport_new(&transport, NULL, "ssh://somehost:somepath"), -1);
-	cl_git_fail_with(git_transport_new(&transport, NULL, "git@somehost:somepath"), -1);
+		cl_git_fail_with(git_transport_new(&transport, NULL, urls[i]), -1);
 #else
-	cl_git_pass(git_transport_new(&transport, NULL, "git@somehost:somepath"));
-	transport->free(transport);
+		cl_git_pass(git_transport_new(&transport, NULL, urls[i]));
+		transport->free(transport);
 #endif
+	}
 }