Merge pull request #3948 from libgit2/cmn/v24-updates

Backport fixes to v0.24
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c79b263..0f9cff5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -40,7 +40,7 @@
 OPTION( USE_SSH				"Link with libssh to enable SSH support" ON )
 OPTION( USE_GSSAPI			"Link with libgssapi for SPNEGO auth"   OFF )
 OPTION( VALGRIND			"Configure build for valgrind"			OFF )
-OPTION( CURL			"User curl for HTTP if available" ON)
+OPTION( CURL			"Use curl for HTTP if available" ON)
 OPTION( DEBUG_POOL			"Enable debug pool allocator"			OFF )
 
 IF(DEBUG_POOL)
@@ -151,6 +151,10 @@
 		TARGET_LINK_LIBRARIES(${target} socket nsl)
 		LIST(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl")
 		SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
+	ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Haiku")
+		TARGET_LINK_LIBRARIES(${target} network)
+		LIST(APPEND LIBGIT2_PC_LIBS "-lnetwork")
+		SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
 	ENDIF()
 	CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" NEED_LIBRT)
 	IF(NEED_LIBRT)
@@ -161,6 +165,8 @@
 
 	IF(THREADSAFE)
 		TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
+		LIST(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT})
+		SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
 	ENDIF()
 ENDFUNCTION()
 
@@ -280,6 +286,7 @@
 	IF (CURL_FOUND)
 		ADD_DEFINITIONS(-DGIT_CURL)
 		INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIRS})
+		LINK_DIRECTORIES(${CURL_LIBRARY_DIRS})
 		LINK_LIBRARIES(${CURL_LIBRARIES})
 		LIST(APPEND LIBGIT2_PC_LIBS ${CURL_LDFLAGS})
 	ENDIF()
@@ -467,19 +474,21 @@
 		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
+	IF (MINGW OR MSYS) # MinGW and MSYS always do PIC and complain if we tell them to
 		STRING(REGEX REPLACE "-fPIC" "" CMAKE_SHARED_LIBRARY_C_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS}")
-		# MinGW >= 3.14 uses the C99-style stdio functions
-		# automatically, but forks like mingw-w64 still want
-		# us to define this in order to use them
-		ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO=1)
-
 	ELSEIF (BUILD_SHARED_LIBS)
 		ADD_C_FLAG_IF_SUPPORTED(-fvisibility=hidden)
 
 		SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
 	ENDIF ()
 
+	IF (MINGW)
+		# MinGW >= 3.14 uses the C99-style stdio functions
+		# automatically, but forks like mingw-w64 still want
+		# us to define this in order to use them
+		ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO=1)
+	ENDIF ()
+
 	ADD_C_FLAG_IF_SUPPORTED(-Wdocumentation)
 	ADD_C_FLAG_IF_SUPPORTED(-Wno-missing-field-initializers)
 	ADD_C_FLAG_IF_SUPPORTED(-Wstrict-aliasing=2)
diff --git a/README.md b/README.md
index 8ea787b..b69a18a 100644
--- a/README.md
+++ b/README.md
@@ -15,18 +15,30 @@
 Additionally, the example code has been released to the public domain (see the
 [separate license](examples/COPYING) for more information).
 
-* Website: [libgit2.github.com](http://libgit2.github.com)
-* StackOverflow Tag: [libgit2](http://stackoverflow.com/questions/tagged/libgit2)
-* Issues: [GitHub Issues](https://github.com/libgit2/libgit2/issues) (Right here!)
-* API documentation: <http://libgit2.github.com/libgit2/>
-* IRC: [#libgit2](irc://irc.freenode.net/libgit2) on irc.freenode.net.
-* Mailing list: The libgit2 mailing list was
-    traditionally hosted in Librelist but has been deprecated. We encourage you to
-    [use StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) instead for any questions regarding
-    the library, or [open an issue](https://github.com/libgit2/libgit2/issues)
-    on GitHub for bug reports.  The mailing list archives are still available at
-    <http://librelist.com/browser/libgit2/>.
+Getting Help
+============
 
+**Join us on Slack**
+
+Visit [slack.libgit2.org](http://slack.libgit2.org/) to sign up, then join
+us in `#libgit2`.  If you prefer IRC, you can also point your client to our
+slack channel once you've registered.
+
+**Getting Help**
+
+If you have questions about the library, please be sure to check out the
+[API documentation](http://libgit2.github.com/libgit2/).  If you still have
+questions, reach out to us on Slack or post a question on 
+[StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) (with the `libgit2` tag).
+
+**Reporting Bugs**
+
+Please open a [GitHub Issue](https://github.com/libgit2/libgit2/issues) and
+include as much information as possible.  If possible, provide sample code
+that illustrates the problem you're seeing.  If you're seeing a bug only
+on a specific repository, please provide a link to it if possible.
+
+We ask that you not open a GitHub Issue for help, only for bug reports.
 
 What It Can Do
 ==============
@@ -235,16 +247,22 @@
 How Can I Contribute?
 ==================================
 
-Check the [contribution guidelines](CONTRIBUTING.md) to understand our
-workflow, the libgit2 [coding conventions](CONVENTIONS.md), and our list of
-[good starting projects](PROJECTS.md).
+We welcome new contributors!  We have a number of issues marked as
+["up for grabs"](https://github.com/libgit2/libgit2/issues?q=is%3Aissue+is%3Aopen+label%3A%22up+for+grabs%22)
+and
+["easy fix"](https://github.com/libgit2/libgit2/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3A%22easy+fix%22)
+that are good places to jump in and get started.  There's much more detailed
+information in our list of [outstanding projects](PROJECTS.md).
+
+Please be sure to check the [contribution guidelines](CONTRIBUTING.md) to
+understand our workflow, and the libgit2 [coding conventions](CONVENTIONS.md).
 
 License
 ==================================
 
 `libgit2` is under GPL2 **with linking exception**. This means you can link to
 and use the library from any program, proprietary or open source; paid or
-gratis.  However, you cannot modify libgit2 and distribute it without
-supplying the source.
+gratis.  However, if you modify libgit2 itself, you must distribute the
+source to your modified version of libgit2.
 
 See the [COPYING file](COPYING) for the full license text.
diff --git a/docs/checkout-internals.md b/docs/checkout-internals.md
index 6147ffd..e0b2583 100644
--- a/docs/checkout-internals.md
+++ b/docs/checkout-internals.md
@@ -66,6 +66,8 @@
 - Bi       - ignored blob (WD only)
 - T1,T2,T3 - trees with different SHAs,
 - Ti       - ignored tree (WD only)
+- S1,S2    - submodules with different SHAs
+- Sd       - dirty submodule (WD only)
 - x        - nothing
 
 Diff with 2 non-workdir iterators
@@ -162,6 +164,27 @@
 | 35+ |  T1 |     T2 |      x         | update locally deleted tree (SAFE+MISSING)                         |
 | 36* |  T1 |     T2 |  B1/Bi         | update to tree with typechanged tree->blob conflict (F-1)          |
 | 37  |  T1 |     T2 | T1/T2/T3       | update to existing tree (MAYBE SAFE)                               |
+| 38+ |   x |     S1 |      x         | add submodule (SAFE)                                               |
+| 39  |   x |     S1 |  S1/Sd         | independently added submodule (SUBMODULE)                          |
+| 40* |   x |     S1 |     B1         | add submodule with blob confilct (FORCEABLE)                       |
+| 41* |   x |     S1 |     T1         | add submodule with tree conflict (FORCEABLE)                       |
+| 42  |  S1 |      x |  S1/Sd         | deleted submodule (SUBMODULE)                                      |
+| 43  |  S1 |      x |      x         | independently deleted submodule (SUBMODULE)                        |
+| 44  |  S1 |      x |     B1         | independently deleted submodule with added blob (SAFE+MISSING)     |
+| 45  |  S1 |      x |     T1         | independently deleted submodule with added tree (SAFE+MISSING)     |
+| 46  |  S1 |     S1 |      x         | locally deleted submodule (SUBMODULE)                              |
+| 47+ |  S1 |     S2 |      x         | update locally deleted submodule (SAFE)                            |
+| 48  |  S1 |     S1 |     S2         | locally updated submodule commit (SUBMODULE)                       |
+| 49  |  S1 |     S2 |     S1         | updated submodule commit (SUBMODULE)                               |
+| 50+ |  S1 |     B1 |      x         | add blob with locally deleted submodule (SAFE+MISSING)             |
+| 51* |  S1 |     B1 |     S1         | typechange submodule->blob (SAFE)                                  |
+| 52* |  S1 |     B1 |     Sd         | typechange dirty submodule->blob (SAFE!?!?)                        |
+| 53+ |  S1 |     T1 |      x         | add tree with locally deleted submodule (SAFE+MISSING)             |
+| 54* |  S1 |     T1 |  S1/Sd         | typechange submodule->tree (MAYBE SAFE)                            |
+| 55+ |  B1 |     S1 |      x         | add submodule with locally deleted blob (SAFE+MISSING)             |
+| 56* |  B1 |     S1 |     B1         | typechange blob->submodule (SAFE)                                  |
+| 57+ |  T1 |     S1 |      x         | add submodule with locally deleted tree (SAFE+MISSING)             |
+| 58* |  T1 |     S1 |     T1         | typechange tree->submodule (SAFE)                                  |
 
 
 The number is followed by ' ' if no change is needed or '+' if the case
@@ -176,6 +199,8 @@
                   content, which is unknown at this point
 * FORCEABLE == conflict unless FORCE is given
 * DIRTY     == no conflict but change is not applied unless FORCE
+* SUBMODULE == no conflict and no change is applied unless a deleted
+               submodule dir is empty
 
 Some slightly unusual circumstances:
 
@@ -198,7 +223,9 @@
     cases, if baseline == target, we don't touch the workdir (it is
     either already right or is "dirty").  However, since this case also
     implies that a ?/B1/x case will exist as well, it can be skipped.
+* 41 - It's not clear how core git distinguishes this case from 39 (mode?).
+* 52 - Core git makes destructive changes without any warning when the
+    submodule is dirty and the type changes to a blob.
 
 Cases 3, 17, 24, 26, and 29 are all considered conflicts even though
 none of them will require making any updates to the working directory.
-
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index 6cf9ed8..4a9dbb0 100644
--- a/include/git2/checkout.h
+++ b/include/git2/checkout.h
@@ -313,6 +313,13 @@
  * Updates files in the index and the working tree to match the content of
  * the commit pointed at by HEAD.
  *
+ * Note that this is _not_ the correct mechanism used to switch branches;
+ * do not change your `HEAD` and then call this method, that would leave
+ * you with checkout conflicts since your working directory would then
+ * appear to be dirty.  Instead, checkout the target of the branch and
+ * then update `HEAD` using `git_repository_set_head` to point to the
+ * branch you checked out.
+ *
  * @param repo repository to check out (must be non-bare)
  * @param opts specifies checkout options (may be NULL)
  * @return 0 on success, GIT_EUNBORNBRANCH if HEAD points to a non
diff --git a/src/array.h b/src/array.h
index 490e6be..78d321e 100644
--- a/src/array.h
+++ b/src/array.h
@@ -96,7 +96,7 @@
 {
 	size_t lim;
 	unsigned char *part, *array = array_ptr, *base = array_ptr;
-	int cmp;
+	int cmp = -1;
 
 	for (lim = array_len; lim != 0; lim >>= 1) {
 		part = base + (lim >> 1) * item_size;
diff --git a/src/blame_git.c b/src/blame_git.c
index 700207e..96785c7 100644
--- a/src/blame_git.c
+++ b/src/blame_git.c
@@ -37,25 +37,27 @@
 static int make_origin(git_blame__origin **out, git_commit *commit, const char *path)
 {
 	git_blame__origin *o;
+	git_object *blob;
 	size_t path_len = strlen(path), alloc_len;
 	int error = 0;
 
+	if ((error = git_object_lookup_bypath(&blob, (git_object*)commit,
+			path, GIT_OBJ_BLOB)) < 0)
+		return error;
+
 	GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*o), path_len);
 	GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
 	o = git__calloc(1, alloc_len);
 	GITERR_CHECK_ALLOC(o);
 
 	o->commit = commit;
+	o->blob = (git_blob *) blob;
 	o->refcnt = 1;
 	strcpy(o->path, path);
 
-	if (!(error = git_object_lookup_bypath((git_object**)&o->blob, (git_object*)commit,
-			path, GIT_OBJ_BLOB))) {
-		*out = o;
-	} else {
-		origin_decref(o);
-	}
-	return error;
+	*out = o;
+
+	return 0;
 }
 
 /* Locate an existing origin or create a new one. */
@@ -529,8 +531,16 @@
 			goto finish;
 		porigin = find_origin(blame, p, origin);
 
-		if (!porigin)
+		if (!porigin) {
+			/*
+			 * We only have to decrement the parent's
+			 * reference count when no porigin has
+			 * been created, as otherwise the commit
+			 * is assigned to the created object.
+			 */
+			git_commit_free(p);
 			continue;
+		}
 		if (porigin->blob && origin->blob &&
 		    !git_oid_cmp(git_blob_id(porigin->blob), git_blob_id(origin->blob))) {
 			pass_whole_blame(blame, origin, porigin);
diff --git a/src/checkout.c b/src/checkout.c
index deeee62..1bea00d 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -464,7 +464,8 @@
 			*action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE);
 		break;
 	case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */
-		if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
+		if (wd->mode != GIT_FILEMODE_COMMIT &&
+			checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
 			*action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
 		else
 			*action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
@@ -1342,9 +1343,11 @@
 
 static bool should_remove_existing(checkout_data *data)
 {
-	int ignorecase = 0;
+	int ignorecase;
 
-	git_repository__cvar(&ignorecase, data->repo, GIT_CVAR_IGNORECASE);
+	if (git_repository__cvar(&ignorecase, data->repo, GIT_CVAR_IGNORECASE) < 0) {
+		ignorecase = 0;
+	}
 
 	return (ignorecase &&
 		(data->strategy & GIT_CHECKOUT_DONT_REMOVE_EXISTING) == 0);
@@ -2405,8 +2408,13 @@
 
 	if (!data->opts.baseline && !data->opts.baseline_index) {
 		data->opts_free_baseline = true;
+		error = 0;
 
-		error = checkout_lookup_head_tree(&data->opts.baseline, repo);
+		/* if we don't have an index, this is an initial checkout and
+		 * should be against an empty baseline
+		 */
+		if (data->index->on_disk)
+			error = checkout_lookup_head_tree(&data->opts.baseline, repo);
 
 		if (error == GIT_EUNBORNBRANCH) {
 			error = 0;
@@ -2691,7 +2699,7 @@
 	if ((error = git_repository_index(&index, repo)) < 0)
 		return error;
 
-	if ((opts->checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)) {
+	if (opts && (opts->checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)) {
 		iter_opts.pathlist.count = opts->paths.count;
 		iter_opts.pathlist.strings = opts->paths.strings;
 	}
diff --git a/src/common.h b/src/common.h
index 9abd605..51fb918 100644
--- a/src/common.h
+++ b/src/common.h
@@ -45,7 +45,7 @@
 # include "win32/error.h"
 # include "win32/version.h"
 # ifdef GIT_THREADS
-#	include "win32/pthread.h"
+#	include "win32/thread.h"
 # endif
 # if defined(GIT_MSVC_CRTDBG)
 #   include "win32/w32_stack.h"
diff --git a/src/delta-apply.c b/src/delta-apply.c
index 89745fa..02ec7b7 100644
--- a/src/delta-apply.c
+++ b/src/delta-apply.c
@@ -49,6 +49,37 @@
 	return 0;
 }
 
+#define DELTA_HEADER_BUFFER_LEN 16
+int git__delta_read_header_fromstream(size_t *base_sz, size_t *res_sz, git_packfile_stream *stream)
+{
+	static const size_t buffer_len = DELTA_HEADER_BUFFER_LEN;
+	unsigned char buffer[DELTA_HEADER_BUFFER_LEN];
+	const unsigned char *delta, *delta_end;
+	size_t len;
+	ssize_t read;
+
+	len = read = 0;
+	while (len < buffer_len) {
+		read = git_packfile_stream_read(stream, &buffer[len], buffer_len - len);
+
+		if (read == 0)
+			break;
+
+		if (read == GIT_EBUFS)
+			continue;
+
+		len += read;
+	}
+
+	delta = buffer;
+	delta_end = delta + len;
+	if ((hdr_sz(base_sz, &delta, delta_end) < 0) ||
+	    (hdr_sz(res_sz, &delta, delta_end) < 0))
+		return -1;
+
+	return 0;
+}
+
 int git__delta_apply(
 	git_rawobj *out,
 	const unsigned char *base,
@@ -90,13 +121,13 @@
 			size_t off = 0, len = 0;
 
 			if (cmd & 0x01) off = *delta++;
-			if (cmd & 0x02) off |= *delta++ << 8;
-			if (cmd & 0x04) off |= *delta++ << 16;
-			if (cmd & 0x08) off |= *delta++ << 24;
+			if (cmd & 0x02) off |= *delta++ << 8UL;
+			if (cmd & 0x04) off |= *delta++ << 16UL;
+			if (cmd & 0x08) off |= *delta++ << 24UL;
 
 			if (cmd & 0x10) len = *delta++;
-			if (cmd & 0x20) len |= *delta++ << 8;
-			if (cmd & 0x40) len |= *delta++ << 16;
+			if (cmd & 0x20) len |= *delta++ << 8UL;
+			if (cmd & 0x40) len |= *delta++ << 16UL;
 			if (!len)		len = 0x10000;
 
 			if (base_len < off + len || res_sz < len)
diff --git a/src/delta-apply.h b/src/delta-apply.h
index d7d99d0..eeeb786 100644
--- a/src/delta-apply.h
+++ b/src/delta-apply.h
@@ -8,6 +8,7 @@
 #define INCLUDE_delta_apply_h__
 
 #include "odb.h"
+#include "pack.h"
 
 /**
  * Apply a git binary delta to recover the original content.
@@ -47,4 +48,15 @@
 	size_t *base_sz,
 	size_t *res_sz);
 
+/**
+ * Read the header of a git binary delta
+ *
+ * This variant reads just enough from the packfile stream to read the
+ * delta header.
+ */
+extern int git__delta_read_header_fromstream(
+	size_t *base_sz,
+	size_t *res_sz,
+	git_packfile_stream *stream);
+
 #endif
diff --git a/src/filebuf.c b/src/filebuf.c
index 101d508..507f6a9 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -70,7 +70,7 @@
 		git_file source;
 		char buffer[FILEIO_BUFSIZE];
 		ssize_t read_bytes;
-		int error;
+		int error = 0;
 
 		source = p_open(file->path_original, O_RDONLY);
 		if (source < 0) {
diff --git a/src/global.c b/src/global.c
index adf353d..198bc1e 100644
--- a/src/global.c
+++ b/src/global.c
@@ -59,8 +59,9 @@
 	if ((ret = git_hash_global_init()) == 0 &&
 		(ret = git_sysdir_global_init()) == 0 &&
 		(ret = git_filter_global_init()) == 0 &&
-		(ret = git_transport_ssh_global_init()) == 0)
-		ret = git_openssl_stream_global_init();
+		(ret = git_transport_ssh_global_init()) == 0 &&
+		(ret = git_openssl_stream_global_init()) == 0)
+		ret = git_mwindow_global_init();
 
 	GIT_MEMORY_BARRIER;
 
@@ -85,11 +86,6 @@
 
 	git__free(git__user_agent);
 	git__free(git__ssl_ciphers);
-
-#if defined(GIT_MSVC_CRTDBG)
-	git_win32__crtdbg_stacktrace_cleanup();
-	git_win32__stack_cleanup();
-#endif
 }
 
 /**
@@ -137,7 +133,7 @@
 
 	_tls_index = TlsAlloc();
 
-	win32_pthread_initialize();
+	git_threads_init();
 
 	if (git_mutex_init(&git__mwindow_mutex))
 		return -1;
@@ -181,6 +177,11 @@
 
 		TlsFree(_tls_index);
 		git_mutex_free(&git__mwindow_mutex);
+
+#if defined(GIT_MSVC_CRTDBG)
+		git_win32__crtdbg_stacktrace_cleanup();
+		git_win32__stack_cleanup();
+#endif
 	}
 
 	/* Exit the lock */
@@ -226,6 +227,9 @@
 
 BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved)
 {
+	GIT_UNUSED(hInstDll);
+	GIT_UNUSED(lpvReserved);
+
 	/* This is how Windows lets us know our thread is being shut down */
 	if (fdwReason == DLL_THREAD_DETACH) {
 		git__free_tls_data();
diff --git a/src/ignore.c b/src/ignore.c
index ac2af4f..dcbd5c1 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -11,35 +11,64 @@
 #define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n"
 
 /**
- * A negative ignore pattern can match a positive one without
- * wildcards if its pattern equals the tail of the positive
- * pattern. Thus
+ * A negative ignore pattern can negate a positive one without
+ * wildcards if it is a basename only and equals the basename of
+ * the positive pattern. Thus
  *
  * foo/bar
  * !bar
  *
- * would result in foo/bar being unignored again.
+ * would result in foo/bar being unignored again while
+ *
+ * moo/foo/bar
+ * !foo/bar
+ *
+ * would do nothing. The reverse also holds true: a positive
+ * basename pattern can be negated by unignoring the basename in
+ * subdirectories. Thus
+ *
+ * bar
+ * !foo/bar
+ *
+ * would result in foo/bar being unignored again. As with the
+ * first case,
+ *
+ * foo/bar
+ * !moo/foo/bar
+ *
+ * would do nothing, again.
  */
 static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg)
 {
+	git_attr_fnmatch *longer, *shorter;
 	char *p;
 
 	if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0
 		&& (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0) {
-		/*
-		 * no chance of matching if rule is shorter than
-		 * the negated one
-		 */
-		if (rule->length < neg->length)
+
+		/* If lengths match we need to have an exact match */
+		if (rule->length == neg->length) {
+			return strcmp(rule->pattern, neg->pattern) == 0;
+		} else if (rule->length < neg->length) {
+			shorter = rule;
+			longer = neg;
+		} else {
+			shorter = neg;
+			longer = rule;
+		}
+
+		/* Otherwise, we need to check if the shorter
+		 * rule is a basename only (that is, it contains
+		 * no path separator) and, if so, if it
+		 * matches the tail of the longer rule */
+		p = longer->pattern + longer->length - shorter->length;
+
+		if (p[-1] != '/')
+			return false;
+		if (memchr(shorter->pattern, '/', shorter->length) != NULL)
 			return false;
 
-		/*
-		 * shift pattern so its tail aligns with the
-		 * negated pattern
-		 */
-		p = rule->pattern + rule->length - neg->length;
-		if (strcmp(p, neg->pattern) == 0)
-			return true;
+		return memcmp(p, shorter->pattern, shorter->length) == 0;
 	}
 
 	return false;
diff --git a/src/index.c b/src/index.c
index 63e4796..32f585f 100644
--- a/src/index.c
+++ b/src/index.c
@@ -505,10 +505,11 @@
 	int error = 0;
 	git_index_entry *entry = git_vector_get(&index->entries, pos);
 
-	if (entry != NULL)
+	if (entry != NULL) {
 		git_tree_cache_invalidate_path(index->tree, entry->path);
+		DELETE_IN_MAP(index, entry);
+	}
 
-	DELETE_IN_MAP(index, entry);
 	error = git_vector_remove(&index->entries, pos);
 
 	if (!error) {
@@ -2968,6 +2969,8 @@
 			*remove_entry = NULL;
 		int diff;
 
+		error = 0;
+
 		if (old_entry && new_entry)
 			diff = git_index_entry_cmp(old_entry, new_entry);
 		else if (!old_entry && new_entry)
@@ -2985,7 +2988,8 @@
 			/* Path and stage are equal, if the OID is equal, keep it to
 			 * keep the stat cache data.
 			 */
-			if (git_oid_equal(&old_entry->id, &new_entry->id)) {
+			if (git_oid_equal(&old_entry->id, &new_entry->id) &&
+				old_entry->mode == new_entry->mode) {
 				add_entry = (git_index_entry *)old_entry;
 			} else {
 				dup_entry = (git_index_entry *)new_entry;
@@ -2996,8 +3000,17 @@
 		if (dup_entry) {
 			if ((error = index_entry_dup_nocache(&add_entry, index, dup_entry)) < 0)
 				goto done;
+
+			index_entry_adjust_namemask(add_entry,
+				((struct entry_internal *)add_entry)->pathlen);
 		}
 
+		/* invalidate this path in the tree cache if this is new (to
+		 * invalidate the parent trees)
+		 */
+		if (dup_entry && !remove_entry && index->tree)
+			git_tree_cache_invalidate_path(index->tree, dup_entry->path);
+
 		if (add_entry) {
 			if ((error = git_vector_insert(&new_entries, add_entry)) == 0)
 				INSERT_IN_MAP_EX(index, new_entries_map, add_entry, error);
@@ -3008,7 +3021,7 @@
 
 		if (error < 0) {
 			giterr_set(GITERR_INDEX, "failed to insert entry");
-			return error;
+			goto done;
 		}
 
 		if (diff <= 0) {
diff --git a/src/merge.c b/src/merge.c
index d2f92cc..174cbfb 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -2730,6 +2730,7 @@
 	opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH;
 	opts.pathspec.count = merged_paths->length;
 	opts.pathspec.strings = (char **)merged_paths->contents;
+	opts.ignore_submodules = GIT_SUBMODULE_IGNORE_ALL;
 
 	if ((error = git_diff_index_to_workdir(&wd_diff_list, repo, NULL, &opts)) < 0)
 		goto done;
diff --git a/src/mwindow.c b/src/mwindow.c
index d3e9be7..8a5b5ca 100644
--- a/src/mwindow.c
+++ b/src/mwindow.c
@@ -33,20 +33,7 @@
 /* Global list of mwindow files, to open packs once across repos */
 git_strmap *git__pack_cache = NULL;
 
-/**
- * Run under mwindow lock
- */
-int git_mwindow_files_init(void)
-{
-	if (git__pack_cache)
-		return 0;
-
-	git__on_shutdown(git_mwindow_files_free);
-
-	return git_strmap_alloc(&git__pack_cache);
-}
-
-void git_mwindow_files_free(void)
+static void git_mwindow_files_free(void)
 {
 	git_strmap *tmp = git__pack_cache;
 
@@ -54,6 +41,14 @@
 	git_strmap_free(tmp);
 }
 
+int git_mwindow_global_init(void)
+{
+	assert(!git__pack_cache);
+
+	git__on_shutdown(git_mwindow_files_free);
+	return git_strmap_alloc(&git__pack_cache);
+}
+
 int git_mwindow_get_pack(struct git_pack_file **out, const char *path)
 {
 	int error;
@@ -69,12 +64,6 @@
 		return -1;
 	}
 
-	if (git_mwindow_files_init() < 0) {
-		git_mutex_unlock(&git__mwindow_mutex);
-		git__free(packname);
-		return -1;
-	}
-
 	pos = git_strmap_lookup_index(git__pack_cache, packname);
 	git__free(packname);
 
diff --git a/src/mwindow.h b/src/mwindow.h
index 63418e4..bdde9e0 100644
--- a/src/mwindow.h
+++ b/src/mwindow.h
@@ -43,8 +43,7 @@
 void git_mwindow_file_deregister(git_mwindow_file *mwf);
 void git_mwindow_close(git_mwindow **w_cursor);
 
-int git_mwindow_files_init(void);
-void git_mwindow_files_free(void);
+extern int git_mwindow_global_init(void);
 
 struct git_pack_file; /* just declaration to avoid cyclical includes */
 int git_mwindow_get_pack(struct git_pack_file **out, const char *path);
diff --git a/src/odb.c b/src/odb.c
index cb0f706..1ecfe93 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -803,19 +803,12 @@
 	return 0;
 }
 
-static git_oid empty_blob = {{ 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b,
-			       0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91 }};
 static git_oid empty_tree = {{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60,
 			       0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 }};
 
 static int hardcoded_objects(git_rawobj *raw, const git_oid *id)
 {
-	if (!git_oid_cmp(id, &empty_blob)) {
-		raw->type = GIT_OBJ_BLOB;
-		raw->len = 0;
-		raw->data = git__calloc(1, sizeof(uint8_t));
-		return 0;
-	} else if (!git_oid_cmp(id, &empty_tree)) {
+	if (!git_oid_cmp(id, &empty_tree)) {
 		raw->type = GIT_OBJ_TREE;
 		raw->len = 0;
 		raw->data = git__calloc(1, sizeof(uint8_t));
@@ -1229,7 +1222,7 @@
 {
 	if (oid != NULL) {
 		char oid_str[GIT_OID_HEXSZ + 1];
-		git_oid_tostr(oid_str, oid_len, oid);
+		git_oid_tostr(oid_str, oid_len+1, oid);
 		giterr_set(GITERR_ODB, "Object not found - %s (%.*s)",
 			message, oid_len, oid_str);
 	} else
diff --git a/src/odb_loose.c b/src/odb_loose.c
index 9d9bffd..3c33160 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -91,7 +91,7 @@
 
 static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)
 {
-	unsigned char c;
+	unsigned long c;
 	unsigned char *data = (unsigned char *)obj->ptr;
 	size_t shift, size, used = 0;
 
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 5a57864..0764207 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -591,9 +591,6 @@
 	struct pack_backend *backend = NULL;
 	git_buf path = GIT_BUF_INIT;
 
-	if (git_mwindow_files_init() < 0)
-		return -1;
-
 	if (pack_backend__alloc(&backend, 8) < 0)
 		return -1;
 
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
index a65f558..9d97bae 100644
--- a/src/openssl_stream.c
+++ b/src/openssl_stream.c
@@ -522,8 +522,9 @@
 	openssl_stream *st = (openssl_stream *) stream;
 	int ret;
 
-	if ((ret = SSL_read(st->ssl, data, len)) <= 0)
-		ssl_set_error(st->ssl, ret);
+	if ((ret = SSL_read(st->ssl, data, len)) <= 0) {
+		return ssl_set_error(st->ssl, ret);
+	}
 
 	return ret;
 }
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 11e13f7..29231e0 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -1186,7 +1186,7 @@
 		git_mutex_init(&p[i].mutex);
 		git_cond_init(&p[i].cond);
 
-		ret = git_thread_create(&p[i].thread, NULL,
+		ret = git_thread_create(&p[i].thread,
 					threaded_find_deltas, &p[i]);
 		if (ret) {
 			giterr_set(GITERR_THREAD, "unable to create thread");
diff --git a/src/pack.c b/src/pack.c
index e7003e6..6a700e2 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -499,15 +499,14 @@
 
 	if (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) {
 		size_t base_size;
-		git_rawobj delta;
+		git_packfile_stream stream;
+
 		base_offset = get_delta_base(p, &w_curs, &curpos, type, offset);
 		git_mwindow_close(&w_curs);
-		error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, size, type);
-		git_mwindow_close(&w_curs);
-		if (error < 0)
+		if ((error = git_packfile_stream_open(&stream, p, curpos)) < 0)
 			return error;
-		error = git__delta_read_header(delta.data, delta.len, &base_size, size_p);
-		git__free(delta.data);
+		error = git__delta_read_header_fromstream(&base_size, size_p, &stream);
+		git_packfile_stream_free(&stream);
 		if (error < 0)
 			return error;
 	} else
diff --git a/src/refspec.c b/src/refspec.c
index debde86..d200e56 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -53,8 +53,10 @@
 
 	if (rhs) {
 		size_t rlen = strlen(++rhs);
-		is_glob = (1 <= rlen && strchr(rhs, '*'));
-		refspec->dst = git__strndup(rhs, rlen);
+		if (rlen || !is_fetch) {
+			is_glob = (1 <= rlen && strchr(rhs, '*'));
+			refspec->dst = git__strndup(rhs, rlen);
+		}
 	}
 
 	llen = (rhs ? (size_t)(rhs - lhs - 1) : strlen(lhs));
diff --git a/src/remote.c b/src/remote.c
index 8b7203e..15a00dd 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -1414,7 +1414,11 @@
 		/* In autotag mode, don't overwrite any locally-existing tags */
 		error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag, 
 				log_message);
-		if (error < 0 && error != GIT_EEXISTS)
+
+		if (error == GIT_EEXISTS)
+			continue;
+
+		if (error < 0)
 			goto on_error;
 
 		git_reference_free(ref);
@@ -2224,15 +2228,21 @@
 		if (git_buf_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch) < 0)
 			break;
 
-		if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0)
-			break;
+		if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) {
+			if (error != GIT_ENOTFOUND)
+				break;
+			giterr_clear();
+		}
 
 		git_buf_clear(&buf);
 		if (git_buf_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch) < 0)
 			break;
 
-		if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0)
-			break;
+		if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) {
+			if (error != GIT_ENOTFOUND)
+				break;
+			giterr_clear();
+		}
 	}
 
 	if (error == GIT_ITEROVER)
diff --git a/src/repository.c b/src/repository.c
index 8a6fef0..c19790a 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -264,7 +264,7 @@
  * the stack could remove directories name limits, but at the cost of doing
  * repeated malloc/frees inside the loop below, so let's not do it now.
  */
-static int find_ceiling_dir_offset(
+static size_t find_ceiling_dir_offset(
 	const char *path,
 	const char *ceiling_directories)
 {
@@ -278,7 +278,7 @@
 	min_len = (size_t)(git_path_root(path) + 1);
 
 	if (ceiling_directories == NULL || min_len == 0)
-		return (int)min_len;
+		return min_len;
 
 	for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) {
 		for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);
@@ -305,7 +305,7 @@
 		}
 	}
 
-	return (int)(max_len <= min_len ? min_len : max_len);
+	return (max_len <= min_len ? min_len : max_len);
 }
 
 /*
@@ -359,21 +359,36 @@
 	git_buf path = GIT_BUF_INIT;
 	struct stat st;
 	dev_t initial_device = 0;
-	bool try_with_dot_git = ((flags & GIT_REPOSITORY_OPEN_BARE) != 0);
-	int ceiling_offset;
+	int min_iterations;
+	bool in_dot_git;
+	size_t ceiling_offset = 0;
 
 	git_buf_free(repo_path);
 
 	if ((error = git_path_prettify(&path, start_path, NULL)) < 0)
 		return error;
 
-	ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
+	/* in_dot_git toggles each loop:
+	 * /a/b/c/.git, /a/b/c, /a/b/.git, /a/b, /a/.git, /a
+	 * With GIT_REPOSITORY_OPEN_BARE, we assume we started with /a/b/c.git
+	 * and don't append .git the first time through.
+	 * min_iterations indicates the number of iterations left before going
+	 * further counts as a search. */
+	if (flags & GIT_REPOSITORY_OPEN_BARE) {
+		in_dot_git = true;
+		min_iterations = 1;
+	} else {
+		in_dot_git = false;
+		min_iterations = 2;
+	}
 
-	if (!try_with_dot_git &&
-		(error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
-		return error;
+	while (!error && (min_iterations || !(path.ptr[ceiling_offset] == 0 ||
+					      (flags & GIT_REPOSITORY_OPEN_NO_SEARCH)))) {
+		if (!in_dot_git)
+			if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
+				break;
+		in_dot_git = !in_dot_git;
 
-	while (!error && !git_buf_len(repo_path)) {
 		if (p_stat(path.ptr, &st) == 0) {
 			/* check that we have not crossed device boundaries */
 			if (initial_device == 0)
@@ -414,17 +429,10 @@
 			break;
 		}
 
-		if (try_with_dot_git) {
-			/* if we tried original dir with and without .git AND either hit
-			 * directory ceiling or NO_SEARCH was requested, then be done.
-			 */
-			if (path.ptr[ceiling_offset] == '\0' ||
-				(flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0)
-				break;
-			/* otherwise look first for .git item */
-			error = git_buf_joinpath(&path, path.ptr, DOT_GIT);
-		}
-		try_with_dot_git = !try_with_dot_git;
+		/* Once we've checked the directory (and .git if applicable),
+		 * find the ceiling for a search. */
+		if (min_iterations && (--min_iterations == 0))
+			ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
 	}
 
 	if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
diff --git a/src/stransport_stream.c b/src/stransport_stream.c
index 33b6c5c..9d87d60 100644
--- a/src/stransport_stream.c
+++ b/src/stransport_stream.c
@@ -16,7 +16,7 @@
 #include "socket_stream.h"
 #include "curl_stream.h"
 
-int stransport_error(OSStatus ret)
+static int stransport_error(OSStatus ret)
 {
 	CFStringRef message;
 
@@ -33,6 +33,7 @@
 	CFRelease(message);
 #else
     giterr_set(GITERR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret);
+    GIT_UNUSED(message);
 #endif
 
 	return -1;
@@ -46,7 +47,7 @@
 	git_cert_x509 cert_info;
 } stransport_stream;
 
-int stransport_connect(git_stream *stream)
+static int stransport_connect(git_stream *stream)
 {
 	stransport_stream *st = (stransport_stream *) stream;
 	int error;
@@ -66,6 +67,9 @@
 	if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
 		goto on_error;
 
+	if (!trust)
+		return GIT_ECERTIFICATE;
+
 	if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr)
 		goto on_error;
 
@@ -89,7 +93,7 @@
 	return stransport_error(ret);
 }
 
-int stransport_certificate(git_cert **out, git_stream *stream)
+static int stransport_certificate(git_cert **out, git_stream *stream)
 {
 	stransport_stream *st = (stransport_stream *) stream;
 	SecTrustRef trust = NULL;
@@ -116,7 +120,7 @@
 	return 0;
 }
 
-int stransport_set_proxy(git_stream *stream, const char *proxy)
+static int stransport_set_proxy(git_stream *stream, const char *proxy)
 {
 	stransport_stream *st = (stransport_stream *) stream;
 
@@ -146,7 +150,7 @@
 	return noErr;
 }
 
-ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags)
+static ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags)
 {
 	stransport_stream *st = (stransport_stream *) stream;
 	size_t data_len, processed;
@@ -195,7 +199,7 @@
 	return error;
 }
 
-ssize_t stransport_read(git_stream *stream, void *data, size_t len)
+static ssize_t stransport_read(git_stream *stream, void *data, size_t len)
 {
 	stransport_stream *st = (stransport_stream *) stream;
 	size_t processed;
@@ -207,7 +211,7 @@
 	return processed;
 }
 
-int stransport_close(git_stream *stream)
+static int stransport_close(git_stream *stream)
 {
 	stransport_stream *st = (stransport_stream *) stream;
 	OSStatus ret;
@@ -219,7 +223,7 @@
 	return git_stream_close(st->io);
 }
 
-void stransport_free(git_stream *stream)
+static void stransport_free(git_stream *stream)
 {
 	stransport_stream *st = (stransport_stream *) stream;
 
@@ -255,6 +259,7 @@
 	st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
 	if (!st->ctx) {
 		giterr_set(GITERR_NET, "failed to create SSL context");
+		git__free(st);
 		return -1;
 	}
 
@@ -264,7 +269,8 @@
 	    (ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr ||
 	    (ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr ||
 	    (ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) {
-		git_stream_free((git_stream *)st);
+		CFRelease(st->ctx);
+		git__free(st);
 		return stransport_error(ret);
 	}
 
diff --git a/src/sysdir.c b/src/sysdir.c
index bf53d83..29e53e2 100644
--- a/src/sysdir.c
+++ b/src/sysdir.c
@@ -83,45 +83,43 @@
 #endif
 }
 
-typedef int (*git_sysdir_guess_cb)(git_buf *out);
-
-static git_buf git_sysdir__dirs[GIT_SYSDIR__MAX] =
-	{ GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
-
-static git_sysdir_guess_cb git_sysdir__dir_guess[GIT_SYSDIR__MAX] = {
-	git_sysdir_guess_system_dirs,
-	git_sysdir_guess_global_dirs,
-	git_sysdir_guess_xdg_dirs,
-	git_sysdir_guess_programdata_dirs,
-	git_sysdir_guess_template_dirs,
+struct git_sysdir__dir {
+	git_buf buf;
+	int (*guess)(git_buf *out);
 };
 
-static int git_sysdir__dirs_shutdown_set = 0;
+static struct git_sysdir__dir git_sysdir__dirs[] = {
+	{ GIT_BUF_INIT, git_sysdir_guess_system_dirs },
+	{ GIT_BUF_INIT, git_sysdir_guess_global_dirs },
+	{ GIT_BUF_INIT, git_sysdir_guess_xdg_dirs },
+	{ GIT_BUF_INIT, git_sysdir_guess_programdata_dirs },
+	{ GIT_BUF_INIT, git_sysdir_guess_template_dirs },
+};
+
+static void git_sysdir_global_shutdown(void)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(git_sysdir__dirs); ++i)
+		git_buf_free(&git_sysdir__dirs[i].buf);
+}
 
 int git_sysdir_global_init(void)
 {
-	git_sysdir_t i;
-	const git_buf *path;
+	size_t i;
 	int error = 0;
 
-	for (i = 0; !error && i < GIT_SYSDIR__MAX; i++)
-		error = git_sysdir_get(&path, i);
+	for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); i++)
+		error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf);
+
+	git__on_shutdown(git_sysdir_global_shutdown);
 
 	return error;
 }
 
-void git_sysdir_global_shutdown(void)
-{
-	int i;
-	for (i = 0; i < GIT_SYSDIR__MAX; ++i)
-		git_buf_free(&git_sysdir__dirs[i]);
-
-	git_sysdir__dirs_shutdown_set = 0;
-}
-
 static int git_sysdir_check_selector(git_sysdir_t which)
 {
-	if (which < GIT_SYSDIR__MAX)
+	if (which < ARRAY_SIZE(git_sysdir__dirs))
 		return 0;
 
 	giterr_set(GITERR_INVALID, "config directory selector out of range");
@@ -137,18 +135,7 @@
 
 	GITERR_CHECK_ERROR(git_sysdir_check_selector(which));
 
-	if (!git_buf_len(&git_sysdir__dirs[which])) {
-		/* prepare shutdown if we're going to need it */
-		if (!git_sysdir__dirs_shutdown_set) {
-			git__on_shutdown(git_sysdir_global_shutdown);
-			git_sysdir__dirs_shutdown_set = 1;
-		}
-
-		GITERR_CHECK_ERROR(
-			git_sysdir__dir_guess[which](&git_sysdir__dirs[which]));
-	}
-
-	*out = &git_sysdir__dirs[which];
+	*out = &git_sysdir__dirs[which].buf;
 	return 0;
 }
 
@@ -183,31 +170,38 @@
 	if (search_path != NULL)
 		expand_path = strstr(search_path, PATH_MAGIC);
 
-	/* init with default if not yet done and needed (ignoring error) */
-	if ((!search_path || expand_path) &&
-		!git_buf_len(&git_sysdir__dirs[which]))
-		git_sysdir__dir_guess[which](&git_sysdir__dirs[which]);
+	/* reset the default if this path has been cleared */
+	if (!search_path || expand_path)
+		git_sysdir__dirs[which].guess(&git_sysdir__dirs[which].buf);
 
 	/* if $PATH is not referenced, then just set the path */
-	if (!expand_path)
-		return git_buf_sets(&git_sysdir__dirs[which], search_path);
+	if (!expand_path) {
+		if (search_path)
+			git_buf_sets(&git_sysdir__dirs[which].buf, search_path);
+
+		goto done;
+	}
 
 	/* otherwise set to join(before $PATH, old value, after $PATH) */
 	if (expand_path > search_path)
 		git_buf_set(&merge, search_path, expand_path - search_path);
 
-	if (git_buf_len(&git_sysdir__dirs[which]))
+	if (git_buf_len(&git_sysdir__dirs[which].buf))
 		git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR,
-			merge.ptr, git_sysdir__dirs[which].ptr);
+			merge.ptr, git_sysdir__dirs[which].buf.ptr);
 
 	expand_path += strlen(PATH_MAGIC);
 	if (*expand_path)
 		git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path);
 
-	git_buf_swap(&git_sysdir__dirs[which], &merge);
+	git_buf_swap(&git_sysdir__dirs[which].buf, &merge);
 	git_buf_free(&merge);
 
-	return git_buf_oom(&git_sysdir__dirs[which]) ? -1 : 0;
+done:
+	if (git_buf_oom(&git_sysdir__dirs[which].buf))
+		return -1;
+
+	return 0;
 }
 
 static int git_sysdir_find_in_dirlist(
diff --git a/src/sysdir.h b/src/sysdir.h
index 12874fc..1187898 100644
--- a/src/sysdir.h
+++ b/src/sysdir.h
@@ -103,9 +103,4 @@
  */
 extern int git_sysdir_set(git_sysdir_t which, const char *paths);
 
-/**
- * Free the configuration file search paths.
- */
-extern void git_sysdir_global_shutdown(void);
-
 #endif /* INCLUDE_sysdir_h__ */
diff --git a/src/tag.c b/src/tag.c
index c4bce1f..fe840fe 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -137,8 +137,14 @@
 
 	tag->message = NULL;
 	if (buffer < buffer_end) {
-		if( *buffer != '\n' )
-			return tag_error("No new line before message");
+		/* If we're not at the end of the header, search for it */
+		if( *buffer != '\n' ) {
+			search = strstr(buffer, "\n\n");
+			if (search)
+				buffer = search + 1;
+			else
+				return tag_error("tag contains no message");
+		}
 
 		text_len = buffer_end - ++buffer;
 
diff --git a/src/thread-utils.h b/src/thread-utils.h
index 14c8a41..f016198 100644
--- a/src/thread-utils.h
+++ b/src/thread-utils.h
@@ -40,58 +40,12 @@
 
 #ifdef GIT_THREADS
 
-#if !defined(GIT_WIN32)
-
-typedef struct {
-	pthread_t thread;
-} git_thread;
-
-#define git_thread_create(git_thread_ptr, attr, start_routine, arg) \
-	pthread_create(&(git_thread_ptr)->thread, attr, start_routine, arg)
-#define git_thread_join(git_thread_ptr, status) \
-	pthread_join((git_thread_ptr)->thread, status)
-
+#ifdef GIT_WIN32
+#   include "win32/thread.h"
+#else
+#   include "unix/pthread.h"
 #endif
 
-/* Pthreads Mutex */
-#define git_mutex pthread_mutex_t
-#define git_mutex_init(a)	pthread_mutex_init(a, NULL)
-#define git_mutex_lock(a)	pthread_mutex_lock(a)
-#define git_mutex_unlock(a) pthread_mutex_unlock(a)
-#define git_mutex_free(a)	pthread_mutex_destroy(a)
-
-/* Pthreads condition vars */
-#define git_cond pthread_cond_t
-#define git_cond_init(c)	pthread_cond_init(c, NULL)
-#define git_cond_free(c) 	pthread_cond_destroy(c)
-#define git_cond_wait(c, l)	pthread_cond_wait(c, l)
-#define git_cond_signal(c)	pthread_cond_signal(c)
-#define git_cond_broadcast(c)	pthread_cond_broadcast(c)
-
-/* Pthread (-ish) rwlock
- *
- * This differs from normal pthreads rwlocks in two ways:
- * 1. Separate APIs for releasing read locks and write locks (as
- *    opposed to the pure POSIX API which only has one unlock fn)
- * 2. You should not use recursive read locks (i.e. grabbing a read
- *    lock in a thread that already holds a read lock) because the
- *    Windows implementation doesn't support it
- */
-#define git_rwlock pthread_rwlock_t
-#define git_rwlock_init(a)		pthread_rwlock_init(a, NULL)
-#define git_rwlock_rdlock(a)	pthread_rwlock_rdlock(a)
-#define git_rwlock_rdunlock(a)	pthread_rwlock_rdunlock(a)
-#define git_rwlock_wrlock(a)	pthread_rwlock_wrlock(a)
-#define git_rwlock_wrunlock(a)	pthread_rwlock_wrunlock(a)
-#define git_rwlock_free(a)		pthread_rwlock_destroy(a)
-#define GIT_RWLOCK_STATIC_INIT	PTHREAD_RWLOCK_INITIALIZER
-
-#ifndef GIT_WIN32
-#define pthread_rwlock_rdunlock pthread_rwlock_unlock
-#define pthread_rwlock_wrunlock pthread_rwlock_unlock
-#endif
-
-
 GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
 {
 #if defined(GIT_WIN32)
@@ -178,7 +132,7 @@
 #else
 
 #define git_thread unsigned int
-#define git_thread_create(thread, attr, start_routine, arg) 0
+#define git_thread_create(thread, start_routine, arg) 0
 #define git_thread_join(id, status) (void)0
 
 /* Pthreads Mutex */
diff --git a/src/transports/http.c b/src/transports/http.c
index 88b124b..f0efd95 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -114,7 +114,7 @@
 	size_t scheme_len;
 
 	scheme_len = strlen(scheme_name);
-	return (strncmp(challenge, scheme_name, scheme_len) == 0 &&
+	return (strncasecmp(challenge, scheme_name, scheme_len) == 0 &&
 		(challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '));
 }
 
@@ -569,6 +569,7 @@
 		git_stream_close(t->io);
 		git_stream_free(t->io);
 		t->io = NULL;
+		t->connected = 0;
 	}
 
 	if (t->connection_data.use_ssl) {
diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c
index 2ea57bb..2297cc9 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -433,6 +433,7 @@
 	 * line?
 	 */
 	if (len == PKT_LEN_SIZE) {
+		*head = NULL;
 		*out = line;
 		return 0;
 	}
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 02e1ecf..3448fa7 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -759,6 +759,14 @@
 		line_len -= (line_end - line);
 		line = line_end;
 
+		/* When a valid packet with no content has been
+		 * read, git_pkt_parse_line does not report an
+		 * error, but the pkt pointer has not been set.
+		 * Handle this by skipping over empty packets.
+		 */
+		if (pkt == NULL)
+			continue;
+
 		error = add_push_report_pkt(push, pkt);
 
 		git_pkt_free(pkt);
@@ -813,6 +821,9 @@
 
 		error = 0;
 
+		if (pkt == NULL)
+			continue;
+
 		switch (pkt->type) {
 			case GIT_PKT_DATA:
 				/* This is a sideband packet which contains other packets */
diff --git a/src/tree.c b/src/tree.c
index 6ce460c..3874e45 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -45,7 +45,7 @@
 	if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_COMMIT)
 		return GIT_FILEMODE_COMMIT;
 
-	/* 12XXXX means commit */
+	/* 12XXXX means symlink */
 	if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_LINK)
 		return GIT_FILEMODE_LINK;
 
diff --git a/src/unix/pthread.h b/src/unix/pthread.h
new file mode 100644
index 0000000..0f3f179
--- /dev/null
+++ b/src/unix/pthread.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_unix_pthread_h__
+#define INCLUDE_unix_pthread_h__
+
+typedef struct {
+	pthread_t thread;
+} git_thread;
+
+#define git_threads_init() (void)0
+#define git_thread_create(git_thread_ptr, start_routine, arg) \
+	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)
+
+/* Git Mutex */
+#define git_mutex pthread_mutex_t
+#define git_mutex_init(a)	pthread_mutex_init(a, NULL)
+#define git_mutex_lock(a)	pthread_mutex_lock(a)
+#define git_mutex_unlock(a)     pthread_mutex_unlock(a)
+#define git_mutex_free(a)	pthread_mutex_destroy(a)
+
+/* Git condition vars */
+#define git_cond pthread_cond_t
+#define git_cond_init(c)	pthread_cond_init(c, NULL)
+#define git_cond_free(c) 	pthread_cond_destroy(c)
+#define git_cond_wait(c, l)	pthread_cond_wait(c, l)
+#define git_cond_signal(c)	pthread_cond_signal(c)
+#define git_cond_broadcast(c)	pthread_cond_broadcast(c)
+
+/* Pthread (-ish) rwlock
+ *
+ * This differs from normal pthreads rwlocks in two ways:
+ * 1. Separate APIs for releasing read locks and write locks (as
+ *    opposed to the pure POSIX API which only has one unlock fn)
+ * 2. You should not use recursive read locks (i.e. grabbing a read
+ *    lock in a thread that already holds a read lock) because the
+ *    Windows implementation doesn't support it
+ */
+#define git_rwlock              pthread_rwlock_t
+#define git_rwlock_init(a)	pthread_rwlock_init(a, NULL)
+#define git_rwlock_rdlock(a)	pthread_rwlock_rdlock(a)
+#define git_rwlock_rdunlock(a)	pthread_rwlock_unlock(a)
+#define git_rwlock_wrlock(a)	pthread_rwlock_wrlock(a)
+#define git_rwlock_wrunlock(a)	pthread_rwlock_unlock(a)
+#define git_rwlock_free(a)	pthread_rwlock_destroy(a)
+#define GIT_RWLOCK_STATIC_INIT	PTHREAD_RWLOCK_INITIALIZER
+
+#endif /* INCLUDE_unix_pthread_h__ */
diff --git a/src/util.c b/src/util.c
index 9e67f43..cc4b432 100644
--- a/src/util.c
+++ b/src/util.c
@@ -122,8 +122,8 @@
 			v = c - 'A' + 10;
 		if (v >= base)
 			break;
-		nn = n*base + v;
-		if (nn < n)
+		nn = n * base + (neg ? -v : v);
+		if ((!neg && nn < n) || (neg && nn > n))
 			ovfl = 1;
 		n = nn;
 	}
@@ -142,7 +142,7 @@
 		return -1;
 	}
 
-	*result = neg ? -n : n;
+	*result = n;
 	return 0;
 }
 
diff --git a/src/win32/precompiled.h b/src/win32/precompiled.h
index 33ce106..10ca0b8 100644
--- a/src/win32/precompiled.h
+++ b/src/win32/precompiled.h
@@ -16,7 +16,7 @@
 #include <io.h>
 #include <direct.h>
 #ifdef GIT_THREADS
- #include "win32/pthread.h"
+ #include "win32/thread.h"
 #endif
 
 #include "git2.h"
diff --git a/src/win32/pthread.h b/src/win32/pthread.h
deleted file mode 100644
index e4826ca..0000000
--- a/src/win32/pthread.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef GIT_PTHREAD_H
-#define GIT_PTHREAD_H
-
-#include "../common.h"
-
-#if defined (_MSC_VER)
-#	define GIT_RESTRICT __restrict
-#else
-#	define GIT_RESTRICT __restrict__
-#endif
-
-typedef struct {
-	HANDLE thread;
-	void *(*proc)(void *);
-	void *param;
-	void *result;
-} git_win32_thread;
-
-typedef int pthread_mutexattr_t;
-typedef int pthread_condattr_t;
-typedef int pthread_attr_t;
-typedef int pthread_rwlockattr_t;
-
-typedef CRITICAL_SECTION pthread_mutex_t;
-typedef HANDLE pthread_cond_t;
-
-typedef struct { void *Ptr; } GIT_SRWLOCK;
-
-typedef struct {
-	union {
-		GIT_SRWLOCK srwl;
-		CRITICAL_SECTION csec;
-	} native;
-} pthread_rwlock_t;
-
-#define PTHREAD_MUTEX_INITIALIZER  {(void*)-1}
-
-int git_win32__thread_create(
-	git_win32_thread *GIT_RESTRICT,
-	const pthread_attr_t *GIT_RESTRICT,
-	void *(*) (void *),
-	void *GIT_RESTRICT);
-
-int git_win32__thread_join(
-	git_win32_thread *,
-	void **);
-
-#ifdef GIT_THREADS
-
-typedef git_win32_thread git_thread;
-
-#define git_thread_create(git_thread_ptr, attr, start_routine, arg) \
-	git_win32__thread_create(git_thread_ptr, attr, start_routine, arg)
-#define git_thread_join(git_thread_ptr, status) \
-	git_win32__thread_join(git_thread_ptr, status)
-
-#endif
-
-int pthread_mutex_init(
-	pthread_mutex_t *GIT_RESTRICT mutex,
-	const pthread_mutexattr_t *GIT_RESTRICT mutexattr);
-int pthread_mutex_destroy(pthread_mutex_t *);
-int pthread_mutex_lock(pthread_mutex_t *);
-int pthread_mutex_unlock(pthread_mutex_t *);
-
-int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *);
-int pthread_cond_destroy(pthread_cond_t *);
-int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *);
-int pthread_cond_signal(pthread_cond_t *);
-/* pthread_cond_broadcast is not supported on Win32 yet. */
-
-int pthread_num_processors_np(void);
-
-int pthread_rwlock_init(
-	pthread_rwlock_t *GIT_RESTRICT lock,
-	const pthread_rwlockattr_t *GIT_RESTRICT attr);
-int pthread_rwlock_rdlock(pthread_rwlock_t *);
-int pthread_rwlock_rdunlock(pthread_rwlock_t *);
-int pthread_rwlock_wrlock(pthread_rwlock_t *);
-int pthread_rwlock_wrunlock(pthread_rwlock_t *);
-int pthread_rwlock_destroy(pthread_rwlock_t *);
-
-extern int win32_pthread_initialize(void);
-
-#endif
diff --git a/src/win32/pthread.c b/src/win32/thread.c
similarity index 72%
rename from src/win32/pthread.c
rename to src/win32/thread.c
index a1cc189..80d56ce 100644
--- a/src/win32/pthread.c
+++ b/src/win32/thread.c
@@ -5,18 +5,26 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "pthread.h"
+#include "thread.h"
 #include "../global.h"
 
 #define CLEAN_THREAD_EXIT 0x6F012842
 
+typedef void (WINAPI *win32_srwlock_fn)(GIT_SRWLOCK *);
+
+static win32_srwlock_fn win32_srwlock_initialize;
+static win32_srwlock_fn win32_srwlock_acquire_shared;
+static win32_srwlock_fn win32_srwlock_release_shared;
+static win32_srwlock_fn win32_srwlock_acquire_exclusive;
+static win32_srwlock_fn win32_srwlock_release_exclusive;
+
 /* The thread procedure stub used to invoke the caller's procedure
  * and capture the return value for later collection. Windows will
  * only hold a DWORD, but we need to be able to store an entire
  * void pointer. This requires the indirection. */
 static DWORD WINAPI git_win32__threadproc(LPVOID lpParameter)
 {
-	git_win32_thread *thread = lpParameter;
+	git_thread *thread = lpParameter;
 
 	thread->result = thread->proc(thread->param);
 
@@ -25,14 +33,31 @@
 	return CLEAN_THREAD_EXIT;
 }
 
-int git_win32__thread_create(
-	git_win32_thread *GIT_RESTRICT thread,
-	const pthread_attr_t *GIT_RESTRICT attr,
+int git_threads_init(void)
+{
+	HMODULE hModule = GetModuleHandleW(L"kernel32");
+
+	if (hModule) {
+		win32_srwlock_initialize = (win32_srwlock_fn)
+			GetProcAddress(hModule, "InitializeSRWLock");
+		win32_srwlock_acquire_shared = (win32_srwlock_fn)
+			GetProcAddress(hModule, "AcquireSRWLockShared");
+		win32_srwlock_release_shared = (win32_srwlock_fn)
+			GetProcAddress(hModule, "ReleaseSRWLockShared");
+		win32_srwlock_acquire_exclusive = (win32_srwlock_fn)
+			GetProcAddress(hModule, "AcquireSRWLockExclusive");
+		win32_srwlock_release_exclusive = (win32_srwlock_fn)
+			GetProcAddress(hModule, "ReleaseSRWLockExclusive");
+	}
+
+	return 0;
+}
+
+int git_thread_create(
+	git_thread *GIT_RESTRICT thread,
 	void *(*start_routine)(void*),
 	void *GIT_RESTRICT arg)
 {
-	GIT_UNUSED(attr);
-
 	thread->result = NULL;
 	thread->param = arg;
 	thread->proc = start_routine;
@@ -42,8 +67,8 @@
 	return thread->thread ? 0 : -1;
 }
 
-int git_win32__thread_join(
-	git_win32_thread *thread,
+int git_thread_join(
+	git_thread *thread,
 	void **value_ptr)
 {
 	DWORD exit;
@@ -70,39 +95,32 @@
 	return 0;
 }
 
-int pthread_mutex_init(
-	pthread_mutex_t *GIT_RESTRICT mutex,
-	const pthread_mutexattr_t *GIT_RESTRICT mutexattr)
+int git_mutex_init(git_mutex *GIT_RESTRICT mutex)
 {
-	GIT_UNUSED(mutexattr);
 	InitializeCriticalSection(mutex);
 	return 0;
 }
 
-int pthread_mutex_destroy(pthread_mutex_t *mutex)
+int git_mutex_free(git_mutex *mutex)
 {
 	DeleteCriticalSection(mutex);
 	return 0;
 }
 
-int pthread_mutex_lock(pthread_mutex_t *mutex)
+int git_mutex_lock(git_mutex *mutex)
 {
 	EnterCriticalSection(mutex);
 	return 0;
 }
 
-int pthread_mutex_unlock(pthread_mutex_t *mutex)
+int git_mutex_unlock(git_mutex *mutex)
 {
 	LeaveCriticalSection(mutex);
 	return 0;
 }
 
-int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
+int git_cond_init(git_cond *cond)
 {
-	/* We don't support non-default attributes. */
-	if (attr)
-		return EINVAL;
-
 	/* This is an auto-reset event. */
 	*cond = CreateEventW(NULL, FALSE, FALSE, NULL);
 	assert(*cond);
@@ -112,7 +130,7 @@
 	return *cond ? 0 : ENOMEM;
 }
 
-int pthread_cond_destroy(pthread_cond_t *cond)
+int git_cond_free(git_cond *cond)
 {
 	BOOL closed;
 
@@ -127,7 +145,7 @@
 	return 0;
 }
 
-int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
+int git_cond_wait(git_cond *cond, git_mutex *mutex)
 {
 	int error;
 	DWORD wait_result;
@@ -136,7 +154,7 @@
 		return EINVAL;
 
 	/* The caller must be holding the mutex. */
-	error = pthread_mutex_unlock(mutex);
+	error = git_mutex_unlock(mutex);
 
 	if (error)
 		return error;
@@ -145,10 +163,10 @@
 	assert(WAIT_OBJECT_0 == wait_result);
 	GIT_UNUSED(wait_result);
 
-	return pthread_mutex_lock(mutex);
+	return git_mutex_lock(mutex);
 }
 
-int pthread_cond_signal(pthread_cond_t *cond)
+int git_cond_signal(git_cond *cond)
 {
 	BOOL signaled;
 
@@ -162,36 +180,8 @@
 	return 0;
 }
 
-/* pthread_cond_broadcast is not implemented because doing so with just
- * Win32 events is quite complicated, and no caller in libgit2 uses it
- * yet.
- */
-int pthread_num_processors_np(void)
+int git_rwlock_init(git_rwlock *GIT_RESTRICT lock)
 {
-	DWORD_PTR p, s;
-	int n = 0;
-
-	if (GetProcessAffinityMask(GetCurrentProcess(), &p, &s))
-		for (; p; p >>= 1)
-			n += p&1;
-
-	return n ? n : 1;
-}
-
-typedef void (WINAPI *win32_srwlock_fn)(GIT_SRWLOCK *);
-
-static win32_srwlock_fn win32_srwlock_initialize;
-static win32_srwlock_fn win32_srwlock_acquire_shared;
-static win32_srwlock_fn win32_srwlock_release_shared;
-static win32_srwlock_fn win32_srwlock_acquire_exclusive;
-static win32_srwlock_fn win32_srwlock_release_exclusive;
-
-int pthread_rwlock_init(
-	pthread_rwlock_t *GIT_RESTRICT lock,
-	const pthread_rwlockattr_t *GIT_RESTRICT attr)
-{
-	GIT_UNUSED(attr);
-
 	if (win32_srwlock_initialize)
 		win32_srwlock_initialize(&lock->native.srwl);
 	else
@@ -200,7 +190,7 @@
 	return 0;
 }
 
-int pthread_rwlock_rdlock(pthread_rwlock_t *lock)
+int git_rwlock_rdlock(git_rwlock *lock)
 {
 	if (win32_srwlock_acquire_shared)
 		win32_srwlock_acquire_shared(&lock->native.srwl);
@@ -210,7 +200,7 @@
 	return 0;
 }
 
-int pthread_rwlock_rdunlock(pthread_rwlock_t *lock)
+int git_rwlock_rdunlock(git_rwlock *lock)
 {
 	if (win32_srwlock_release_shared)
 		win32_srwlock_release_shared(&lock->native.srwl);
@@ -220,7 +210,7 @@
 	return 0;
 }
 
-int pthread_rwlock_wrlock(pthread_rwlock_t *lock)
+int git_rwlock_wrlock(git_rwlock *lock)
 {
 	if (win32_srwlock_acquire_exclusive)
 		win32_srwlock_acquire_exclusive(&lock->native.srwl);
@@ -230,7 +220,7 @@
 	return 0;
 }
 
-int pthread_rwlock_wrunlock(pthread_rwlock_t *lock)
+int git_rwlock_wrunlock(git_rwlock *lock)
 {
 	if (win32_srwlock_release_exclusive)
 		win32_srwlock_release_exclusive(&lock->native.srwl);
@@ -240,30 +230,10 @@
 	return 0;
 }
 
-int pthread_rwlock_destroy(pthread_rwlock_t *lock)
+int git_rwlock_free(git_rwlock *lock)
 {
 	if (!win32_srwlock_initialize)
 		DeleteCriticalSection(&lock->native.csec);
 	git__memzero(lock, sizeof(*lock));
 	return 0;
 }
-
-int win32_pthread_initialize(void)
-{
-	HMODULE hModule = GetModuleHandleW(L"kernel32");
-
-	if (hModule) {
-		win32_srwlock_initialize = (win32_srwlock_fn)
-			GetProcAddress(hModule, "InitializeSRWLock");
-		win32_srwlock_acquire_shared = (win32_srwlock_fn)
-			GetProcAddress(hModule, "AcquireSRWLockShared");
-		win32_srwlock_release_shared = (win32_srwlock_fn)
-			GetProcAddress(hModule, "ReleaseSRWLockShared");
-		win32_srwlock_acquire_exclusive = (win32_srwlock_fn)
-			GetProcAddress(hModule, "AcquireSRWLockExclusive");
-		win32_srwlock_release_exclusive = (win32_srwlock_fn)
-			GetProcAddress(hModule, "ReleaseSRWLockExclusive");
-	}
-
-	return 0;
-}
diff --git a/src/win32/thread.h b/src/win32/thread.h
new file mode 100644
index 0000000..0d01822
--- /dev/null
+++ b/src/win32/thread.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_win32_thread_h__
+#define INCLUDE_win32_thread_h__
+
+#include "../common.h"
+
+#if defined (_MSC_VER)
+#	define GIT_RESTRICT __restrict
+#else
+#	define GIT_RESTRICT __restrict__
+#endif
+
+typedef struct {
+	HANDLE thread;
+	void *(*proc)(void *);
+	void *param;
+	void *result;
+} git_thread;
+
+typedef CRITICAL_SECTION git_mutex;
+typedef HANDLE git_cond;
+
+typedef struct { void *Ptr; } GIT_SRWLOCK;
+
+typedef struct {
+	union {
+		GIT_SRWLOCK srwl;
+		CRITICAL_SECTION csec;
+	} native;
+} git_rwlock;
+
+int git_threads_init(void);
+
+int git_thread_create(git_thread *GIT_RESTRICT,
+	void *(*) (void *),
+	void *GIT_RESTRICT);
+int git_thread_join(git_thread *, void **);
+
+int git_mutex_init(git_mutex *GIT_RESTRICT mutex);
+int git_mutex_free(git_mutex *);
+int git_mutex_lock(git_mutex *);
+int git_mutex_unlock(git_mutex *);
+
+int git_cond_init(git_cond *);
+int git_cond_free(git_cond *);
+int git_cond_wait(git_cond *, git_mutex *);
+int git_cond_signal(git_cond *);
+
+int git_rwlock_init(git_rwlock *GIT_RESTRICT lock);
+int git_rwlock_rdlock(git_rwlock *);
+int git_rwlock_rdunlock(git_rwlock *);
+int git_rwlock_wrlock(git_rwlock *);
+int git_rwlock_wrunlock(git_rwlock *);
+int git_rwlock_free(git_rwlock *);
+
+#endif /* INCLUDE_win32_thread_h__ */
diff --git a/tests/checkout/index.c b/tests/checkout/index.c
index 8af3e56..ca63dc3 100644
--- a/tests/checkout/index.c
+++ b/tests/checkout/index.c
@@ -294,11 +294,12 @@
 	(void)p_umask(um = p_umask(022));
 
 	cl_git_pass(p_stat("./testrepo/a", &st));
-	cl_assert_equal_i_fmt(st.st_mode, (GIT_FILEMODE_TREE | 0701) & ~um, "%07o");
+	/* Haiku & Hurd use other mode bits, so we must mask them out */
+	cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), (GIT_FILEMODE_TREE | 0701) & ~um, "%07o");
 
 	/* File-mode test, since we're on the 'dir' branch */
 	cl_git_pass(p_stat("./testrepo/a/b.txt", &st));
-	cl_assert_equal_i_fmt(st.st_mode, GIT_FILEMODE_BLOB_EXECUTABLE & ~um, "%07o");
+	cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), GIT_FILEMODE_BLOB_EXECUTABLE & ~um, "%07o");
 
 	git_commit_free(commit);
 }
diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c
index 5680b86..4a0314a 100644
--- a/tests/checkout/tree.c
+++ b/tests/checkout/tree.c
@@ -1416,3 +1416,70 @@
 	git_object_free(obj);
 }
 
+static int checkout_conflict_count_cb(
+	git_checkout_notify_t why,
+	const char *path,
+	const git_diff_file *b,
+	const git_diff_file *t,
+	const git_diff_file *w,
+	void *payload)
+{
+	size_t *n = payload;
+
+	GIT_UNUSED(why);
+	GIT_UNUSED(path);
+	GIT_UNUSED(b);
+	GIT_UNUSED(t);
+	GIT_UNUSED(w);
+
+	(*n)++;
+
+	return 0;
+}
+
+/* A repo that has a HEAD (even a properly born HEAD that peels to
+ * a commit) but no index should be treated as if it's an empty baseline
+ */
+void test_checkout_tree__baseline_is_empty_when_no_index(void)
+{
+	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+	git_reference *head;
+	git_object *obj;
+	git_status_list *status;
+	size_t conflicts = 0;
+
+	assert_on_branch(g_repo, "master");
+	cl_git_pass(git_repository_head(&head, g_repo));
+	cl_git_pass(git_reference_peel(&obj, head, GIT_OBJ_COMMIT));
+
+	cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL));
+
+	cl_must_pass(p_unlink("testrepo/.git/index"));
+
+	/* for a safe checkout, we should have checkout conflicts with
+	 * the existing untracked files.
+	 */
+	opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
+	opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
+	opts.notify_cb = checkout_conflict_count_cb;
+	opts.notify_payload = &conflicts;
+
+	cl_git_fail_with(GIT_ECONFLICT, git_checkout_tree(g_repo, obj, &opts));
+	cl_assert_equal_i(4, conflicts);
+
+	/* but force should succeed and update the index */
+	opts.checkout_strategy |= GIT_CHECKOUT_FORCE;
+	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+	cl_git_pass(git_status_list_new(&status, g_repo, NULL));
+	cl_assert_equal_i(0, git_status_list_entrycount(status));
+	git_status_list_free(status);
+
+	git_object_free(obj);
+	git_reference_free(head);
+}
+
+void test_checkout_tree__nullopts(void)
+{
+	cl_git_pass(git_checkout_tree(g_repo, NULL, NULL));
+}
diff --git a/tests/checkout/typechange.c b/tests/checkout/typechange.c
index b4959a3..8a5110c 100644
--- a/tests/checkout/typechange.c
+++ b/tests/checkout/typechange.c
@@ -6,6 +6,36 @@
 
 static git_repository *g_repo = NULL;
 
+/*
+From the test repo used for this test:
+--------------------------------------
+
+This is a test repo for libgit2 where tree entries have type changes
+
+The key types that could be found in tree entries are:
+
+1 - GIT_FILEMODE_NEW             = 0000000
+2 - GIT_FILEMODE_TREE            = 0040000
+3 - GIT_FILEMODE_BLOB            = 0100644
+4 - GIT_FILEMODE_BLOB_EXECUTABLE = 0100755
+5 - GIT_FILEMODE_LINK            = 0120000
+6 - GIT_FILEMODE_COMMIT          = 0160000
+
+I will try to have every type of transition somewhere in the history
+of this repo.
+
+Commits
+-------
+Initial commit - a(1)    b(1)    c(1)    d(1)    e(1)
+Create content - a(1->2) b(1->3) c(1->4) d(1->5) e(1->6)
+Changes #1     - a(2->3) b(3->4) c(4->5) d(5->6) e(6->2)
+Changes #2     - a(3->5) b(4->6) c(5->2) d(6->3) e(2->4)
+Changes #3     - a(5->3) b(6->4) c(2->5) d(3->6) e(4->2)
+Changes #4     - a(3->2) b(4->3) c(5->4) d(6->5) e(2->6)
+Changes #5     - a(2->1) b(3->1) c(4->1) d(5->1) e(6->1)
+
+*/
+
 static const char *g_typechange_oids[] = {
 	"79b9f23e85f55ea36a472a902e875bc1121a94cb",
 	"9bdb75b73836a99e3dbeea640a81de81031fdc29",
@@ -21,6 +51,14 @@
 	true, false, false, false, false, false, true, true
 };
 
+static const int g_typechange_expected_conflicts[] = {
+	1, 2, 3, 3, 2, 3, 2
+};
+
+static const int g_typechange_expected_untracked[] = {
+	6, 4, 3, 2, 3, 2, 5
+};
+
 void test_checkout_typechange__initialize(void)
 {
 	g_repo = cl_git_sandbox_init("typechanges");
@@ -112,12 +150,7 @@
 	for (i = 0; g_typechange_oids[i] != NULL; ++i) {
 		cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
 
-		opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
-		/* There are bugs in some submodule->tree changes that prevent
-		 * SAFE from passing here, even though the following should work:
-		 */
-		/* !i ? GIT_CHECKOUT_FORCE : GIT_CHECKOUT_SAFE; */
+		opts.checkout_strategy = !i ? GIT_CHECKOUT_FORCE : GIT_CHECKOUT_SAFE;
 
 		cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
 
@@ -190,6 +223,38 @@
 	cl_git_rewritefile(file, "yowza!!");
 }
 
+static int make_submodule_dirty(git_submodule *sm, const char *name, void *payload)
+{
+	git_buf submodulepath = GIT_BUF_INIT;
+	git_buf dirtypath = GIT_BUF_INIT;
+	git_repository *submodule_repo;
+
+	GIT_UNUSED(name);
+	GIT_UNUSED(payload);
+
+	/* remove submodule directory in preparation for init and repo_init */
+	cl_git_pass(git_buf_joinpath(
+		&submodulepath,
+		git_repository_workdir(g_repo),
+		git_submodule_path(sm)
+	));
+	git_futils_rmdir_r(git_buf_cstr(&submodulepath), NULL, GIT_RMDIR_REMOVE_FILES);
+
+	/* initialize submodule's repository */
+	cl_git_pass(git_submodule_repo_init(&submodule_repo, sm, 0));
+
+	/* create a file in the submodule workdir to make it dirty */
+	cl_git_pass(
+		git_buf_joinpath(&dirtypath, git_repository_workdir(submodule_repo), "dirty"));
+	force_create_file(git_buf_cstr(&dirtypath));
+
+	git_buf_free(&dirtypath);
+	git_buf_free(&submodulepath);
+	git_repository_free(submodule_repo);
+
+	return 0;
+}
+
 void test_checkout_typechange__checkout_with_conflicts(void)
 {
 	int i;
@@ -211,13 +276,17 @@
 		git_futils_rmdir_r("typechanges/d", NULL, GIT_RMDIR_REMOVE_FILES);
 		p_mkdir("typechanges/d", 0777); /* intentionally empty dir */
 		force_create_file("typechanges/untracked");
+		cl_git_pass(git_submodule_foreach(g_repo, make_submodule_dirty, NULL));
 
 		opts.checkout_strategy = GIT_CHECKOUT_SAFE;
 		memset(&cts, 0, sizeof(cts));
 
 		cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
-		cl_assert(cts.conflicts > 0);
-		cl_assert(cts.untracked > 0);
+		cl_assert_equal_i(cts.conflicts, g_typechange_expected_conflicts[i]);
+		cl_assert_equal_i(cts.untracked, g_typechange_expected_untracked[i]);
+		cl_assert_equal_i(cts.dirty, 0);
+		cl_assert_equal_i(cts.updates, 0);
+		cl_assert_equal_i(cts.ignored, 0);
 
 		opts.checkout_strategy =
 			GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
diff --git a/tests/core/strtol.c b/tests/core/strtol.c
index 8765e04..0d3b6a5 100644
--- a/tests/core/strtol.c
+++ b/tests/core/strtol.c
@@ -33,5 +33,13 @@
 	cl_assert(i == 2147483657LL);
 	cl_git_pass(git__strtol64(&i, "  -2147483657 ", NULL, 10));
 	cl_assert(i == -2147483657LL);
+	cl_git_pass(git__strtol64(&i, " 9223372036854775807  ", NULL, 10));
+	cl_assert(i == INT64_MAX);
+	cl_git_pass(git__strtol64(&i, "   -9223372036854775808  ", NULL, 10));
+	cl_assert(i == INT64_MIN);
+	cl_git_pass(git__strtol64(&i, "   0x7fffffffffffffff  ", NULL, 16));
+	cl_assert(i == INT64_MAX);
+	cl_git_pass(git__strtol64(&i, "   -0x8000000000000000   ", NULL, 16));
+	cl_assert(i == INT64_MIN);
 }
 
diff --git a/tests/index/collision.c b/tests/index/collision.c
index 19c1548..ad5827e 100644
--- a/tests/index/collision.c
+++ b/tests/index/collision.c
@@ -2,105 +2,103 @@
 #include "git2/repository.h"
 #include "git2/index.h"
 
-git_repository *repo = NULL;
+static git_repository *g_repo;
+static git_odb *g_odb;
+static git_index *g_index;
+static git_oid g_empty_id;
+
+void test_index_collision__initialize(void)
+{
+	g_repo = cl_git_sandbox_init("empty_standard_repo");
+	cl_git_pass(git_repository_odb(&g_odb, g_repo));
+	cl_git_pass(git_repository_index(&g_index, g_repo));
+
+	cl_git_pass(git_odb_write(&g_empty_id, g_odb, "", 0, GIT_OBJ_BLOB));
+}
 
 void test_index_collision__cleanup(void)
 {
+	git_index_free(g_index);
+	git_odb_free(g_odb);
 	cl_git_sandbox_cleanup();
-	repo = NULL;
 }
 
 void test_index_collision__add(void)
 {
-	git_index *index;
 	git_index_entry entry;
 	git_oid tree_id;
 	git_tree *tree;
 
-	repo = cl_git_sandbox_init("empty_standard_repo");
-	cl_git_pass(git_repository_index(&index, repo));
-
 	memset(&entry, 0, sizeof(entry));
 	entry.ctime.seconds = 12346789;
 	entry.mtime.seconds = 12346789;
 	entry.mode  = 0100644;
 	entry.file_size = 0;
-	git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391");
+	git_oid_cpy(&entry.id, &g_empty_id);
 
 	entry.path = "a/b";
-	cl_git_pass(git_index_add(index, &entry));
+	cl_git_pass(git_index_add(g_index, &entry));
 
 	/* create a tree/blob collision */
 	entry.path = "a/b/c";
-	cl_git_fail(git_index_add(index, &entry));
+	cl_git_fail(git_index_add(g_index, &entry));
 
-	cl_git_pass(git_index_write_tree(&tree_id, index));
-	cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
+	cl_git_pass(git_index_write_tree(&tree_id, g_index));
+	cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
 
 	git_tree_free(tree);
-	git_index_free(index);
 }
 
 void test_index_collision__add_with_highstage_1(void)
 {
-	git_index *index;
 	git_index_entry entry;
 
-	repo = cl_git_sandbox_init("empty_standard_repo");
-	cl_git_pass(git_repository_index(&index, repo));
-
 	memset(&entry, 0, sizeof(entry));
 	entry.ctime.seconds = 12346789;
 	entry.mtime.seconds = 12346789;
 	entry.mode  = 0100644;
 	entry.file_size = 0;
-	git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391");
+	git_oid_cpy(&entry.id, &g_empty_id);
 
 	entry.path = "a/b";
 	GIT_IDXENTRY_STAGE_SET(&entry, 2);
-	cl_git_pass(git_index_add(index, &entry));
+	cl_git_pass(git_index_add(g_index, &entry));
 
 	/* create a blob beneath the previous tree entry */
 	entry.path = "a/b/c";
 	entry.flags = 0;
-	cl_git_pass(git_index_add(index, &entry));
+	cl_git_pass(git_index_add(g_index, &entry));
 
 	/* create another tree entry above the blob */
 	entry.path = "a/b";
 	GIT_IDXENTRY_STAGE_SET(&entry, 1);
-	cl_git_pass(git_index_add(index, &entry));
-
-	git_index_free(index);
+	cl_git_pass(git_index_add(g_index, &entry));
 }
 
 void test_index_collision__add_with_highstage_2(void)
 {
-	git_index *index;
 	git_index_entry entry;
 
-	repo = cl_git_sandbox_init("empty_standard_repo");
-	cl_git_pass(git_repository_index(&index, repo));
+	cl_git_pass(git_repository_index(&g_index, g_repo));
 
 	memset(&entry, 0, sizeof(entry));
 	entry.ctime.seconds = 12346789;
 	entry.mtime.seconds = 12346789;
 	entry.mode  = 0100644;
 	entry.file_size = 0;
-	git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391");
+	git_oid_cpy(&entry.id, &g_empty_id);
 
 	entry.path = "a/b/c";
 	GIT_IDXENTRY_STAGE_SET(&entry, 1);
-	cl_git_pass(git_index_add(index, &entry));
+	cl_git_pass(git_index_add(g_index, &entry));
 
 	/* create a blob beneath the previous tree entry */
 	entry.path = "a/b/c";
 	GIT_IDXENTRY_STAGE_SET(&entry, 2);
-	cl_git_pass(git_index_add(index, &entry));
+	cl_git_pass(git_index_add(g_index, &entry));
 
 	/* create another tree entry above the blob */
 	entry.path = "a/b";
 	GIT_IDXENTRY_STAGE_SET(&entry, 3);
-	cl_git_pass(git_index_add(index, &entry));
-
-	git_index_free(index);
+	cl_git_pass(git_index_add(g_index, &entry));
 }
diff --git a/tests/index/read_index.c b/tests/index/read_index.c
index 82a771d..6d14bc9 100644
--- a/tests/index/read_index.c
+++ b/tests/index/read_index.c
@@ -71,3 +71,58 @@
 		}
 	}
 }
+
+static bool roundtrip_with_read_index(const char *tree_idstr)
+{
+	git_oid tree_id, new_tree_id;
+	git_tree *tree;
+	git_index *tree_index;
+
+	cl_git_pass(git_oid_fromstr(&tree_id, tree_idstr));
+	cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id));
+	cl_git_pass(git_index_new(&tree_index));
+	cl_git_pass(git_index_read_tree(tree_index, tree));
+	cl_git_pass(git_index_read_index(_index, tree_index));
+	cl_git_pass(git_index_write_tree(&new_tree_id, _index));
+
+	git_tree_free(tree);
+	git_index_free(tree_index);
+
+	return git_oid_equal(&tree_id, &new_tree_id);
+}
+
+void test_index_read_index__produces_treesame_indexes(void)
+{
+	roundtrip_with_read_index("53fc32d17276939fc79ed05badaef2db09990016");
+	roundtrip_with_read_index("944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
+	roundtrip_with_read_index("1810dff58d8a660512d4832e740f692884338ccd");
+	roundtrip_with_read_index("d52a8fe84ceedf260afe4f0287bbfca04a117e83");
+	roundtrip_with_read_index("c36d8ea75da8cb510fcb0c408c1d7e53f9a99dbe");
+	roundtrip_with_read_index("7b2417a23b63e1fdde88c80e14b33247c6e5785a");
+	roundtrip_with_read_index("f82a8eb4cb20e88d1030fd10d89286215a715396");
+	roundtrip_with_read_index("fd093bff70906175335656e6ce6ae05783708765");
+	roundtrip_with_read_index("ae90f12eea699729ed24555e40b9fd669da12a12");
+}
+
+void test_index_read_index__read_and_writes(void)
+{
+	git_oid tree_id, new_tree_id;
+	git_tree *tree;
+	git_index *tree_index, *new_index;
+
+	cl_git_pass(git_oid_fromstr(&tree_id, "ae90f12eea699729ed24555e40b9fd669da12a12"));
+	cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id));
+	cl_git_pass(git_index_new(&tree_index));
+	cl_git_pass(git_index_read_tree(tree_index, tree));
+	cl_git_pass(git_index_read_index(_index, tree_index));
+	cl_git_pass(git_index_write(_index));
+
+	cl_git_pass(git_index_open(&new_index, git_index_path(_index)));
+	cl_git_pass(git_index_write_tree_to(&new_tree_id, new_index, _repo));
+
+	cl_assert_equal_oid(&tree_id, &new_tree_id);
+
+	git_tree_free(tree);
+	git_index_free(tree_index);
+	git_index_free(new_index);
+}
diff --git a/tests/object/cache.c b/tests/object/cache.c
index bdf12da..680f236 100644
--- a/tests/object/cache.c
+++ b/tests/object/cache.c
@@ -220,7 +220,7 @@
 			fn = (th & 1) ? cache_parsed : cache_raw;
 
 #ifdef GIT_THREADS
-			cl_git_pass(git_thread_create(&t[th], NULL, fn, data));
+			cl_git_pass(git_thread_create(&t[th], fn, data));
 #else
 			cl_assert(fn(data) == data);
 			git__free(data);
@@ -267,7 +267,7 @@
 			data[th] = th;
 #ifdef GIT_THREADS
 			cl_git_pass(
-				git_thread_create(&t[th], NULL, cache_quick, &data[th]));
+				git_thread_create(&t[th], cache_quick, &data[th]));
 #else
 			cl_assert(cache_quick(&data[th]) == &data[th]);
 #endif
diff --git a/tests/object/tag/read.c b/tests/object/tag/read.c
index c9787a4..8f28afd 100644
--- a/tests/object/tag/read.c
+++ b/tests/object/tag/read.c
@@ -140,3 +140,40 @@
 	git_tag_free(tag);
 	git_repository_free(repo);
 }
+
+static const char *silly_tag = "object c054ccaefbf2da31c3b19178f9e3ef20a3867924\n\
+type commit\n\
+tag v1_0_1\n\
+tagger Jamis Buck <jamis@37signals.com> 1107717917\n\
+diff --git a/lib/sqlite3/version.rb b/lib/sqlite3/version.rb\n\
+index 0b3bf69..4ee8fc2 100644\n\
+--- a/lib/sqlite3/version.rb\n\
++++ b/lib/sqlite3/version.rb\n\
+@@ -36,7 +36,7 @@ module SQLite3\n\
+ \n\
+     MAJOR = 1\n\
+     MINOR = 0\n\
+-    TINY  = 0\n\
++    TINY  = 1\n\
+ \n\
+     STRING = [ MAJOR, MINOR, TINY ].join( \".\" )\n\
+ \n\
+ -0600\n\
+\n\
+v1_0_1 release\n";
+
+void test_object_tag_read__extra_header_fields(void)
+{
+	git_tag *tag;
+	git_odb *odb;
+	git_oid id;
+
+	cl_git_pass(git_repository_odb__weakptr(&odb, g_repo));
+
+	cl_git_pass(git_odb_write(&id, odb, silly_tag, strlen(silly_tag), GIT_OBJ_TAG));
+	cl_git_pass(git_tag_lookup(&tag, g_repo, &id));
+
+	cl_assert_equal_s("v1_0_1 release\n", git_tag_message(tag));
+
+	git_tag_free(tag);
+}
diff --git a/tests/odb/emptyobjects.c b/tests/odb/emptyobjects.c
index 783d051..61bb2b8 100644
--- a/tests/odb/emptyobjects.c
+++ b/tests/odb/emptyobjects.c
@@ -2,29 +2,33 @@
 #include "odb.h"
 #include "filebuf.h"
 
+#define TEST_REPO_PATH "redundant.git"
+
 git_repository *g_repo;
+git_odb *g_odb;
 
 void test_odb_emptyobjects__initialize(void)
 {
-	cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
-}
-void test_odb_emptyobjects__cleanup(void)
-{
-	git_repository_free(g_repo);
+	g_repo = cl_git_sandbox_init(TEST_REPO_PATH);
+	cl_git_pass(git_repository_odb(&g_odb, g_repo));
 }
 
-void test_odb_emptyobjects__read(void)
+void test_odb_emptyobjects__cleanup(void)
 {
-	git_oid id;
+	git_odb_free(g_odb);
+	cl_git_sandbox_cleanup();
+}
+
+void test_odb_emptyobjects__blob_notfound(void)
+{
+	git_oid id, written_id;
 	git_blob *blob;
 
 	cl_git_pass(git_oid_fromstr(&id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"));
-	cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
-	cl_assert_equal_i(GIT_OBJ_BLOB, git_object_type((git_object *) blob));
-	cl_assert(git_blob_rawcontent(blob));
-	cl_assert_equal_s("", git_blob_rawcontent(blob));
-	cl_assert_equal_i(0, git_blob_rawsize(blob));
-	git_blob_free(blob);
+	cl_git_fail_with(GIT_ENOTFOUND, git_blob_lookup(&blob, g_repo, &id));
+
+	cl_git_pass(git_odb_write(&written_id, g_odb, "", 0, GIT_OBJ_BLOB));
+	cl_assert(git_path_exists(TEST_REPO_PATH "/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391"));
 }
 
 void test_odb_emptyobjects__read_tree(void)
@@ -43,15 +47,12 @@
 void test_odb_emptyobjects__read_tree_odb(void)
 {
 	git_oid id;
-	git_odb *odb;
 	git_odb_object *tree_odb;
 
 	cl_git_pass(git_oid_fromstr(&id, "4b825dc642cb6eb9a060e54bf8d69288fbee4904"));
-	cl_git_pass(git_repository_odb(&odb, g_repo));
-	cl_git_pass(git_odb_read(&tree_odb, odb, &id));
+	cl_git_pass(git_odb_read(&tree_odb, g_odb, &id));
 	cl_assert(git_odb_object_data(tree_odb));
 	cl_assert_equal_s("", git_odb_object_data(tree_odb));
 	cl_assert_equal_i(0, git_odb_object_size(tree_odb));
 	git_odb_object_free(tree_odb);
-	git_odb_free(odb);
 }
diff --git a/tests/online/fetchhead.c b/tests/online/fetchhead.c
index 200edac..9aaad25 100644
--- a/tests/online/fetchhead.c
+++ b/tests/online/fetchhead.c
@@ -35,6 +35,19 @@
 	cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
 }
 
+static int count_references(void)
+{
+	git_strarray array;
+	int refs;
+
+	cl_git_pass(git_reference_list(&array, g_repo));
+	refs = array.count;
+
+	git_strarray_free(&array);
+
+	return refs;
+}
+
 static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fetchhead)
 {
 	git_remote *remote;
@@ -101,3 +114,41 @@
 	cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
 	fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA3);
 }
+
+void test_online_fetchhead__explicit_dst_refspec_creates_branch(void)
+{
+	git_reference *ref;
+	int refs;
+
+	fetchhead_test_clone();
+	refs = count_references();
+	fetchhead_test_fetch("refs/heads/first-merge:refs/heads/explicit-refspec", FETCH_HEAD_EXPLICIT_DATA);
+
+	cl_git_pass(git_branch_lookup(&ref, g_repo, "explicit-refspec", GIT_BRANCH_ALL));
+	cl_assert_equal_i(refs + 1, count_references());
+}
+
+void test_online_fetchhead__empty_dst_refspec_creates_no_branch(void)
+{
+	git_reference *ref;
+	int refs;
+
+	fetchhead_test_clone();
+	refs = count_references();
+
+	fetchhead_test_fetch("refs/heads/first-merge", FETCH_HEAD_EXPLICIT_DATA);
+	cl_git_fail(git_branch_lookup(&ref, g_repo, "first-merge", GIT_BRANCH_ALL));
+
+	cl_assert_equal_i(refs, count_references());
+}
+
+void test_online_fetchhead__colon_only_dst_refspec_creates_no_branch(void)
+{
+	int refs;
+
+	fetchhead_test_clone();
+	refs = count_references();
+	fetchhead_test_fetch("refs/heads/first-merge:", FETCH_HEAD_EXPLICIT_DATA);
+
+	cl_assert_equal_i(refs, count_references());
+}
diff --git a/tests/repo/discover.c b/tests/repo/discover.c
index 86bd745..358daee 100644
--- a/tests/repo/discover.c
+++ b/tests/repo/discover.c
@@ -118,12 +118,22 @@
 	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);
+
+	/* 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));
+
 	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
-	cl_git_pass(git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
+	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));
diff --git a/tests/repo/open.c b/tests/repo/open.c
index d3d0872..7cdd182 100644
--- a/tests/repo/open.c
+++ b/tests/repo/open.c
@@ -196,8 +196,9 @@
 		&repo, "attr/sub", GIT_REPOSITORY_OPEN_NO_SEARCH, NULL));
 
 	/* fail with ceiling too low */
-	cl_git_pass(git_buf_joinpath(&ceiling, ceiling.ptr, "sub"));
 	cl_git_fail(git_repository_open_ext(&repo, "attr/sub", 0, ceiling.ptr));
+	cl_git_pass(git_buf_joinpath(&ceiling, ceiling.ptr, "sub"));
+	cl_git_fail(git_repository_open_ext(&repo, "attr/sub/sub", 0, ceiling.ptr));
 
 	/* fail with no repo */
 	cl_git_pass(p_mkdir("alternate", 0777));
diff --git a/tests/reset/hard.c b/tests/reset/hard.c
index e461f80..69ef41e 100644
--- a/tests/reset/hard.c
+++ b/tests/reset/hard.c
@@ -240,14 +240,18 @@
 {
 	git_index_entry entry = {{ 0 }};
 	git_index *idx;
+	git_odb *odb;
 	git_object *commit;
 	git_tree *tree;
 	git_signature *sig;
 	git_oid src_tree_id, tgt_tree_id;
 	git_oid src_id, tgt_id;
 
+	cl_git_pass(git_repository_odb(&odb, repo));
+	cl_git_pass(git_odb_write(&entry.id, odb, "", 0, GIT_OBJ_BLOB));
+	git_odb_free(odb);
+
 	entry.mode = GIT_FILEMODE_BLOB;
-	cl_git_pass(git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"));
 	cl_git_pass(git_index_new(&idx));
 	cl_git_pass(git_signature_now(&sig, "foo", "bar"));
 
diff --git a/tests/status/ignore.c b/tests/status/ignore.c
index c318046..c4878b2 100644
--- a/tests/status/ignore.c
+++ b/tests/status/ignore.c
@@ -945,6 +945,44 @@
 	assert_is_ignored("padded_parent/child8/bar.txt");
 }
 
+void test_status_ignore__unignore_entry_in_ignored_dir(void)
+{
+	static const char *test_files[] = {
+		"empty_standard_repo/bar.txt",
+		"empty_standard_repo/parent/bar.txt",
+		"empty_standard_repo/parent/child/bar.txt",
+		"empty_standard_repo/nested/parent/child/bar.txt",
+		NULL
+	};
+
+	make_test_data("empty_standard_repo", test_files);
+	cl_git_mkfile(
+		"empty_standard_repo/.gitignore",
+		"bar.txt\n"
+		"!parent/child/bar.txt\n");
+
+	assert_is_ignored("bar.txt");
+	assert_is_ignored("parent/bar.txt");
+	refute_is_ignored("parent/child/bar.txt");
+	assert_is_ignored("nested/parent/child/bar.txt");
+}
+
+void test_status_ignore__do_not_unignore_basename_prefix(void)
+{
+	static const char *test_files[] = {
+		"empty_standard_repo/foo_bar.txt",
+		NULL
+	};
+
+	make_test_data("empty_standard_repo", test_files);
+	cl_git_mkfile(
+		"empty_standard_repo/.gitignore",
+		"foo_bar.txt\n"
+		"!bar.txt\n");
+
+	assert_is_ignored("foo_bar.txt");
+}
+
 void test_status_ignore__filename_with_cr(void)
 {
 	int ignored;
diff --git a/tests/threads/refdb.c b/tests/threads/refdb.c
index 6589e39..f869bcb 100644
--- a/tests/threads/refdb.c
+++ b/tests/threads/refdb.c
@@ -75,7 +75,7 @@
 		for (t = 0; t < THREADS; ++t) {
 			id[t] = t;
 #ifdef GIT_THREADS
-			cl_git_pass(git_thread_create(&th[t], NULL, iterate_refs, &id[t]));
+			cl_git_pass(git_thread_create(&th[t], iterate_refs, &id[t]));
 #else
 			th[t] = t;
 			iterate_refs(&id[t]);
@@ -196,7 +196,7 @@
 		 * for now by just running on a single thread...
 		 */
 /* #ifdef GIT_THREADS */
-/*		cl_git_pass(git_thread_create(&th[t], NULL, fn, &id[t])); */
+/*		cl_git_pass(git_thread_create(&th[t], fn, &id[t])); */
 /* #else */
 		fn(&id[t]);
 /* #endif */
@@ -211,7 +211,7 @@
 
 	for (t = 0; t < THREADS; ++t) {
 		id[t] = t;
-		cl_git_pass(git_thread_create(&th[t], NULL, iterate_refs, &id[t]));
+		cl_git_pass(git_thread_create(&th[t], iterate_refs, &id[t]));
 	}
 
 	for (t = 0; t < THREADS; ++t) {
diff --git a/tests/threads/thread_helpers.c b/tests/threads/thread_helpers.c
index 760a7bd..54bf609 100644
--- a/tests/threads/thread_helpers.c
+++ b/tests/threads/thread_helpers.c
@@ -24,7 +24,7 @@
 		for (t = 0; t < threads; ++t) {
 			id[t] = t;
 #ifdef GIT_THREADS
-			cl_git_pass(git_thread_create(&th[t], NULL, func, &id[t]));
+			cl_git_pass(git_thread_create(&th[t], func, &id[t]));
 #else
 			cl_assert(func(&id[t]) == &id[t]);
 #endif