Merge pull request #3980 from tiennou/doc-fixes

Documentation fixes
diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE
new file mode 100644
index 0000000..1e432ae
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE
@@ -0,0 +1,14 @@
+You are opening a _bug report_ against the libgit2 project.  If you have a
+question about an API or usage, please ask on StackOverflow:
+http://stackoverflow.com/questions/tagged/libgit2.  Please fill out the
+reproduction steps (below) and delete this introductory paragraph.  Thanks!
+
+### Reproduction steps
+
+### Expected behavior
+
+### Actual behavior
+
+### Version of libgit2 (release number or SHA1)
+
+### Operating system(s) tested
diff --git a/.travis.yml b/.travis.yml
index bfc0fac..af38252 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -47,9 +47,6 @@
      os: linux
  allow_failures:
    - env: COVERITY=1
-   - env:
-       - VALGRIND=1
-         OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug"
 
 install:
   - if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dae86de..8544ac4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,15 @@
-v0.24 + 1
+v0.25 + 1
+-------
+
+### Changes or improvements
+
+### API additions
+
+### API removals
+
+### Breaking API changes
+
+v0.25
 -------
 
 ### Changes or improvements
@@ -17,14 +28,37 @@
 
 * Improve the performance of the revwalk and bring us closer to git's code.
 
+* The reference db has improved support for concurrency and returns `GIT_ELOCKED`
+  when an operation could not be performed due to locking.
+
+* Nanosecond resolution is now activated by default, following git's change to
+  do this.
+
+* We now restrict the set of ciphers we let OpenSSL use by default.
+
+* Users can now register their own merge drivers for use with `.gitattributes`.
+  The library also gained built-in support for the union merge driver.
+
+* The default for creating references is now to validate that the object does
+  exist.
+
+* Add `git_proxy_options` which is used by the different networking
+  implementations to let the caller specify the proxy settings instead of
+  relying on the environment variables.
+
 ### API additions
 
 * You can now get the user-agent used by libgit2 using the
   `GIT_OPT_GET_USER_AGENT` option with `git_libgit2_opts()`.
   It is the counterpart to `GIT_OPT_SET_USER_AGENT`.
 
+* The `GIT_OPT_SET_SSL_CIPHERS` option for `git_libgit2_opts()` lets you specify
+  a custom list of ciphers to use for OpenSSL.
+
 * `git_commit_create_buffer()` creates a commit and writes it into a
-  user-provided buffer instead of writing it into the object db.
+  user-provided buffer instead of writing it into the object db. Combine it with
+  `git_commit_create_with_signature()` in order to create a commit with a
+  cryptographic signature.
 
 * `git_blob_create_fromstream()` and
   `git_blob_create_fromstream_commit()` allow you to create a blob by
@@ -50,12 +84,48 @@
       `git_repository_open_ext` with this flag will error out if either
       `$GIT_WORK_TREE` or `$GIT_COMMON_DIR` is set.
 
-* `git_diff_from_buffer` can create a `git_diff` object from the contents
+* `git_diff_from_buffer()` can create a `git_diff` object from the contents
   of a git-style patch file.
 
 * `git_index_version()` and `git_index_set_version()` to get and set
   the index version
 
+* `git_odb_expand_ids()` lets you check for the existence of multiple
+  objects at once.
+
+* The new `git_blob_dup()`, `git_commit_dup()`, `git_tag_dup()` and
+  `git_tree_dup()` functions provide type-specific wrappers for
+  `git_object_dup()` to reduce noise and increase type safety for callers.
+
+* `git_reference_dup()` lets you duplicate a reference to aid in ownership
+  management and cleanup.
+
+* `git_signature_from_buffer()` lets you create a signature from a string in the
+  format that appear in objects.
+
+* `git_tree_create_updated()` lets you create a tree based on another one
+  together with a list of updates. For the covered update cases, it's more
+  efficient than the `git_index` route.
+
+* `git_apply_patch()` applies hunks from a `git_patch` to a buffer.
+
+* `git_diff_to_buf()` lets you print an entire diff directory to a buffer,
+  similar to how `git_patch_to_buf()` works.
+
+* `git_proxy_init_options()` is added to initialize a `git_proxy_options`
+  structure at run-time.
+
+* `git_merge_driver_register()`, `git_merge_driver_unregister()` let you
+  register and unregister a custom merge driver to be used when `.gitattributes`
+  specifies it.
+
+* `git_merge_driver_lookup()` can be used to look up a merge driver by name.
+
+* `git_merge_driver_source_repo()`, `git_merge_driver_source_ancestor()`,
+  `git_merge_driver_source_ours()`, `git_merge_driver_source_theirs()`,
+  `git_merge_driver_source_file_options()` added as accessors to
+  `git_merge_driver_source`.
+
 ### API removals
 
 * `git_blob_create_fromchunks()` has been removed in favour of
@@ -80,6 +150,8 @@
   If this is `NULL`, then it will not be called and the `exists` function
   will be used instead.
 
+* `git_remote_connect()` now accepts proxy options.
+
 v0.24
 -------
 
diff --git a/README.md b/README.md
index e656a46..19d24de 100644
--- a/README.md
+++ b/README.md
@@ -43,9 +43,17 @@
 What It Can Do
 ==============
 
-`libgit2` is already very usable and is being used in production for many
-applications including the GitHub.com site, in Plastic SCM and also powering
-Microsoft's Visual Studio tools for Git.  The library provides:
+The goal of this library is to allow its users the ability to handle Git data in
+their applications from their programming language of choice, as is used in
+production for many applications including the GitHub.com site, in Plastic SCM
+and also powering Microsoft's Visual Studio tools for Git.
+
+It does not aim to replace the git tool or its user-facing commands. Some APIs
+resemble the plumbing commands as those align closely with the concepts of the
+Git system, but most commands a user would type are out of scope for this
+library to implement directly.
+
+The library provides:
 
 * SHA conversions, formatting and shortening
 * abstracted ODB backend system
@@ -236,7 +244,7 @@
 * Rust
     * git2-rs <https://github.com/alexcrichton/git2-rs>
 * Swift
-    * Gift <https://github.com/modocache/Gift>
+    * SwiftGit2 <https://github.com/SwiftGit2/SwiftGit2>
 * Vala
     * libgit2.vapi <https://github.com/apmasell/vapis/blob/master/libgit2.vapi>
 
diff --git a/THREADING.md b/THREADING.md
index e9be8b9..430bca8 100644
--- a/THREADING.md
+++ b/THREADING.md
@@ -62,29 +62,34 @@
 General Case
 ------------
 
-By default we use libcurl, which has its own ![recommendations for
-thread safety](http://curl.haxx.se/libcurl/c/libcurl-tutorial.html#Multi-threading).
+If it's available, by default we use libcurl to provide HTTP tunneling support,
+which may be linked against a number of cryptographic libraries and has its
+own
+[recommendations for thread safety](https://curl.haxx.se/libcurl/c/threadsafe.html).
 
-If libcurl was not found or was disabled, libgit2 uses OpenSSL to be
-able to use HTTPS as a transport. This library is made to be
-thread-implementation agnostic, and the users of the library must set
-which locking function it should use. This means that libgit2 cannot
-know what to set as the user of libgit2 may use OpenSSL independently
-and the locking settings must survive libgit2 shutting down.
+If there are no alternative TLS implementations (currently only
+SecureTransport), libgit2 uses OpenSSL in order to use HTTPS as a transport.
+OpenSSL is thread-safe starting at version 1.1.0. If your copy of libgit2 is
+linked against that version, you do not need to take any further steps.
 
-Even if libgit2 doesn't use OpenSSL directly, OpenSSL can still be used
-by libssh2 depending on the configuration.  If OpenSSL is used both by
-libgit2 and libssh2, you only need to set up threading for OpenSSL once.
+Older versions of OpenSSL are made to be thread-implementation agnostic, and the
+users of the library must set which locking function it should use. libgit2
+cannot know what to set as the user of libgit2 may also be using OpenSSL independently and
+the locking settings must then live outside the lifetime of libgit2.
 
-libgit2 does provide a last-resort convenience function
+Even if libgit2 doesn't use OpenSSL directly, OpenSSL can still be used by
+libssh2 or libcurl depending on the configuration. If OpenSSL is used by
+more than one library, you only need to set up threading for OpenSSL once.
+
+If libgit2 is linked against OpenSSL, it provides a last-resort convenience function
 `git_openssl_set_locking()` (available in `sys/openssl.h`) to use the
-platform-native mutex mechanisms to perform the locking, which you may
-rely on if you do not want to use OpenSSL outside of libgit2, or you
-know that libgit2 will outlive the rest of the operations. It is not
+platform-native mutex mechanisms to perform the locking, which you can use
+if you do not want to use OpenSSL outside of libgit2, or you
+know that libgit2 will outlive the rest of the operations. It is then not
 safe to use OpenSSL multi-threaded after libgit2's shutdown function
 has been called.  Note `git_openssl_set_locking()` only works if
 libgit2 uses OpenSSL directly - if OpenSSL is only used as a dependency
-of libssh2 as described above, `git_openssl_set_locking()` is a no-op.
+of libssh2 or libcurl as described above, `git_openssl_set_locking()` is a no-op.
 
 If your programming language offers a package/bindings for OpenSSL,
 you should very strongly prefer to use that in order to set up
@@ -96,9 +101,6 @@
 on threading for more details, and http://trac.libssh2.org/wiki/MultiThreading
 for a specific example of providing the threading callbacks.
 
-Be also aware that libgit2 does not always link against OpenSSL
-if there are alternatives provided by the system.
-
 libssh2 may be linked against OpenSSL or libgcrypt. If it uses OpenSSL,
 see the above paragraphs. If it uses libgcrypt, then you need to
 set up its locking before using it multi-threaded. libgit2 has no
diff --git a/include/git2/common.h b/include/git2/common.h
index a8d698f..99c9981 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -109,9 +109,27 @@
  * was compiled
  */
 typedef enum {
+  /**
+   * If set, libgit2 was built thread-aware and can be safely used from multiple
+   * threads.
+   */
 	GIT_FEATURE_THREADS	= (1 << 0),
+  /**
+   * If set, libgit2 was built with and linked against a TLS implementation.
+   * Custom TLS streams may still be added by the user to support HTTPS
+   * regardless of this.
+   */
 	GIT_FEATURE_HTTPS	= (1 << 1),
+  /**
+   * If set, libgit2 was built with and linked against libssh2. A custom
+   * transport may still be added by the user to support libssh2 regardless of
+   * this.
+   */
 	GIT_FEATURE_SSH		= (1 << 2),
+  /**
+   * If set, libgit2 was built with support for sub-second resolution in file
+   * modification times.
+   */
 	GIT_FEATURE_NSEC	= (1 << 3),
 } git_feature_t;
 
diff --git a/include/git2/proxy.h b/include/git2/proxy.h
index dcd6156..194cbb6 100644
--- a/include/git2/proxy.h
+++ b/include/git2/proxy.h
@@ -19,7 +19,7 @@
 	/**
 	 * Do not attempt to connect through a proxy
 	 *
-	 * If built against lbicurl, it itself may attempt to connect
+	 * If built against libcurl, it itself may attempt to connect
 	 * to a proxy if the environment variables specify it.
 	 */
 	GIT_PROXY_NONE,
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 370abf1..6911141 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -569,7 +569,7 @@
  * Initializes a `git_fetch_options` with default values. Equivalent to
  * creating an instance with GIT_FETCH_OPTIONS_INIT.
  *
- * @param opts the `git_push_options` instance to initialize.
+ * @param opts the `git_fetch_options` instance to initialize.
  * @param version the version of the struct; you should pass
  *        `GIT_FETCH_OPTIONS_VERSION` here.
  * @return Zero on success; -1 on failure.
diff --git a/include/git2/version.h b/include/git2/version.h
index 66a6623..0df191f 100644
--- a/include/git2/version.h
+++ b/include/git2/version.h
@@ -7,12 +7,12 @@
 #ifndef INCLUDE_git_version_h__
 #define INCLUDE_git_version_h__
 
-#define LIBGIT2_VERSION "0.24.0"
+#define LIBGIT2_VERSION "0.25.0"
 #define LIBGIT2_VER_MAJOR 0
-#define LIBGIT2_VER_MINOR 24
+#define LIBGIT2_VER_MINOR 25
 #define LIBGIT2_VER_REVISION 0
 #define LIBGIT2_VER_PATCH 0
 
-#define LIBGIT2_SOVERSION 24
+#define LIBGIT2_SOVERSION 25
 
 #endif
diff --git a/script/coverity.sh b/script/coverity.sh
index 7fe9eb4..5fe16c0 100755
--- a/script/coverity.sh
+++ b/script/coverity.sh
@@ -1,23 +1,22 @@
 #!/bin/bash
 set -e
 
-# Environment check
-[ -z "$COVERITY_TOKEN" ] && echo "Need to set a coverity token" && exit 1
-
 # Only run this on our branches
-echo "Pull request: $TRAVIS_PULL_REQUEST  |  Slug: $TRAVIS_REPO_SLUG"
-if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_REPO_SLUG" != "libgit2/libgit2" ];
+echo "Branch: $TRAVIS_BRANCH  |  Pull request: $TRAVIS_PULL_REQUEST  |  Slug: $TRAVIS_REPO_SLUG"
+if [ "$TRAVIS_BRANCH" != "master" -o "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_REPO_SLUG" != "libgit2/libgit2" ];
 then
-	echo "Only analyzing 'development' on the main repo."
+	echo "Only analyzing the 'master' brach of the main repository."
 	exit 0
 fi
 
-COV_VERSION=6.6.1
+# Environment check
+[ -z "$COVERITY_TOKEN" ] && echo "Need to set a coverity token" && exit 1
+
 case $(uname -m) in
 	i?86)				BITS=32 ;;
 	amd64|x86_64)	BITS=64 ;;
 esac
-SCAN_TOOL=https://scan.coverity.com/download/linux-${BITS}
+SCAN_TOOL=https://scan.coverity.com/download/cxx/linux${BITS}
 TOOL_BASE=$(pwd)/_coverity-scan
 
 # Install coverity tools
diff --git a/src/apply.c b/src/apply.c
index f701724..6359342 100644
--- a/src/apply.c
+++ b/src/apply.c
@@ -173,7 +173,7 @@
 		git_diff_line *line = git_array_get(patch->lines, linenum);
 
 		if (!line) {
-			error = apply_err("Preimage does not contain line %d", linenum);
+			error = apply_err("Preimage does not contain line %"PRIuZ, linenum);
 			goto done;
 		}
 
diff --git a/src/branch.c b/src/branch.c
index 51c35d7..8d1ed65 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -58,16 +58,17 @@
 	const char *from,
 	int force)
 {
-	int is_head = 0;
+	int is_unmovable_head = 0;
 	git_reference *branch = NULL;
 	git_buf canonical_branch_name = GIT_BUF_INIT,
 			  log_message = GIT_BUF_INIT;
 	int error = -1;
+	int bare = git_repository_is_bare(repository);
 
 	assert(branch_name && commit && ref_out);
 	assert(git_object_owner((const git_object *)commit) == repository);
 
-	if (force && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
+	if (force && !bare && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
 		error = git_branch_is_head(branch);
 		git_reference_free(branch);
 		branch = NULL;
@@ -75,10 +76,10 @@
 		if (error < 0)
 			goto cleanup;
 
-		is_head = error;
+		is_unmovable_head = error;
 	}
 
-	if (is_head && force) {
+	if (is_unmovable_head && force) {
 		giterr_set(GITERR_REFERENCE, "Cannot force update branch '%s' as it is "
 			"the current HEAD of the repository.", branch_name);
 		error = -1;
diff --git a/src/checkout.c b/src/checkout.c
index b3427fb..6295091 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -1966,7 +1966,7 @@
 	if (i == INT_MAX) {
 		git_buf_truncate(path, path_len);
 
-		giterr_set(GITERR_CHECKOUT, "Could not write '%s': working directory file exists", path);
+		giterr_set(GITERR_CHECKOUT, "Could not write '%s': working directory file exists", path->ptr);
 		return GIT_EEXISTS;
 	}
 
@@ -2469,7 +2469,7 @@
 			data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3;
 		else {
 			giterr_set(GITERR_CHECKOUT, "unknown style '%s' given for 'merge.conflictstyle'",
-				conflict_style);
+				conflict_style->value);
 			error = -1;
 			git_config_entry_free(conflict_style);
 			goto cleanup;
diff --git a/src/common.h b/src/common.h
index 51fb918..f12cc98 100644
--- a/src/common.h
+++ b/src/common.h
@@ -103,7 +103,8 @@
 /**
  * Set the error message for this thread, formatting as needed.
  */
-void giterr_set(int error_class, const char *string, ...);
+
+void giterr_set(int error_class, const char *string, ...) GIT_FORMAT_PRINTF(2, 3);
 
 /**
  * Set the error message for a regex failure, using the internal regex
diff --git a/src/curl_stream.c b/src/curl_stream.c
index 98de187..4e0455c 100644
--- a/src/curl_stream.c
+++ b/src/curl_stream.c
@@ -15,6 +15,16 @@
 #include "vector.h"
 #include "proxy.h"
 
+/* This is for backwards compatibility with curl<7.45.0. */
+#ifndef CURLINFO_ACTIVESOCKET
+# define CURLINFO_ACTIVESOCKET CURLINFO_LASTSOCKET
+# define GIT_CURL_BADSOCKET -1
+# define git_activesocket_t long
+#else
+# define GIT_CURL_BADSOCKET CURL_SOCKET_BAD
+# define git_activesocket_t curl_socket_t
+#endif
+
 typedef struct {
 	git_stream parent;
 	CURL *handle;
@@ -87,7 +97,8 @@
 static int curls_connect(git_stream *stream)
 {
 	curl_stream *s = (curl_stream *) stream;
-	long sockextr, connect_last = 0;
+	git_activesocket_t sockextr;
+	long connect_last = 0;
 	int failed_cert = 0, error;
 	bool retry_connect;
 	CURLcode res;
@@ -117,10 +128,15 @@
 	if (res == CURLE_PEER_FAILED_VERIFICATION)
 		failed_cert = 1;
 
-	if ((res = curl_easy_getinfo(s->handle, CURLINFO_LASTSOCKET, &sockextr)) != CURLE_OK) {
+	if ((res = curl_easy_getinfo(s->handle, CURLINFO_ACTIVESOCKET, &sockextr)) != CURLE_OK) {
 		return seterr_curl(s);
 	}
 
+	if (sockextr == GIT_CURL_BADSOCKET) {
+		giterr_set(GITERR_NET, "curl socket is no longer valid");
+		return -1;
+	}
+
 	s->socket = sockextr;
 
 	if (s->parent.encrypted && failed_cert)
@@ -198,6 +214,7 @@
 	FD_ZERO(&outfd);
 	FD_ZERO(&errfd);
 
+	assert(fd >= 0);
 	FD_SET(fd, &errfd);
 	if (reading)
 		FD_SET(fd, &infd);
diff --git a/src/fetchhead.c b/src/fetchhead.c
index a95ea4c..3d16c21 100644
--- a/src/fetchhead.c
+++ b/src/fetchhead.c
@@ -149,7 +149,7 @@
 
 	if (!*line) {
 		giterr_set(GITERR_FETCHHEAD,
-			"Empty line in FETCH_HEAD line %d", line_num);
+			"Empty line in FETCH_HEAD line %"PRIuZ, line_num);
 		return -1;
 	}
 
@@ -163,7 +163,7 @@
 
 	if (strlen(oid_str) != GIT_OID_HEXSZ) {
 		giterr_set(GITERR_FETCHHEAD,
-			"Invalid object ID in FETCH_HEAD line %d", line_num);
+			"Invalid object ID in FETCH_HEAD line %"PRIuZ, line_num);
 		return -1;
 	}
 
@@ -171,7 +171,7 @@
 		const git_error *oid_err = giterr_last();
 		const char *err_msg = oid_err ? oid_err->message : "Invalid object ID";
 
-		giterr_set(GITERR_FETCHHEAD, "%s in FETCH_HEAD line %d",
+		giterr_set(GITERR_FETCHHEAD, "%s in FETCH_HEAD line %"PRIuZ,
 			err_msg, line_num);
 		return -1;
 	}
@@ -180,7 +180,7 @@
 	if (*line) {
 		if ((is_merge_str = git__strsep(&line, "\t")) == NULL) {
 			giterr_set(GITERR_FETCHHEAD,
-				"Invalid description data in FETCH_HEAD line %d", line_num);
+				"Invalid description data in FETCH_HEAD line %"PRIuZ, line_num);
 			return -1;
 		}
 
@@ -190,13 +190,13 @@
 			*is_merge = 0;
 		else {
 			giterr_set(GITERR_FETCHHEAD,
-				"Invalid for-merge entry in FETCH_HEAD line %d", line_num);
+				"Invalid for-merge entry in FETCH_HEAD line %"PRIuZ, line_num);
 			return -1;
 		}
 
 		if ((desc = line) == NULL) {
 			giterr_set(GITERR_FETCHHEAD,
-				"Invalid description in FETCH_HEAD line %d", line_num);
+				"Invalid description in FETCH_HEAD line %"PRIuZ, line_num);
 			return -1;
 		}
 
@@ -213,7 +213,7 @@
 			if ((desc = strstr(name, "' ")) == NULL ||
 				git__prefixcmp(desc, "' of ") != 0) {
 				giterr_set(GITERR_FETCHHEAD,
-					"Invalid description in FETCH_HEAD line %d", line_num);
+					"Invalid description in FETCH_HEAD line %"PRIuZ, line_num);
 				return -1;
 			}
 
@@ -277,7 +277,7 @@
 	}
 
 	if (*buffer) {
-		giterr_set(GITERR_FETCHHEAD, "No EOL at line %d", line_num+1);
+		giterr_set(GITERR_FETCHHEAD, "No EOL at line %"PRIuZ, line_num+1);
 		error = -1;
 		goto done;
 	}
diff --git a/src/fileops.c b/src/fileops.c
index fcc0301..a82202c 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -72,8 +72,16 @@
 		O_EXCL | O_BINARY | O_CLOEXEC, mode);
 
 	if (fd < 0) {
+		int error = errno;
 		giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
-		return errno == EEXIST ? GIT_ELOCKED : -1;
+		switch (error) {
+		case EEXIST:
+			return GIT_ELOCKED;
+		case ENOENT:
+			return GIT_ENOTFOUND;
+		default:
+			return -1;
+		}
 	}
 
 	return fd;
diff --git a/src/fileops.h b/src/fileops.h
index 54e3bd4..65c96a6 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -45,12 +45,12 @@
 extern int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode);
 
 /**
- * Create an open a process-locked file
+ * Create and open a process-locked file
  */
 extern int git_futils_creat_locked(const char *path, const mode_t mode);
 
 /**
- * Create an open a process-locked file, while
+ * Create and open a process-locked file, while
  * also creating all the folders in its path
  */
 extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode);
diff --git a/src/global.c b/src/global.c
index 45b1ab8..e2ad8fe 100644
--- a/src/global.c
+++ b/src/global.c
@@ -247,6 +247,7 @@
 #elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
 
 static pthread_key_t _tls_key;
+static pthread_mutex_t _init_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
 int init_error = 0;
 
@@ -268,12 +269,19 @@
 
 int git_libgit2_init(void)
 {
-	int ret;
+	int ret, err;
 
 	ret = git_atomic_inc(&git__n_inits);
-	pthread_once(&_once_init, init_once);
 
-	return init_error ? init_error : ret;
+	if ((err = pthread_mutex_lock(&_init_mutex)) != 0)
+		return err;
+	err = pthread_once(&_once_init, init_once);
+	err |= pthread_mutex_unlock(&_init_mutex);
+
+	if (err || init_error)
+		return err | init_error;
+
+	return ret;
 }
 
 int git_libgit2_shutdown(void)
@@ -285,6 +293,9 @@
 	if ((ret = git_atomic_dec(&git__n_inits)) != 0)
 		return ret;
 
+	if ((ret = pthread_mutex_lock(&_init_mutex)) != 0)
+		return ret;
+
 	/* Shut down any subsystems that have global state */
 	shutdown_common();
 
@@ -298,6 +309,9 @@
 	git_mutex_free(&git__mwindow_mutex);
 	_once_init = new_once;
 
+	if ((ret = pthread_mutex_unlock(&_init_mutex)) != 0)
+		return ret;
+
 	return 0;
 }
 
@@ -327,7 +341,7 @@
 {
 	int ret;
 
-	/* Only init SSL the first time */
+	/* Only init subsystems the first time */
 	if ((ret = git_atomic_inc(&git__n_inits)) != 1)
 		return ret;
 
@@ -345,6 +359,7 @@
 	if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
 		shutdown_common();
 		git__global_state_cleanup(&__state);
+		memset(&__state, 0, sizeof(__state));
 	}
 
 	return ret;
diff --git a/src/global.h b/src/global.h
index 2199515..88f40aa 100644
--- a/src/global.h
+++ b/src/global.h
@@ -16,6 +16,12 @@
 	git_error error_t;
 	git_buf error_buf;
 	char oid_fmt[GIT_OID_HEXSZ+1];
+
+	/* On Windows, this is the current child thread that was started by
+	 * `git_thread_create`.  This is used to set the thread's exit code
+	 * when terminated by `git_thread_exit`.  It is unused on POSIX.
+	 */
+	git_thread *current_thread;
 } git_global_st;
 
 #ifdef GIT_OPENSSL
diff --git a/src/graph.c b/src/graph.c
index 8accd80..948f7d3 100644
--- a/src/graph.c
+++ b/src/graph.c
@@ -59,7 +59,7 @@
 	/* as long as there are non-STALE commits */
 	while (interesting(&list, roots)) {
 		git_commit_list_node *commit = git_pqueue_pop(&list);
-		int flags;
+		unsigned int flags;
 
 		if (commit == NULL)
 			break;
diff --git a/src/index.c b/src/index.c
index bc15959..42579f1 100644
--- a/src/index.c
+++ b/src/index.c
@@ -552,7 +552,7 @@
 
 static int create_index_error(int error, const char *msg)
 {
-	giterr_set(GITERR_INDEX, msg);
+	giterr_set_str(GITERR_INDEX, msg);
 	return error;
 }
 
diff --git a/src/iterator.c b/src/iterator.c
index 598c69c..8fc62c0 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -1311,7 +1311,7 @@
 
 	if (iter->frames.size == FILESYSTEM_MAX_DEPTH) {
 		giterr_set(GITERR_REPOSITORY,
-			"directory nesting too deep (%d)", iter->frames.size);
+			"directory nesting too deep (%"PRIuZ")", iter->frames.size);
 		return -1;
 	}
 
diff --git a/src/merge.c b/src/merge.c
index 6934aa7..1142917 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -591,7 +591,7 @@
 	}
 
 	if (*buffer) {
-		giterr_set(GITERR_MERGE, "No EOL at line %d", line_num);
+		giterr_set(GITERR_MERGE, "No EOL at line %"PRIuZ, line_num);
 		error = -1;
 		goto cleanup;
 	}
diff --git a/src/odb.c b/src/odb.c
index acf4dea..7b194c7 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -1400,7 +1400,7 @@
 		char oid_str[GIT_OID_HEXSZ + 1];
 		git_oid_tostr(oid_str, oid_len+1, oid);
 		giterr_set(GITERR_ODB, "Object not found - %s (%.*s)",
-			message, oid_len, oid_str);
+			message, (int) oid_len, oid_str);
 	} else
 		giterr_set(GITERR_ODB, "Object not found - %s", message);
 
diff --git a/src/odb_mempack.c b/src/odb_mempack.c
index 594a278..68db3bc 100644
--- a/src/odb_mempack.c
+++ b/src/odb_mempack.c
@@ -24,7 +24,7 @@
 	git_oid oid;
 	size_t len;
 	git_otype type;
-	char data[];
+	char data[GIT_FLEX_ARRAY];
 };
 
 struct memory_packer_db {
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
index cab7d94..ddb9c4a 100644
--- a/src/openssl_stream.c
+++ b/src/openssl_stream.c
@@ -13,6 +13,7 @@
 #include "posix.h"
 #include "stream.h"
 #include "socket_stream.h"
+#include "openssl_stream.h"
 #include "netops.h"
 #include "git2/transport.h"
 #include "git2/sys/openssl.h"
@@ -71,12 +72,20 @@
 
 #endif /* GIT_THREADS */
 
+static BIO_METHOD *git_stream_bio_method;
+static int init_bio_method(void);
+
 /**
  * This function aims to clean-up the SSL context which
  * we allocated.
  */
 static void shutdown_ssl(void)
 {
+	if (git_stream_bio_method) {
+		BIO_meth_free(git_stream_bio_method);
+		git_stream_bio_method = NULL;
+	}
+
 	if (git__ssl_ctx) {
 		SSL_CTX_free(git__ssl_ctx);
 		git__ssl_ctx = NULL;
@@ -121,6 +130,13 @@
 		git__ssl_ctx = NULL;
 		return -1;
 	}
+
+	if (init_bio_method() < 0) {
+		SSL_CTX_free(git__ssl_ctx);
+		git__ssl_ctx = NULL;
+		return -1;
+	}
+
 #endif
 
 	git__on_shutdown(shutdown_ssl);
@@ -156,10 +172,8 @@
 
 static int bio_create(BIO *b)
 {
-	b->init = 1;
-	b->num = 0;
-	b->ptr = NULL;
-	b->flags = 0;
+	BIO_set_init(b, 1);
+	BIO_set_data(b, NULL);
 
 	return 1;
 }
@@ -169,23 +183,22 @@
 	if (!b)
 		return 0;
 
-	b->init = 0;
-	b->num = 0;
-	b->ptr = NULL;
-	b->flags = 0;
+	BIO_set_data(b, NULL);
 
 	return 1;
 }
 
 static int bio_read(BIO *b, char *buf, int len)
 {
-	git_stream *io = (git_stream *) b->ptr;
+	git_stream *io = (git_stream *) BIO_get_data(b);
+
 	return (int) git_stream_read(io, buf, len);
 }
 
 static int bio_write(BIO *b, const char *buf, int len)
 {
-	git_stream *io = (git_stream *) b->ptr;
+	git_stream *io = (git_stream *) BIO_get_data(b);
+
 	return (int) git_stream_write(io, buf, len, 0);
 }
 
@@ -214,17 +227,22 @@
 	return bio_write(b, str, strlen(str));
 }
 
-static BIO_METHOD git_stream_bio_method = {
-	BIO_TYPE_SOURCE_SINK,
-	"git_stream",
-	bio_write,
-	bio_read,
-	bio_puts,
-	bio_gets,
-	bio_ctrl,
-	bio_create,
-	bio_destroy
-};
+static int init_bio_method(void)
+{
+	/* Set up the BIO_METHOD we use for wrapping our own stream implementations */
+	git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream");
+	GITERR_CHECK_ALLOC(git_stream_bio_method);
+
+	BIO_meth_set_write(git_stream_bio_method, bio_write);
+	BIO_meth_set_read(git_stream_bio_method, bio_read);
+	BIO_meth_set_puts(git_stream_bio_method, bio_puts);
+	BIO_meth_set_gets(git_stream_bio_method, bio_gets);
+	BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl);
+	BIO_meth_set_create(git_stream_bio_method, bio_create);
+	BIO_meth_set_destroy(git_stream_bio_method, bio_destroy);
+
+	return 0;
+}
 
 static int ssl_set_error(SSL *ssl, int error)
 {
@@ -339,7 +357,7 @@
 		num = sk_GENERAL_NAME_num(alts);
 		for (i = 0; i < num && matched != 1; i++) {
 			const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
-			const char *name = (char *) ASN1_STRING_data(gn->d.ia5);
+			const char *name = (char *) ASN1_STRING_get0_data(gn->d.ia5);
 			size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
 
 			/* Skip any names of a type we're not looking for */
@@ -394,7 +412,7 @@
 		if (size > 0) {
 			peer_cn = OPENSSL_malloc(size + 1);
 			GITERR_CHECK_ALLOC(peer_cn);
-			memcpy(peer_cn, ASN1_STRING_data(str), size);
+			memcpy(peer_cn, ASN1_STRING_get0_data(str), size);
 			peer_cn[size] = '\0';
 		} else {
 			goto cert_fail_name;
@@ -445,11 +463,12 @@
 
 	st->connected = true;
 
-	bio = BIO_new(&git_stream_bio_method);
+	bio = BIO_new(git_stream_bio_method);
 	GITERR_CHECK_ALLOC(bio);
-	bio->ptr = st->io;
 
+	BIO_set_data(bio, st->io);
 	SSL_set_bio(st->ssl, bio, bio);
+
 	/* specify the host in case SNI is needed */
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
 	SSL_set_tlsext_host_name(st->ssl, st->host);
diff --git a/src/openssl_stream.h b/src/openssl_stream.h
index 82b5110..b769437 100644
--- a/src/openssl_stream.h
+++ b/src/openssl_stream.h
@@ -13,4 +13,110 @@
 
 extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port);
 
+/*
+ * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it
+ * which do not exist in previous versions. We define these inline functions so
+ * we can program against the interface instead of littering the implementation
+ * with ifdefs.
+ */
+#ifdef GIT_OPENSSL
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+
+
+
+# if OPENSSL_VERSION_NUMBER < 0x10100000L
+
+GIT_INLINE(BIO_METHOD*) BIO_meth_new(int type, const char *name)
+{
+	BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD));
+	if (!meth) {
+		return NULL;
+	}
+
+	meth->type = type;
+	meth->name = name;
+
+	return meth;
+}
+
+GIT_INLINE(void) BIO_meth_free(BIO_METHOD *biom)
+{
+	git__free(biom);
+}
+
+GIT_INLINE(int) BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
+{
+	biom->bwrite = write;
+	return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
+{
+	biom->bread = read;
+	return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
+{
+	biom->bputs = puts;
+	return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
+
+{
+	biom->bgets = gets;
+	return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
+{
+	biom->ctrl = ctrl;
+	return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *))
+{
+	biom->create = create;
+	return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *))
+{
+	biom->destroy = destroy;
+	return 1;
+}
+
+GIT_INLINE(int) BIO_get_new_index(void)
+{
+	/* This exists as of 1.1 so before we'd just have 0 */
+	return 0;
+}
+
+GIT_INLINE(void) BIO_set_init(BIO *b, int init)
+{
+	b->init = init;
+}
+
+GIT_INLINE(void) BIO_set_data(BIO *a, void *ptr)
+{
+	a->ptr = ptr;
+}
+
+GIT_INLINE(void*) BIO_get_data(BIO *a)
+{
+	return a->ptr;
+}
+
+GIT_INLINE(const unsigned char *) ASN1_STRING_get0_data(const ASN1_STRING *x)
+{
+	return ASN1_STRING_data((ASN1_STRING *)x);
+}
+
+# endif // OpenSSL < 1.1
+#endif // GIT_OPENSSL
+
 #endif
diff --git a/src/pack.c b/src/pack.c
index 310f00f..2e19b3b 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -509,8 +509,10 @@
 		git_packfile_stream_free(&stream);
 		if (error < 0)
 			return error;
-	} else
+	} else {
 		*size_p = size;
+		base_offset = 0;
+	}
 
 	while (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) {
 		curpos = base_offset;
@@ -757,8 +759,11 @@
 	}
 
 cleanup:
-	if (error < 0)
+	if (error < 0) {
 		git__free(obj->data);
+		if (cached)
+			git_atomic_dec(&cached->refcount);
+	}
 
 	if (elem)
 		*obj_offset = curpos;
@@ -1268,8 +1273,8 @@
 	const git_oid *short_oid,
 	size_t len)
 {
-	const uint32_t *level1_ofs = p->index_map.data;
-	const unsigned char *index = p->index_map.data;
+	const uint32_t *level1_ofs;
+	const unsigned char *index;
 	unsigned hi, lo, stride;
 	int pos, found = 0;
 	git_off_t offset;
@@ -1283,11 +1288,11 @@
 		if ((error = pack_index_open(p)) < 0)
 			return error;
 		assert(p->index_map.data);
-
-		index = p->index_map.data;
-		level1_ofs = p->index_map.data;
 	}
 
+	index = p->index_map.data;
+	level1_ofs = p->index_map.data;
+
 	if (p->index_version > 1) {
 		level1_ofs += 2;
 		index += 8;
diff --git a/src/patch_generate.c b/src/patch_generate.c
index a13f2ff..0e5d1db 100644
--- a/src/patch_generate.c
+++ b/src/patch_generate.c
@@ -284,7 +284,7 @@
 	size_t b_datalen)
 {
 	git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT;
-	size_t delta_data_len;
+	size_t delta_data_len = 0;
 	int error;
 
 	/* The git_delta function accepts unsigned long only */
diff --git a/src/patch_parse.c b/src/patch_parse.c
index 5ee09ee..f527594 100644
--- a/src/patch_parse.c
+++ b/src/patch_parse.c
@@ -176,7 +176,7 @@
 	int ret;
 
 	if (ctx->line_len < 1 || !git__isdigit(ctx->line[0]))
-		return parse_err("invalid file mode at line %d", ctx->line_num);
+		return parse_err("invalid file mode at line %"PRIuZ, ctx->line_num);
 
 	if ((ret = git__strntol32(&m, ctx->line, ctx->line_len, &end, 8)) < 0)
 		return ret;
@@ -205,7 +205,7 @@
 
 	if (len < GIT_OID_MINPREFIXLEN || len > GIT_OID_HEXSZ ||
 		git_oid_fromstrn(oid, ctx->line, len) < 0)
-		return parse_err("invalid hex formatted object id at line %d",
+		return parse_err("invalid hex formatted object id at line %"PRIuZ,
 			ctx->line_num);
 
 	parse_advance_chars(ctx, len);
@@ -350,7 +350,7 @@
 	git_patch_parsed *patch, git_patch_parse_ctx *ctx)
 {
 	if (parse_header_percent(&patch->base.delta->similarity, ctx) < 0)
-		return parse_err("invalid similarity percentage at line %d",
+		return parse_err("invalid similarity percentage at line %"PRIuZ,
 			ctx->line_num);
 
 	return 0;
@@ -362,7 +362,7 @@
 	uint16_t dissimilarity;
 
 	if (parse_header_percent(&dissimilarity, ctx) < 0)
-		return parse_err("invalid similarity percentage at line %d",
+		return parse_err("invalid similarity percentage at line %"PRIuZ,
 			ctx->line_num);
 
 	patch->base.delta->similarity = 100 - dissimilarity;
@@ -406,15 +406,15 @@
 
 	/* Parse the diff --git line */
 	if (parse_advance_expected_str(ctx, "diff --git ") < 0)
-		return parse_err("corrupt git diff header at line %d", ctx->line_num);
+		return parse_err("corrupt git diff header at line %"PRIuZ, ctx->line_num);
 
 	if (parse_header_path(&patch->header_old_path, ctx) < 0)
-		return parse_err("corrupt old path in git diff header at line %d",
+		return parse_err("corrupt old path in git diff header at line %"PRIuZ,
 			ctx->line_num);
 
 	if (parse_advance_ws(ctx) < 0 ||
 		parse_header_path(&patch->header_new_path, ctx) < 0)
-		return parse_err("corrupt new path in git diff header at line %d",
+		return parse_err("corrupt new path in git diff header at line %"PRIuZ,
 			ctx->line_num);
 
 	/* Parse remaining header lines */
@@ -447,7 +447,7 @@
 			parse_advance_expected_str(ctx, "\n");
 
 			if (ctx->line_len > 0) {
-				error = parse_err("trailing data at line %d", ctx->line_num);
+				error = parse_err("trailing data at line %"PRIuZ, ctx->line_num);
 				goto done;
 			}
 
@@ -456,7 +456,7 @@
 		}
 		
 		if (!found) {
-			error = parse_err("invalid patch header at line %d",
+			error = parse_err("invalid patch header at line %"PRIuZ,
 				ctx->line_num);
 			goto done;
 		}
@@ -536,7 +536,7 @@
 
 	hunk->hunk.header_len = ctx->line - header_start;
 	if (hunk->hunk.header_len > (GIT_DIFF_HUNK_HEADER_SIZE - 1))
-		return parse_err("oversized patch hunk header at line %d",
+		return parse_err("oversized patch hunk header at line %"PRIuZ,
 			ctx->line_num);
 
 	memcpy(hunk->hunk.header, header_start, hunk->hunk.header_len);
@@ -545,7 +545,7 @@
 	return 0;
 
 fail:
-	giterr_set(GITERR_PATCH, "invalid patch hunk header at line %d",
+	giterr_set(GITERR_PATCH, "invalid patch hunk header at line %"PRIuZ,
 		ctx->line_num);
 	return -1;
 }
@@ -570,7 +570,7 @@
 		int prefix = 1;
 
 		if (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\n') {
-			error = parse_err("invalid patch instruction at line %d",
+			error = parse_err("invalid patch instruction at line %"PRIuZ,
 				ctx->line_num);
 			goto done;
 		}
@@ -596,7 +596,7 @@
 			break;
 
 		default:
-			error = parse_err("invalid patch hunk at line %d", ctx->line_num);
+			error = parse_err("invalid patch hunk at line %"PRIuZ, ctx->line_num);
 			goto done;
 		}
 
@@ -672,7 +672,7 @@
 				continue;
 			}
 
-			error = parse_err("invalid hunk header outside patch at line %d",
+			error = parse_err("invalid hunk header outside patch at line %"PRIuZ,
 				line_num);
 			goto done;
 		}
@@ -715,12 +715,12 @@
 		parse_advance_chars(ctx, 6);
 	} else {
 		error = parse_err(
-			"unknown binary delta type at line %d", ctx->line_num);
+			"unknown binary delta type at line %"PRIuZ, ctx->line_num);
 		goto done;
 	}
 
 	if (parse_number(&len, ctx) < 0 || parse_advance_nl(ctx) < 0 || len < 0) {
-		error = parse_err("invalid binary size at line %d", ctx->line_num);
+		error = parse_err("invalid binary size at line %"PRIuZ, ctx->line_num);
 		goto done;
 	}
 
@@ -736,7 +736,7 @@
 			decoded_len = c - 'a' + (('z' - 'a') + 1) + 1;
 
 		if (!decoded_len) {
-			error = parse_err("invalid binary length at line %d", ctx->line_num);
+			error = parse_err("invalid binary length at line %"PRIuZ, ctx->line_num);
 			goto done;
 		}
 
@@ -745,7 +745,7 @@
 		encoded_len = ((decoded_len / 4) + !!(decoded_len % 4)) * 5;
 
 		if (encoded_len > ctx->line_len - 1) {
-			error = parse_err("truncated binary data at line %d", ctx->line_num);
+			error = parse_err("truncated binary data at line %"PRIuZ, ctx->line_num);
 			goto done;
 		}
 
@@ -754,14 +754,14 @@
 			goto done;
 
 		if (decoded.size - decoded_orig != decoded_len) {
-			error = parse_err("truncated binary data at line %d", ctx->line_num);
+			error = parse_err("truncated binary data at line %"PRIuZ, ctx->line_num);
 			goto done;
 		}
 
 		parse_advance_chars(ctx, encoded_len);
 
 		if (parse_advance_nl(ctx) < 0) {
-			error = parse_err("trailing data at line %d", ctx->line_num);
+			error = parse_err("trailing data at line %"PRIuZ, ctx->line_num);
 			goto done;
 		}
 	}
@@ -785,7 +785,7 @@
 
 	if (parse_advance_expected_str(ctx, "GIT binary patch") < 0 ||
 		parse_advance_nl(ctx) < 0)
-		return parse_err("corrupt git binary header at line %d", ctx->line_num);
+		return parse_err("corrupt git binary header at line %"PRIuZ, ctx->line_num);
 
 	/* parse old->new binary diff */
 	if ((error = parse_patch_binary_side(
@@ -793,7 +793,7 @@
 		return error;
 
 	if (parse_advance_nl(ctx) < 0)
-		return parse_err("corrupt git binary separator at line %d",
+		return parse_err("corrupt git binary separator at line %"PRIuZ,
 			ctx->line_num);
 
 	/* parse new->old binary diff */
@@ -802,7 +802,7 @@
 		return error;
 
 	if (parse_advance_nl(ctx) < 0)
-		return parse_err("corrupt git binary patch separator at line %d",
+		return parse_err("corrupt git binary patch separator at line %"PRIuZ,
 			ctx->line_num);
 
 	patch->base.binary.contains_data = 1;
@@ -820,7 +820,7 @@
 		parse_advance_expected_str(ctx, patch->header_new_path) < 0 ||
 		parse_advance_expected_str(ctx, " differ") < 0 ||
 		parse_advance_nl(ctx) < 0)
-		return parse_err("corrupt git binary header at line %d", ctx->line_num);
+		return parse_err("corrupt git binary header at line %"PRIuZ, ctx->line_num);
 
 	patch->base.binary.contains_data = 0;
 	patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
@@ -912,7 +912,7 @@
 
 	if (remain_len || !*path)
 		return parse_err(
-			"header filename does not contain %d path components",
+			"header filename does not contain %"PRIuZ" path components",
 			prefix_len);
 
 done:
@@ -1014,8 +1014,10 @@
 		return NULL;
 
 	if (content_len) {
-		if ((ctx->content = git__malloc(content_len)) == NULL)
+		if ((ctx->content = git__malloc(content_len)) == NULL) {
+			git__free(ctx);
 			return NULL;
+		}
 
 		memcpy((char *)ctx->content, content, content_len);
 	}
diff --git a/src/path.c b/src/path.c
index e5f04a5..7675527 100644
--- a/src/path.c
+++ b/src/path.c
@@ -644,6 +644,10 @@
 		giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path);
 		return GIT_EEXISTS;
 
+	case EACCES:
+		giterr_set(GITERR_OS, "Failed %s - '%s' is locked", action, path);
+		return GIT_ELOCKED;
+
 	default:
 		giterr_set(GITERR_OS, "Could not %s '%s'", action, path);
 		return -1;
@@ -1141,7 +1145,6 @@
 	unsigned int flags)
 {
 	git_win32_path path_filter;
-	git_buf hack = {0};
 
 	static int is_win7_or_later = -1;
 	if (is_win7_or_later < 0)
@@ -1347,7 +1350,7 @@
 				return GIT_ITEROVER;
 
 			giterr_set(GITERR_OS,
-				"Could not read directory '%s'", diriter->path);
+				"Could not read directory '%s'", diriter->path.ptr);
 			return -1;
 		}
 	} while (skip_dot && git_path_is_dot_or_dotdot(de->d_name));
diff --git a/src/pqueue.c b/src/pqueue.c
index 8cfc439..9341d1a 100644
--- a/src/pqueue.c
+++ b/src/pqueue.c
@@ -86,8 +86,9 @@
 	if ((pq->flags & GIT_PQUEUE_FIXED_SIZE) != 0 &&
 		pq->length >= pq->_alloc_size)
 	{
-		/* skip this item if below min item in heap */
-		if (pq->_cmp(item, git_vector_get(pq, 0)) <= 0)
+		/* skip this item if below min item in heap or if
+		 * we do not have a comparison function */
+		if (!pq->_cmp || pq->_cmp(item, git_vector_get(pq, 0)) <= 0)
 			return 0;
 		/* otherwise remove the min item before inserting new */
 		(void)git_pqueue_pop(pq);
diff --git a/src/rebase.c b/src/rebase.c
index e86312e..0af2b3c 100644
--- a/src/rebase.c
+++ b/src/rebase.c
@@ -630,7 +630,7 @@
 	rebase->state_path = git_buf_detach(&state_path);
 	GITERR_CHECK_ALLOC(rebase->state_path);
 
-	if (branch->ref_name) {
+	if (branch->ref_name && strcmp(branch->ref_name, "HEAD")) {
 		rebase->orig_head_name = git__strdup(branch->ref_name);
 		GITERR_CHECK_ALLOC(rebase->orig_head_name);
 	} else {
diff --git a/src/refdb.c b/src/refdb.c
index debba12..85c8489 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -125,13 +125,15 @@
 
 int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob)
 {
+	int error;
+
 	if (!db->backend || !db->backend->iterator) {
 		giterr_set(GITERR_REFERENCE, "This backend doesn't support iterators");
 		return -1;
 	}
 
-	if (db->backend->iterator(out, db->backend, glob) < 0)
-		return -1;
+	if ((error = db->backend->iterator(out, db->backend, glob)) < 0)
+		return error;
 
 	GIT_REFCOUNT_INC(db);
 	(*out)->db = db;
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index f978038..8739d5b 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -326,12 +326,13 @@
 {
 	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 	git_buf ref_path = GIT_BUF_INIT;
+	int error;
 
 	assert(backend);
 
-	if (packed_reload(backend) < 0 ||
-		git_buf_joinpath(&ref_path, backend->path, ref_name) < 0)
-		return -1;
+	if ((error = packed_reload(backend)) < 0 ||
+		(error = git_buf_joinpath(&ref_path, backend->path, ref_name)) < 0)
+		return error;
 
 	*exists = git_path_isfile(ref_path.ptr) ||
 		(git_sortedcache_lookup(backend->refcache, ref_name) != NULL);
@@ -409,8 +410,8 @@
 	int error = 0;
 	struct packref *entry;
 
-	if (packed_reload(backend) < 0)
-		return -1;
+	if ((error = packed_reload(backend)) < 0)
+		return error;
 
 	if (git_sortedcache_rlock(backend->refcache) < 0)
 		return -1;
@@ -615,13 +616,14 @@
 static int refdb_fs_backend__iterator(
 	git_reference_iterator **out, git_refdb_backend *_backend, const char *glob)
 {
+	int error;
 	refdb_fs_iter *iter;
 	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 
 	assert(backend);
 
-	if (packed_reload(backend) < 0)
-		return -1;
+	if ((error = packed_reload(backend)) < 0)
+		return error;
 
 	iter = git__calloc(1, sizeof(refdb_fs_iter));
 	GITERR_CHECK_ALLOC(iter);
@@ -674,16 +676,18 @@
 	int force)
 {
 	size_t i;
+	int error;
 
-	if (packed_reload(backend) < 0)
-		return -1;
+	if ((error = packed_reload(backend)) < 0)
+		return error;
 
 	if (!force) {
 		int exists;
 
-		if (refdb_fs_backend__exists(
-				&exists, (git_refdb_backend *)backend, new_ref) < 0)
-			return -1;
+		if ((error = refdb_fs_backend__exists(
+			&exists, (git_refdb_backend *)backend, new_ref)) < 0) {
+			return error;
+		}
 
 		if (exists) {
 			giterr_set(GITERR_REFERENCE,
@@ -725,8 +729,8 @@
 	/* Remove a possibly existing empty directory hierarchy
 	 * which name would collide with the reference name
 	 */
-	if (git_futils_rmdir_r(name, backend->path, GIT_RMDIR_SKIP_NONEMPTY) < 0)
-		return -1;
+	if ((error = git_futils_rmdir_r(name, backend->path, GIT_RMDIR_SKIP_NONEMPTY)) < 0)
+		return error;
 
 	if (git_buf_joinpath(&ref_path, backend->path, name) < 0)
 		return -1;
@@ -901,40 +905,62 @@
 static int packed_remove_loose(refdb_fs_backend *backend)
 {
 	size_t i;
-	git_buf full_path = GIT_BUF_INIT;
-	int failed = 0;
+	git_filebuf lock = GIT_FILEBUF_INIT;
+	git_buf ref_content = GIT_BUF_INIT;
+	int error = 0;
 
 	/* backend->refcache is already locked when this is called */
 
 	for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
 		struct packref *ref = git_sortedcache_entry(backend->refcache, i);
+		git_oid current_id;
 
 		if (!ref || !(ref->flags & PACKREF_WAS_LOOSE))
 			continue;
 
-		if (git_buf_joinpath(&full_path, backend->path, ref->name) < 0)
-			return -1; /* critical; do not try to recover on oom */
+		git_filebuf_cleanup(&lock);
 
-		if (git_path_exists(full_path.ptr) && p_unlink(full_path.ptr) < 0) {
-			if (failed)
-				continue;
+		/* We need to stop anybody from updating the ref while we try to do a safe delete */
+		error = loose_lock(&lock, backend, ref->name);
+		/* If someone else is updating it, let them do it */
+		if (error == GIT_EEXISTS || error == GIT_ENOTFOUND)
+			continue;
 
-			giterr_set(GITERR_REFERENCE,
-				"Failed to remove loose reference '%s' after packing: %s",
-				full_path.ptr, strerror(errno));
-			failed = 1;
+		if (error < 0) {
+			git_buf_free(&ref_content);
+			giterr_set(GITERR_REFERENCE, "failed to lock loose reference '%s'", ref->name);
+			return error;
 		}
 
+		error = git_futils_readbuffer(&ref_content, lock.path_original);
+		/* Someone else beat us to cleaning up the ref, let's simply continue */
+		if (error == GIT_ENOTFOUND)
+			continue;
+
+		/* This became a symref between us packing and trying to delete it, so ignore it */
+		if (!git__prefixcmp(ref_content.ptr, GIT_SYMREF))
+			continue;
+
+		/* Figure out the current id; if we find a bad ref file, skip it so we can do the rest */
+		if (loose_parse_oid(&current_id, lock.path_original, &ref_content) < 0)
+			continue;
+
+		/* If the ref moved since we packed it, we must not delete it */
+		if (!git_oid_equal(&current_id, &ref->oid))
+			continue;
+
 		/*
 		 * if we fail to remove a single file, this is *not* good,
 		 * but we should keep going and remove as many as possible.
-		 * After we've removed as many files as possible, we return
-		 * the error code anyway.
+		 * If we fail to remove, the ref is still in the old state, so
+		 * we haven't lost information.
 		 */
+		p_unlink(lock.path_original);
 	}
 
-	git_buf_free(&full_path);
-	return failed ? -1 : 0;
+	git_buf_free(&ref_content);
+	git_filebuf_cleanup(&lock);
+	return 0;
 }
 
 /*
@@ -944,41 +970,42 @@
 {
 	git_sortedcache *refcache = backend->refcache;
 	git_filebuf pack_file = GIT_FILEBUF_INIT;
+	int error;
 	size_t i;
 
 	/* lock the cache to updates while we do this */
-	if (git_sortedcache_wlock(refcache) < 0)
-		return -1;
+	if ((error = git_sortedcache_wlock(refcache)) < 0)
+		return error;
 
 	/* Open the file! */
-	if (git_filebuf_open(&pack_file, git_sortedcache_path(refcache), 0, GIT_PACKEDREFS_FILE_MODE) < 0)
+	if ((error = git_filebuf_open(&pack_file, git_sortedcache_path(refcache), 0, GIT_PACKEDREFS_FILE_MODE)) < 0)
 		goto fail;
 
 	/* Packfiles have a header... apparently
 	 * This is in fact not required, but we might as well print it
 	 * just for kicks */
-	if (git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER) < 0)
+	if ((error = git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < 0)
 		goto fail;
 
 	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)
+		if ((error = packed_find_peel(backend, ref)) < 0)
 			goto fail;
 
-		if (packed_write_ref(ref, &pack_file) < 0)
+		if ((error = packed_write_ref(ref, &pack_file)) < 0)
 			goto fail;
 	}
 
 	/* if we've written all the references properly, we can commit
 	 * the packfile to make the changes effective */
-	if (git_filebuf_commit(&pack_file) < 0)
+	if ((error = git_filebuf_commit(&pack_file)) < 0)
 		goto fail;
 
 	/* when and only when the packfile has been properly written,
 	 * we can go ahead and remove the loose refs */
-	if (packed_remove_loose(backend) < 0)
+	if ((error = packed_remove_loose(backend)) < 0)
 		goto fail;
 
 	git_sortedcache_updated(refcache);
@@ -991,7 +1018,7 @@
 	git_filebuf_cleanup(&pack_file);
 	git_sortedcache_wunlock(refcache);
 
-	return -1;
+	return error;
 }
 
 static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *author, const char *message);
@@ -1143,8 +1170,7 @@
 
 	assert(backend);
 
-	error = reference_path_available(backend, ref->name, NULL, force);
-	if (error < 0)
+	if ((error = reference_path_available(backend, ref->name, NULL, force)) < 0)
 		return error;
 
 	/* We need to perform the reflog append and old value check under the ref's lock */
@@ -1260,15 +1286,14 @@
 	if (git_buf_joinpath(&loose_path, backend->path, ref_name) < 0)
 		return -1;
 
-	if (git_path_isfile(loose_path.ptr)) {
-		error = p_unlink(loose_path.ptr);
-		loose_deleted = 1;
-	}
 
-	git_buf_free(&loose_path);
-
-	if (error != 0)
+	error = p_unlink(loose_path.ptr);
+	if (error < 0 && errno == ENOENT)
+		error = 0;
+	else if (error < 0)
 		goto cleanup;
+	else if (error == 0)
+		loose_deleted = 1;
 
 	if ((error = packed_reload(backend)) < 0)
 		goto cleanup;
@@ -1291,6 +1316,7 @@
 	error = packed_write(backend);
 
 cleanup:
+	git_buf_free(&loose_path);
 	git_filebuf_cleanup(file);
 
 	return error;
@@ -1362,14 +1388,15 @@
 
 static int refdb_fs_backend__compress(git_refdb_backend *_backend)
 {
+	int error;
 	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 
 	assert(backend);
 
-	if (packed_reload(backend) < 0 || /* load the existing packfile */
-		packed_loadloose(backend) < 0 || /* add all the loose refs */
-		packed_write(backend) < 0) /* write back to disk */
-		return -1;
+	if ((error = packed_reload(backend)) < 0 || /* load the existing packfile */
+	    (error = packed_loadloose(backend)) < 0 || /* add all the loose refs */
+	    (error = packed_write(backend)) < 0) /* write back to disk */
+		return error;
 
 	return 0;
 }
@@ -1789,9 +1816,10 @@
 	 * there maybe an obsolete/unused directory (or directory hierarchy) in the way.
 	 */
 	if (git_path_isdir(git_buf_cstr(&path))) {
-		if ((git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0))
-			error = -1;
-		else if (git_path_isdir(git_buf_cstr(&path))) {
+		if ((error = git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY)) < 0) {
+			if (error == GIT_ENOTFOUND)
+				error = 0;
+		} else if (git_path_isdir(git_buf_cstr(&path))) {
 			giterr_set(GITERR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder",
 				ref->name);
 			error = GIT_EDIRECTORY;
diff --git a/src/repository.c b/src/repository.c
index cf3d18a..5c44423 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -410,7 +410,7 @@
 					break;
 				}
 			}
-			else if (S_ISREG(st.st_mode)) {
+			else if (S_ISREG(st.st_mode) && git__suffixcmp(path.ptr, "/" DOT_GIT) == 0) {
 				error = read_gitfile(&repo_link, path.ptr);
 				if (error < 0)
 					break;
@@ -613,9 +613,10 @@
 		git_repository_set_odb(repo, odb);
 
 	error = git__getenv(&alts_buf, "GIT_ALTERNATE_OBJECT_DIRECTORIES");
-	if (error == GIT_ENOTFOUND)
+	if (error == GIT_ENOTFOUND) {
 		giterr_clear();
-	else if (error < 0)
+		error = 0;
+	} else if (error < 0)
 		goto error;
         else {
 		const char *end;
@@ -638,9 +639,11 @@
 		}
 	}
 
-	error = git_repository_set_namespace(repo, git_buf_cstr(&namespace_buf));
-	if (error < 0)
-		goto error;
+	if (git_buf_len(&namespace_buf)) {
+		error = git_repository_set_namespace(repo, git_buf_cstr(&namespace_buf));
+		if (error < 0)
+			goto error;
+	}
 
 	git_repository_set_index(repo, index);
 
diff --git a/src/revwalk.c b/src/revwalk.c
index 0ada587..f5502a7 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -290,7 +290,7 @@
 
 
 	while (parents) {
-		git_commit_list_node *commit = git_commit_list_pop(&parents);
+		commit = git_commit_list_pop(&parents);
 
 		while (commit) {
 			if (commit->uninteresting)
diff --git a/src/settings.c b/src/settings.c
index cb2317f..4a6e0f3 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -29,7 +29,9 @@
 #ifdef GIT_THREADS
 		| GIT_FEATURE_THREADS
 #endif
+#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
 		| GIT_FEATURE_HTTPS
+#endif
 #if defined(GIT_SSH)
 		| GIT_FEATURE_SSH
 #endif
diff --git a/src/signature.c b/src/signature.c
index dcc3797..22cba7e 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -251,7 +251,7 @@
 			 * only store timezone if it's not overflowing;
 			 * see http://www.worldtimezone.com/faq.html
 			 */
-			if (hours < 14 && mins < 59) {
+			if (hours <= 14 && mins <= 59) {
 				sig->when.offset = (hours * 60) + mins;
 				if (tz_start[0] == '-')
 					sig->when.offset = -sig->when.offset;
diff --git a/src/sortedcache.c b/src/sortedcache.c
index 5c2a167..5bd989a 100644
--- a/src/sortedcache.c
+++ b/src/sortedcache.c
@@ -200,6 +200,7 @@
 int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf)
 {
 	int error, fd;
+	struct stat st;
 
 	if ((error = git_sortedcache_wlock(sc)) < 0)
 		return error;
@@ -207,19 +208,27 @@
 	if ((error = git_futils_filestamp_check(&sc->stamp, sc->path)) <= 0)
 		goto unlock;
 
-	if (!git__is_sizet(sc->stamp.size)) {
-		giterr_set(GITERR_INVALID, "Unable to load file larger than size_t");
-		error = -1;
-		goto unlock;
-	}
-
 	if ((fd = git_futils_open_ro(sc->path)) < 0) {
 		error = fd;
 		goto unlock;
 	}
 
+	if (p_fstat(fd, &st) < 0) {
+		giterr_set(GITERR_OS, "failed to stat file");
+		error = -1;
+		(void)p_close(fd);
+		goto unlock;
+	}
+
+	if (!git__is_sizet(st.st_size)) {
+		giterr_set(GITERR_INVALID, "Unable to load file larger than size_t");
+		error = -1;
+		(void)p_close(fd);
+		goto unlock;
+	}
+
 	if (buf)
-		error = git_futils_readbuffer_fd(buf, fd, (size_t)sc->stamp.size);
+		error = git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size);
 
 	(void)p_close(fd);
 
diff --git a/src/sysdir.c b/src/sysdir.c
index 29e53e2..e89db76 100644
--- a/src/sysdir.c
+++ b/src/sysdir.c
@@ -171,7 +171,7 @@
 		expand_path = strstr(search_path, PATH_MAGIC);
 
 	/* reset the default if this path has been cleared */
-	if (!search_path || expand_path)
+	if (!search_path)
 		git_sysdir__dirs[which].guess(&git_sysdir__dirs[which].buf);
 
 	/* if $PATH is not referenced, then just set the path */
diff --git a/src/transports/http.c b/src/transports/http.c
index ca1f504..ad28c58 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -208,7 +208,7 @@
 
 	git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
 
-	git_buf_printf(buf, "User-Agent: git/1.0 (%s)\r\n", user_agent());
+	git_buf_printf(buf, "User-Agent: git/2.0 (%s)\r\n", user_agent());
 	git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
 
 	if (s->chunked || content_length > 0) {
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 3448fa7..53c0b08 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -50,7 +50,7 @@
 			if ((recvd = gitno_recv(buf)) < 0)
 				return recvd;
 
-			if (recvd == 0 && !flush) {
+			if (recvd == 0) {
 				giterr_set(GITERR_NET, "early EOF");
 				return GIT_EEOF;
 			}
@@ -222,8 +222,12 @@
 		if (error < 0 && error != GIT_EBUFS)
 			return error;
 
-		if ((ret = gitno_recv(buf)) < 0)
+		if ((ret = gitno_recv(buf)) < 0) {
 			return ret;
+		} else if (ret == 0) {
+			giterr_set(GITERR_NET, "early EOF");
+			return GIT_EEOF;
+		}
 	} while (error);
 
 	gitno_consume(buf, line_end);
@@ -408,12 +412,12 @@
 
 		if (i % 20 == 0 && t->rpc) {
 			git_pkt_ack *pkt;
-			unsigned int i;
+			unsigned int j;
 
 			if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
 				goto on_error;
 
-			git_vector_foreach(&t->common, i, pkt) {
+			git_vector_foreach(&t->common, j, pkt) {
 				if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
 					goto on_error;
 			}
@@ -428,12 +432,12 @@
 	/* Tell the other end that we're done negotiating */
 	if (t->rpc && t->common.length > 0) {
 		git_pkt_ack *pkt;
-		unsigned int i;
+		unsigned int j;
 
 		if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
 			goto on_error;
 
-		git_vector_foreach(&t->common, i, pkt) {
+		git_vector_foreach(&t->common, j, pkt) {
 			if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
 				goto on_error;
 		}
@@ -724,7 +728,7 @@
 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, *line_end;
+	const char *line, *line_end = NULL;
 	size_t line_len;
 	int error;
 	int reading_from_buf = data_pkt_buf->size > 0;
diff --git a/src/tree.c b/src/tree.c
index 6008a95..9655ad7 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -917,7 +917,7 @@
 
 	if (entry == NULL) {
 		giterr_set(GITERR_TREE,
-			   "the path '%.*s' does not exist in the given tree", filename_len, path);
+			   "the path '%.*s' does not exist in the given tree", (int) filename_len, path);
 		return GIT_ENOTFOUND;
 	}
 
@@ -927,7 +927,7 @@
 		 * then this entry *must* be a tree */
 		if (!git_tree_entry__is_tree(entry)) {
 			giterr_set(GITERR_TREE,
-				   "the path '%.*s' exists but is not a tree", filename_len, path);
+				   "the path '%.*s' exists but is not a tree", (int) filename_len, path);
 			return GIT_ENOTFOUND;
 		}
 
@@ -1164,8 +1164,8 @@
 		goto cleanup;
 
 	for (i = 0; i < nupdates; i++) {
-		const git_tree_update *last_update = i == 0 ? NULL : &updates[i-1];
-		const git_tree_update *update = &updates[i];
+		const git_tree_update *last_update = i == 0 ? NULL : git_vector_get(&entries, i-1);
+		const git_tree_update *update = git_vector_get(&entries, i);
 		size_t common_prefix = 0, steps_up, j;
 		const char *path;
 
@@ -1200,6 +1200,9 @@
 
 			last = git_array_last(stack);
 			entry = last->tree ? git_tree_entry_byname(last->tree, component.ptr) : NULL;
+			if (!entry)
+				entry = treebuilder_get(last->bld, component.ptr);
+
 			if (entry && git_tree_entry_type(entry) != GIT_OBJ_TREE) {
 				giterr_set(GITERR_TREE, "D/F conflict when updating tree");
 				error = -1;
diff --git a/src/unix/pthread.h b/src/unix/pthread.h
index 0f3f179..3f23d10 100644
--- a/src/unix/pthread.h
+++ b/src/unix/pthread.h
@@ -17,6 +17,8 @@
 	pthread_create(&(git_thread_ptr)->thread, NULL, start_routine, arg)
 #define git_thread_join(git_thread_ptr, status) \
 	pthread_join((git_thread_ptr)->thread, status)
+#define git_thread_currentid() ((size_t)(pthread_self()))
+#define git_thread_exit(retval) pthread_exit(retval)
 
 /* Git Mutex */
 #define git_mutex pthread_mutex_t
diff --git a/src/win32/thread.c b/src/win32/thread.c
index 80d56ce..87318c9 100644
--- a/src/win32/thread.c
+++ b/src/win32/thread.c
@@ -26,6 +26,9 @@
 {
 	git_thread *thread = lpParameter;
 
+	/* Set the current thread for `git_thread_exit` */
+	GIT_GLOBAL->current_thread = thread;
+
 	thread->result = thread->proc(thread->param);
 
 	git__free_tls_data();
@@ -95,6 +98,21 @@
 	return 0;
 }
 
+void git_thread_exit(void *value)
+{
+	assert(GIT_GLOBAL->current_thread);
+	GIT_GLOBAL->current_thread->result = value;
+
+	git__free_tls_data();
+
+	ExitThread(CLEAN_THREAD_EXIT);
+}
+
+size_t git_thread_currentid(void)
+{
+	return GetCurrentThreadId();
+}
+
 int git_mutex_init(git_mutex *GIT_RESTRICT mutex)
 {
 	InitializeCriticalSection(mutex);
diff --git a/src/win32/thread.h b/src/win32/thread.h
index 0d01822..7f4a217 100644
--- a/src/win32/thread.h
+++ b/src/win32/thread.h
@@ -41,6 +41,8 @@
 	void *(*) (void *),
 	void *GIT_RESTRICT);
 int git_thread_join(git_thread *, void **);
+size_t git_thread_currentid(void);
+void git_thread_exit(void *);
 
 int git_mutex_init(git_mutex *GIT_RESTRICT mutex);
 int git_mutex_free(git_mutex *);
diff --git a/src/win32/w32_util.h b/src/win32/w32_util.h
index 2e475e5..784a7a0 100644
--- a/src/win32/w32_util.h
+++ b/src/win32/w32_util.h
@@ -174,7 +174,7 @@
 			/* st_size gets the UTF-8 length of the target name, in bytes,
 			 * not counting the NULL terminator */
 			if ((st->st_size = git__utf16_to_8(NULL, 0, target)) < 0) {
-				giterr_set(GITERR_OS, "Could not convert reparse point name for '%s'", path);
+				giterr_set(GITERR_OS, "Could not convert reparse point name for '%ls'", path);
 				return -1;
 			}
 		}
diff --git a/src/zstream.c b/src/zstream.c
index d9ad4ca..d949aa8 100644
--- a/src/zstream.c
+++ b/src/zstream.c
@@ -21,7 +21,7 @@
 	if (zs->zerr == Z_MEM_ERROR)
 		giterr_set_oom();
 	else if (zs->z.msg)
-		giterr_set(GITERR_ZLIB, zs->z.msg);
+		giterr_set_str(GITERR_ZLIB, zs->z.msg);
 	else
 		giterr_set(GITERR_ZLIB, "Unknown compression error");
 
diff --git a/tests/clar_libgit2.h b/tests/clar_libgit2.h
index d7e6353..fc08bbf 100644
--- a/tests/clar_libgit2.h
+++ b/tests/clar_libgit2.h
@@ -41,6 +41,51 @@
 	} \
 	} while(0)
 
+/**
+ * Thread safe assertions; you cannot use `cl_git_report_failure` from a
+ * child thread since it will try to `longjmp` to abort and "the effect of
+ * a call to longjmp() where initialization of the jmp_buf structure was
+ * not performed in the calling thread is undefined."
+ *
+ * Instead, callers can provide a clar thread error context to a thread,
+ * which will populate and return it on failure.  Callers can check the
+ * status with `cl_git_thread_check`.
+ */
+typedef struct {
+	int error;
+	const char *file;
+	int line;
+	const char *expr;
+	char error_msg[4096];
+} cl_git_thread_err;
+
+#ifdef GIT_THREADS
+# define cl_git_thread_pass(threaderr, expr) cl_git_thread_pass_(threaderr, (expr), __FILE__, __LINE__)
+#else
+# define cl_git_thread_pass(threaderr, expr) cl_git_pass(expr)
+#endif
+
+#define cl_git_thread_pass_(__threaderr, __expr, __file, __line) do { \
+	giterr_clear(); \
+	if ((((cl_git_thread_err *)__threaderr)->error = (__expr)) != 0) { \
+		const git_error *_last = giterr_last(); \
+		((cl_git_thread_err *)__threaderr)->file = __file; \
+		((cl_git_thread_err *)__threaderr)->line = __line; \
+		((cl_git_thread_err *)__threaderr)->expr = "Function call failed: " #__expr; \
+		p_snprintf(((cl_git_thread_err *)__threaderr)->error_msg, 4096, "thread 0x%" PRIxZ " - error %d - %s", \
+			git_thread_currentid(), ((cl_git_thread_err *)__threaderr)->error, \
+			_last ? _last->message : "<no message>"); \
+		git_thread_exit(__threaderr); \
+	} \
+	} while (0)
+
+GIT_INLINE(void) cl_git_thread_check(void *data)
+{
+	cl_git_thread_err *threaderr = (cl_git_thread_err *)data;
+	if (threaderr->error != 0)
+		clar__assert(0, threaderr->file, threaderr->line, threaderr->expr, threaderr->error_msg, 1);
+}
+
 void cl_git_report_failure(int, const char *, int, const char *);
 
 #define cl_assert_at_line(expr,file,line) \
diff --git a/tests/core/env.c b/tests/core/env.c
index ee08258..1af0e6e 100644
--- a/tests/core/env.c
+++ b/tests/core/env.c
@@ -298,3 +298,24 @@
 	git_buf_free(&path);
 	git_buf_free(&found);
 }
+
+void test_core_env__substitution(void)
+{
+  git_buf buf = GIT_BUF_INIT, expected = GIT_BUF_INIT;
+
+  /* Set it to something non-default so we have controllable values */
+  cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, "/tmp/a"));
+  cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &buf));
+  cl_assert_equal_s("/tmp/a", buf.ptr);
+
+  git_buf_clear(&buf);
+  cl_git_pass(git_buf_join(&buf, GIT_PATH_LIST_SEPARATOR, "$PATH", "/tmp/b"));
+  cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, buf.ptr));
+  cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &buf));
+
+  cl_git_pass(git_buf_join(&expected, GIT_PATH_LIST_SEPARATOR, "/tmp/a", "/tmp/b"));
+  cl_assert_equal_s(expected.ptr, buf.ptr);
+
+  git_buf_free(&expected);
+  git_buf_free(&buf);
+}
diff --git a/tests/core/init.c b/tests/core/init.c
index e17b784..a8cbd93 100644
--- a/tests/core/init.c
+++ b/tests/core/init.c
@@ -12,3 +12,43 @@
 	cl_assert_equal_i(1, git_libgit2_shutdown());
 }
 
+void test_core_init__reinit_succeeds(void)
+{
+	cl_assert_equal_i(0, git_libgit2_shutdown());
+	cl_assert_equal_i(1, git_libgit2_init());
+	cl_sandbox_set_search_path_defaults();
+}
+
+#ifdef GIT_THREADS
+static void *reinit(void *unused)
+{
+	unsigned i;
+
+	for (i = 0; i < 20; i++) {
+		cl_assert(git_libgit2_init() > 0);
+		cl_assert(git_libgit2_shutdown() >= 0);
+	}
+
+	return unused;
+}
+#endif
+
+void test_core_init__concurrent_init_succeeds(void)
+{
+#ifdef GIT_THREADS
+	git_thread threads[10];
+	unsigned i;
+
+	cl_assert_equal_i(2, git_libgit2_init());
+
+	for (i = 0; i < ARRAY_SIZE(threads); i++)
+		git_thread_create(&threads[i], reinit, NULL);
+	for (i = 0; i < ARRAY_SIZE(threads); i++)
+		git_thread_join(&threads[i], NULL);
+
+	cl_assert_equal_i(1, git_libgit2_shutdown());
+	cl_sandbox_set_search_path_defaults();
+#else
+	cl_skip();
+#endif
+}
diff --git a/tests/core/pqueue.c b/tests/core/pqueue.c
index bcd4eea..2b90f41 100644
--- a/tests/core/pqueue.c
+++ b/tests/core/pqueue.c
@@ -93,7 +93,29 @@
 	cl_assert_equal_i(0, git_pqueue_size(&pq));
 
 	git_pqueue_free(&pq);
+}
 
+void test_core_pqueue__max_heap_size_without_comparison(void)
+{
+	git_pqueue pq;
+	int i, vals[100] = { 0 };
+
+	cl_git_pass(git_pqueue_init(&pq, GIT_PQUEUE_FIXED_SIZE, 50, NULL));
+
+	for (i = 0; i < 100; ++i)
+		cl_git_pass(git_pqueue_insert(&pq, &vals[i]));
+
+	cl_assert_equal_i(50, git_pqueue_size(&pq));
+
+	/* As we have no comparison function, we cannot make any
+	 * actual assumptions about which entries are part of the
+	 * pqueue */
+	for (i = 0; i < 50; ++i)
+		cl_assert(git_pqueue_pop(&pq));
+
+	cl_assert_equal_i(0, git_pqueue_size(&pq));
+
+	git_pqueue_free(&pq);
 }
 
 static int cmp_ints_like_commit_time(const void *a, const void *b)
diff --git a/tests/object/tree/update.c b/tests/object/tree/update.c
index 54c4335..b76e861 100644
--- a/tests/object/tree/update.c
+++ b/tests/object/tree/update.c
@@ -196,6 +196,63 @@
 	git_tree_free(base_tree);
 }
 
+void test_object_tree_update__add_blobs_unsorted(void)
+{
+	git_oid tree_index_id, tree_updater_id, base_id;
+	git_tree *base_tree;
+	git_index *idx;
+	git_index_entry entry = { {0} };
+	int i;
+	const char *paths[] = {
+		"some/deep/path",
+		"a/path/elsewhere",
+		"some/other/path",
+	};
+
+	git_tree_update updates[] = {
+		{ GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_BLOB, paths[0]},
+		{ GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_BLOB, paths[1]},
+		{ GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_BLOB, paths[2]},
+	};
+
+	cl_git_pass(git_oid_fromstr(&base_id, "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b"));
+
+	entry.mode = GIT_FILEMODE_BLOB;
+	cl_git_pass(git_oid_fromstr(&entry.id, "fa49b077972391ad58037050f2a75f74e3671e92"));
+
+	for (i = 0; i < 3; i++) {
+		cl_git_pass(git_oid_fromstr(&updates[i].id, "fa49b077972391ad58037050f2a75f74e3671e92"));
+	}
+
+	for (i = 0; i < 2; i++) {
+		int j;
+
+		/* Create it with an index */
+		cl_git_pass(git_index_new(&idx));
+
+		base_tree = NULL;
+		if (i == 1) {
+			cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
+			cl_git_pass(git_index_read_tree(idx, base_tree));
+		}
+
+		for (j = 0; j < 3; j++) {
+			entry.path = paths[j];
+			cl_git_pass(git_index_add(idx, &entry));
+		}
+
+		cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
+		git_index_free(idx);
+
+		/* Perform the same operations via the tree updater */
+		cl_git_pass(git_tree_create_updated(&tree_updater_id, g_repo, base_tree, 3, updates));
+
+		cl_assert_equal_oid(&tree_index_id, &tree_updater_id);
+	}
+
+	git_tree_free(base_tree);
+}
+
 void test_object_tree_update__add_conflict(void)
 {
 	int i;
diff --git a/tests/rebase/merge.c b/tests/rebase/merge.c
index 0f06ed1..7b2d687 100644
--- a/tests/rebase/merge.c
+++ b/tests/rebase/merge.c
@@ -1,4 +1,5 @@
 #include "clar_libgit2.h"
+#include "git2/checkout.h"
 #include "git2/rebase.h"
 #include "posix.h"
 #include "signature.h"
@@ -475,6 +476,59 @@
 	git_rebase_free(rebase);
 }
 
+void test_rebase_merge__detached_finish(void)
+{
+	git_rebase *rebase;
+	git_reference *branch_ref, *upstream_ref, *head_ref;
+	git_annotated_commit *branch_head, *upstream_head;
+	git_rebase_operation *rebase_operation;
+	git_oid commit_id;
+	git_reflog *reflog;
+	const git_reflog_entry *reflog_entry;
+	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+	int error;
+
+	cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy"));
+	cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+	cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+	cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+	cl_git_pass(git_repository_set_head_detached_from_annotated(repo, branch_head));
+	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+	git_checkout_head(repo, &opts);
+
+	cl_git_pass(git_rebase_init(&rebase, repo, NULL, upstream_head, NULL, NULL));
+
+	cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+	cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+		NULL, NULL));
+
+	cl_git_fail(error = git_rebase_next(&rebase_operation, rebase));
+	cl_assert_equal_i(GIT_ITEROVER, error);
+
+	cl_git_pass(git_rebase_finish(rebase, signature));
+
+	cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+	cl_git_pass(git_reference_lookup(&head_ref, repo, "HEAD"));
+	cl_assert_equal_i(GIT_REF_OID, git_reference_type(head_ref));
+
+	/* Make sure the reflogs are updated appropriately */
+	cl_git_pass(git_reflog_read(&reflog, repo, "HEAD"));
+	cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0));
+	cl_assert_equal_oid(git_annotated_commit_id(upstream_head), git_reflog_entry_id_old(reflog_entry));
+	cl_assert_equal_oid(&commit_id, git_reflog_entry_id_new(reflog_entry));
+
+	git_reflog_free(reflog);
+	git_annotated_commit_free(branch_head);
+	git_annotated_commit_free(upstream_head);
+	git_reference_free(head_ref);
+	git_reference_free(branch_ref);
+	git_reference_free(upstream_ref);
+	git_rebase_free(rebase);
+}
+
 void test_rebase_merge__finish_with_ids(void)
 {
 	git_rebase *rebase;
diff --git a/tests/refs/branches/create.c b/tests/refs/branches/create.c
index 31dec06..69488e6 100644
--- a/tests/refs/branches/create.c
+++ b/tests/refs/branches/create.c
@@ -65,10 +65,14 @@
 	cl_assert_equal_s("refs/heads/br2", git_reference_name(branch));
 }
 
-void test_refs_branches_create__cannot_force_create_over_current_branch(void)
+void test_refs_branches_create__cannot_force_create_over_current_branch_in_nonbare_repo(void)
 {
 	const git_oid *oid;
 	git_reference *branch2;
+
+	/* Default repo for these tests is a bare repo, but this test requires a non-bare one */
+	cl_git_sandbox_cleanup();
+	repo = cl_git_sandbox_init("testrepo");
 	retrieve_known_commit(&target, repo);
 
 	cl_git_pass(git_branch_lookup(&branch2, repo, "master", GIT_BRANCH_LOCAL));
@@ -84,6 +88,26 @@
 	git_reference_free(branch2);
 }
 
+void test_refs_branches_create__can_force_create_over_current_branch_in_bare_repo(void)
+{
+	const git_oid *oid;
+	git_reference *branch2;
+	retrieve_known_commit(&target, repo);
+
+	cl_git_pass(git_branch_lookup(&branch2, repo, "master", GIT_BRANCH_LOCAL));
+	cl_assert_equal_s("refs/heads/master", git_reference_name(branch2));
+	cl_assert_equal_i(true, git_branch_is_head(branch2));
+	oid = git_commit_id(target);
+
+	cl_git_pass(git_branch_create(&branch, repo, "master", target, 1));
+	git_reference_free(branch);
+	branch = NULL;
+	cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL));
+	cl_assert_equal_s("refs/heads/master", git_reference_name(branch));
+	cl_git_pass(git_oid_cmp(git_reference_target(branch), oid));
+	git_reference_free(branch2);
+}
+
 void test_refs_branches_create__creating_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void)
 {
 	retrieve_known_commit(&target, repo);
diff --git a/tests/repo/discover.c b/tests/repo/discover.c
index 358daee..48aa275 100644
--- a/tests/repo/discover.c
+++ b/tests/repo/discover.c
@@ -9,6 +9,7 @@
 
 #define SUB_REPOSITORY_FOLDER_NAME "sub_repo"
 #define SUB_REPOSITORY_FOLDER DISCOVER_FOLDER "/" SUB_REPOSITORY_FOLDER_NAME
+#define SUB_REPOSITORY_GITDIR SUB_REPOSITORY_FOLDER "/.git"
 #define SUB_REPOSITORY_FOLDER_SUB SUB_REPOSITORY_FOLDER "/sub"
 #define SUB_REPOSITORY_FOLDER_SUB_SUB SUB_REPOSITORY_FOLDER_SUB "/subsub"
 #define SUB_REPOSITORY_FOLDER_SUB_SUB_SUB SUB_REPOSITORY_FOLDER_SUB_SUB "/subsubsub"
@@ -24,20 +25,26 @@
 #define ALTERNATE_NOT_FOUND_FOLDER DISCOVER_FOLDER "/alternate_not_found_repo"
 
 static void ensure_repository_discover(const char *start_path,
-                                       const char *ceiling_dirs,
-				       git_buf *expected_path)
+				       const char *ceiling_dirs,
+				       const char *expected_path)
 {
-	git_buf found_path = GIT_BUF_INIT;
+	git_buf found_path = GIT_BUF_INIT, resolved = GIT_BUF_INIT;
+
+	git_buf_attach(&resolved, p_realpath(expected_path, NULL), 0);
+	cl_assert(resolved.size > 0);
+	cl_git_pass(git_path_to_dir(&resolved));
 	cl_git_pass(git_repository_discover(&found_path, start_path, 0, ceiling_dirs));
-	//across_fs is always 0 as we can't automate the filesystem change tests
-	cl_assert_equal_s(found_path.ptr, expected_path->ptr);
+
+	cl_assert_equal_s(found_path.ptr, resolved.ptr);
+
+	git_buf_free(&resolved);
 	git_buf_free(&found_path);
 }
 
 static void write_file(const char *path, const char *content)
 {
 	git_file file;
-   int error;
+	int error;
 
 	if (git_path_exists(path)) {
 		cl_git_pass(p_unlink(path));
@@ -68,42 +75,30 @@
 	cl_assert(git_buf_oom(ceiling_dirs) == 0);
 }
 
-void test_repo_discover__0(void)
+static git_buf discovered;
+static git_buf ceiling_dirs;
+
+void test_repo_discover__initialize(void)
 {
-	// test discover
 	git_repository *repo;
-	git_buf ceiling_dirs_buf = GIT_BUF_INIT, repository_path = GIT_BUF_INIT,
-		sub_repository_path = GIT_BUF_INIT, found_path = GIT_BUF_INIT;
-	const char *ceiling_dirs;
 	const mode_t mode = 0777;
-
 	git_futils_mkdir_r(DISCOVER_FOLDER, mode);
-	append_ceiling_dir(&ceiling_dirs_buf, TEMP_REPO_FOLDER);
-	ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
 
-	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&repository_path, DISCOVER_FOLDER, 0, ceiling_dirs));
+	git_buf_init(&discovered, 0);
+	git_buf_init(&ceiling_dirs, 0);
+	append_ceiling_dir(&ceiling_dirs, TEMP_REPO_FOLDER);
 
 	cl_git_pass(git_repository_init(&repo, DISCOVER_FOLDER, 1));
-	cl_git_pass(git_repository_discover(&repository_path, DISCOVER_FOLDER, 0, ceiling_dirs));
 	git_repository_free(repo);
 
 	cl_git_pass(git_repository_init(&repo, SUB_REPOSITORY_FOLDER, 0));
 	cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
-	cl_git_pass(git_repository_discover(&sub_repository_path, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
-
 	cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
-	ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, &sub_repository_path);
-	ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs, &sub_repository_path);
-	ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs, &sub_repository_path);
 
 	cl_git_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, mode));
 	write_file(REPOSITORY_ALTERNATE_FOLDER "/" DOT_GIT, "gitdir: ../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT);
 	write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/" DOT_GIT, "gitdir: ../../../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT);
 	write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB "/" DOT_GIT, "gitdir: ../../../../");
-	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, &sub_repository_path);
-	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, &sub_repository_path);
-	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, &sub_repository_path);
-	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, &repository_path);
 
 	cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, mode));
 	write_file(ALTERNATE_MALFORMED_FOLDER1 "/" DOT_GIT, "Anything but not gitdir:");
@@ -113,41 +108,94 @@
 	write_file(ALTERNATE_MALFORMED_FOLDER3 "/" DOT_GIT, "gitdir: \n\n\n");
 	cl_git_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, mode));
 	write_file(ALTERNATE_NOT_FOUND_FOLDER "/" DOT_GIT, "gitdir: a_repository_that_surely_does_not_exist");
-	cl_git_fail(git_repository_discover(&found_path, ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs));
-	cl_git_fail(git_repository_discover(&found_path, ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs));
-	cl_git_fail(git_repository_discover(&found_path, ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs));
-	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs));
 
-	append_ceiling_dir(&ceiling_dirs_buf, SUB_REPOSITORY_FOLDER_SUB);
-	ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
+	git_repository_free(repo);
+}
+
+void test_repo_discover__cleanup(void)
+{
+	git_buf_free(&discovered);
+	git_buf_free(&ceiling_dirs);
+	cl_git_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_repo_discover__discovering_repo_with_exact_path_succeeds(void)
+{
+	cl_git_pass(git_repository_discover(&discovered, DISCOVER_FOLDER, 0, ceiling_dirs.ptr));
+	cl_git_pass(git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs.ptr));
+}
+
+void test_repo_discover__discovering_nonexistent_dir_fails(void)
+{
+	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, DISCOVER_FOLDER "-nonexistent", 0, NULL));
+}
+
+void test_repo_discover__discovering_repo_with_subdirectory_succeeds(void)
+{
+	ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+	ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+	ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+}
+
+void test_repo_discover__discovering_repository_with_alternative_gitdir_succeeds(void)
+{
+	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs.ptr, DISCOVER_FOLDER);
+}
+
+void test_repo_discover__discovering_repository_with_malformed_alternative_gitdir_fails(void)
+{
+	cl_git_fail(git_repository_discover(&discovered, ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs.ptr));
+	cl_git_fail(git_repository_discover(&discovered, ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs.ptr));
+	cl_git_fail(git_repository_discover(&discovered, ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs.ptr));
+	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs.ptr));
+}
+
+void test_repo_discover__discovering_repository_with_ceiling(void)
+{
+	append_ceiling_dir(&ceiling_dirs, SUB_REPOSITORY_FOLDER_SUB);
 
 	/* this must pass as ceiling_directories cannot prevent the current
 	 * working directory to be checked */
-	ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs, &sub_repository_path);
-	ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, &sub_repository_path);
-	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs));
-	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs));
+	ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
 
-	append_ceiling_dir(&ceiling_dirs_buf, SUB_REPOSITORY_FOLDER);
-	ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
-
-	//this must pass as ceiling_directories cannot predent the current
-	//working directory to be checked
-	ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs, &sub_repository_path);
-	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs));
-	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs));
-	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs));
-
-	//.gitfile redirection should not be affected by ceiling directories
-	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, &sub_repository_path);
-	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, &sub_repository_path);
-	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, &sub_repository_path);
-	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, &repository_path);
-
-	cl_git_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, NULL, GIT_RMDIR_REMOVE_FILES));
-	git_repository_free(repo);
-	git_buf_free(&ceiling_dirs_buf);
-	git_buf_free(&repository_path);
-	git_buf_free(&sub_repository_path);
+	ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs.ptr));
+	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs.ptr));
 }
 
+void test_repo_discover__other_ceiling(void)
+{
+	append_ceiling_dir(&ceiling_dirs, SUB_REPOSITORY_FOLDER);
+
+	/* this must pass as ceiling_directories cannot predent the current
+	 * working directory to be checked */
+	ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+
+	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs.ptr));
+	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs.ptr));
+	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs.ptr));
+}
+
+void test_repo_discover__ceiling_should_not_affect_gitdir_redirection(void)
+{
+	append_ceiling_dir(&ceiling_dirs, SUB_REPOSITORY_FOLDER);
+
+	/* gitfile redirection should not be affected by ceiling directories */
+	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+	ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs.ptr, DISCOVER_FOLDER);
+}
+
+void test_repo_discover__discovery_starting_at_file_succeeds(void)
+{
+	int fd;
+
+	cl_assert((fd = p_creat(SUB_REPOSITORY_FOLDER "/file", 0600)) >= 0);
+	cl_assert(p_close(fd) == 0);
+
+	ensure_repository_discover(SUB_REPOSITORY_FOLDER "/file", ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+}
diff --git a/tests/threads/basic.c b/tests/threads/basic.c
index 9c342bc..a9310bb 100644
--- a/tests/threads/basic.c
+++ b/tests/threads/basic.c
@@ -48,3 +48,36 @@
 {
 	run_in_parallel(1, 4, set_error, NULL, NULL);
 }
+
+#ifdef GIT_THREADS
+static void *return_normally(void *param)
+{
+	return param;
+}
+
+static void *exit_abruptly(void *param)
+{
+	git_thread_exit(param);
+	return NULL;
+}
+#endif
+
+void test_threads_basic__exit(void)
+{
+#ifndef GIT_THREADS
+	clar__skip();
+#else
+	git_thread thread;
+	void *result;
+
+	/* Ensure that the return value of the threadproc is returned. */
+	cl_git_pass(git_thread_create(&thread, return_normally, (void *)424242));
+	cl_git_pass(git_thread_join(&thread, &result));
+	cl_assert_equal_sz(424242, (size_t)result);
+
+	/* Ensure that the return value of `git_thread_exit` is returned. */
+	cl_git_pass(git_thread_create(&thread, return_normally, (void *)232323));
+	cl_git_pass(git_thread_join(&thread, &result));
+	cl_assert_equal_sz(232323, (size_t)result);
+#endif
+}
diff --git a/tests/threads/refdb.c b/tests/threads/refdb.c
index f869bcb..94c5f50 100644
--- a/tests/threads/refdb.c
+++ b/tests/threads/refdb.c
@@ -5,6 +5,12 @@
 static git_repository *g_repo;
 static int g_expected = 0;
 
+#ifdef GIT_WIN32
+static bool concurrent_compress = false;
+#else
+static bool concurrent_compress = true;
+#endif
+
 void test_threads_refdb__initialize(void)
 {
 	g_repo = NULL;
@@ -18,14 +24,28 @@
 
 #define REPEAT 20
 #define THREADS 20
+/* Number of references to create or delete in each thread */
+#define NREFS 10
+
+struct th_data {
+	cl_git_thread_err error;
+	int id;
+	const char *path;
+};
 
 static void *iterate_refs(void *arg)
 {
+	struct th_data *data = (struct th_data *) arg;
 	git_reference_iterator *i;
 	git_reference *ref;
-	int count = 0;
+	int count = 0, error;
+	git_repository *repo;
 
-	cl_git_pass(git_reference_iterator_new(&i, g_repo));
+	cl_git_thread_pass(data, git_repository_open(&repo, data->path));
+	do {
+		error = git_reference_iterator_new(&i, repo);
+	} while (error == GIT_ELOCKED);
+	cl_git_thread_pass(data, error);
 
 	for (count = 0; !git_reference_next(&ref, i); ++count) {
 		cl_assert(ref != NULL);
@@ -37,112 +57,92 @@
 
 	git_reference_iterator_free(i);
 
+	git_repository_free(repo);
 	giterr_clear();
 	return arg;
 }
 
-void test_threads_refdb__iterator(void)
-{
-	int r, t;
-	git_thread th[THREADS];
-	int id[THREADS];
-	git_oid head;
-	git_reference *ref;
-	char name[128];
-	git_refdb *refdb;
-
-	g_repo = cl_git_sandbox_init("testrepo2");
-
-	cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
-
-	/* make a bunch of references */
-
-	for (r = 0; r < 200; ++r) {
-		p_snprintf(name, sizeof(name), "refs/heads/direct-%03d", r);
-		cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0, NULL));
-		git_reference_free(ref);
-	}
-
-	cl_git_pass(git_repository_refdb(&refdb, g_repo));
-	cl_git_pass(git_refdb_compress(refdb));
-	git_refdb_free(refdb);
-
-	g_expected = 206;
-
-	for (r = 0; r < REPEAT; ++r) {
-		g_repo = cl_git_sandbox_reopen(); /* reopen to flush caches */
-
-		for (t = 0; t < THREADS; ++t) {
-			id[t] = t;
-#ifdef GIT_THREADS
-			cl_git_pass(git_thread_create(&th[t], iterate_refs, &id[t]));
-#else
-			th[t] = t;
-			iterate_refs(&id[t]);
-#endif
-		}
-
-#ifdef GIT_THREADS
-		for (t = 0; t < THREADS; ++t) {
-			cl_git_pass(git_thread_join(&th[t], NULL));
-		}
-#endif
-
-		memset(th, 0, sizeof(th));
-	}
-}
-
 static void *create_refs(void *arg)
 {
-	int *id = arg, i;
+	int i, error;
+	struct th_data *data = (struct th_data *) arg;
 	git_oid head;
 	char name[128];
-	git_reference *ref[10];
+	git_reference *ref[NREFS];
+	git_repository *repo;
 
-	cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
+	cl_git_thread_pass(data, git_repository_open(&repo, data->path));
 
-	for (i = 0; i < 10; ++i) {
-		p_snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", *id, i);
-		cl_git_pass(git_reference_create(&ref[i], g_repo, name, &head, 0, NULL));
+	do {
+		error = git_reference_name_to_id(&head, repo, "HEAD");
+	} while (error == GIT_ELOCKED);
+	cl_git_thread_pass(data, error);
 
-		if (i == 5) {
+	for (i = 0; i < NREFS; ++i) {
+		p_snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", data->id, i);
+		do {
+			error = git_reference_create(&ref[i], repo, name, &head, 0, NULL);
+		} while (error == GIT_ELOCKED);
+		cl_git_thread_pass(data, error);
+
+		if (concurrent_compress && i == NREFS/2) {
 			git_refdb *refdb;
-			cl_git_pass(git_repository_refdb(&refdb, g_repo));
-			cl_git_pass(git_refdb_compress(refdb));
+			cl_git_thread_pass(data, git_repository_refdb(&refdb, repo));
+			do {
+				error = git_refdb_compress(refdb);
+			} while (error == GIT_ELOCKED);
+			cl_git_thread_pass(data, error);
 			git_refdb_free(refdb);
 		}
 	}
 
-	for (i = 0; i < 10; ++i)
+	for (i = 0; i < NREFS; ++i)
 		git_reference_free(ref[i]);
 
+	git_repository_free(repo);
+
 	giterr_clear();
 	return arg;
 }
 
 static void *delete_refs(void *arg)
 {
-	int *id = arg, i;
+	int i, error;
+	struct th_data *data = (struct th_data *) arg;
 	git_reference *ref;
 	char name[128];
+	git_repository *repo;
 
-	for (i = 0; i < 10; ++i) {
+	cl_git_thread_pass(data, git_repository_open(&repo, data->path));
+
+	for (i = 0; i < NREFS; ++i) {
 		p_snprintf(
-			name, sizeof(name), "refs/heads/thread-%03d-%02d", (*id) & ~0x3, i);
+			name, sizeof(name), "refs/heads/thread-%03d-%02d", (data->id) & ~0x3, i);
 
-		if (!git_reference_lookup(&ref, g_repo, name)) {
-			cl_git_pass(git_reference_delete(ref));
+		if (!git_reference_lookup(&ref, repo, name)) {
+			do {
+				error = git_reference_delete(ref);
+			} while (error == GIT_ELOCKED);
+			/* Sometimes we race with other deleter threads */
+			if (error == GIT_ENOTFOUND)
+				error = 0;
+
+			cl_git_thread_pass(data, error);
 			git_reference_free(ref);
 		}
 
-		if (i == 5) {
+		if (concurrent_compress && i == NREFS/2) {
 			git_refdb *refdb;
-			cl_git_pass(git_repository_refdb(&refdb, g_repo));
-			cl_git_pass(git_refdb_compress(refdb));
+			cl_git_thread_pass(data, git_repository_refdb(&refdb, repo));
+			do {
+				error = git_refdb_compress(refdb);
+			} while (error == GIT_ELOCKED);
+			cl_git_thread_pass(data, error);
 			git_refdb_free(refdb);
 		}
 	}
 
+	git_repository_free(repo);
 	giterr_clear();
 	return arg;
 }
@@ -150,7 +150,7 @@
 void test_threads_refdb__edit_while_iterate(void)
 {
 	int r, t;
-	int id[THREADS];
+	struct th_data th_data[THREADS];
 	git_oid head;
 	git_reference *ref;
 	char name[128];
@@ -189,33 +189,32 @@
 		default: fn = iterate_refs; break;
 		}
 
-		id[t] = t;
+		th_data[t].id = t;
+		th_data[t].path = git_repository_path(g_repo);
 
-		/* It appears with all reflog writing changes, etc., that this
-		 * test has started to fail quite frequently, so let's disable it
-		 * for now by just running on a single thread...
-		 */
-/* #ifdef GIT_THREADS */
-/*		cl_git_pass(git_thread_create(&th[t], fn, &id[t])); */
-/* #else */
-		fn(&id[t]);
-/* #endif */
+#ifdef GIT_THREADS
+		cl_git_pass(git_thread_create(&th[t], fn, &th_data[t]));
+#else
+		fn(&th_data[t]);
+#endif
 	}
 
 #ifdef GIT_THREADS
-/*	for (t = 0; t < THREADS; ++t) { */
-/*		cl_git_pass(git_thread_join(th[t], NULL)); */
-/*	} */
+	for (t = 0; t < THREADS; ++t) {
+		cl_git_pass(git_thread_join(&th[t], NULL));
+		cl_git_thread_check(&th_data[t]);
+	}
 
 	memset(th, 0, sizeof(th));
 
 	for (t = 0; t < THREADS; ++t) {
-		id[t] = t;
-		cl_git_pass(git_thread_create(&th[t], iterate_refs, &id[t]));
+		th_data[t].id = t;
+		cl_git_pass(git_thread_create(&th[t], iterate_refs, &th_data[t]));
 	}
 
 	for (t = 0; t < THREADS; ++t) {
 		cl_git_pass(git_thread_join(&th[t], NULL));
+		cl_git_thread_check(&th_data[t]);
 	}
 #endif
 }