Merge branch 'pr/3957'
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..0ae75e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,23 @@
-v0.24 + 1
+v0.25 + 1
+-------
+
+### Changes or improvements
+
+* `GIT_MERGE_OPTIONS_INIT` now includes a setting to perform rename detection.
+  This aligns this structure with the default by `git_merge` and
+  `git_merge_trees` when `NULL` was provided for the options.
+
+### API additions
+
+### API removals
+
+### Breaking API changes
+
+* `clone_checkout_strategy` has been removed from
+  `git_submodule_update_option`. The checkout strategy used to clone will
+  be the same strategy specified in `checkout_opts`.
+
+v0.25
 -------
 
 ### Changes or improvements
@@ -17,14 +36,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 +92,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 +158,13 @@
   If this is `NULL`, then it will not be called and the `exists` function
   will be used instead.
 
+* `git_remote_connect()` now accepts `git_proxy_options` argument, and
+  `git_fetch_options` and `git_push_options` each have a `proxy_opts` field.
+
+* `git_merge_options` now provides a `default_driver` that can be used
+  to provide the name of a merge driver to be used to handle files changed
+  during a merge.
+
 v0.24
 -------
 
@@ -155,10 +240,6 @@
 
 ### Breaking API changes
 
-* `git_merge_options` now provides a `default_driver` that can be used
-  to provide the name of a merge driver to be used to handle files changed
-  during a merge.
-
 * The `git_merge_tree_flag_t` is now `git_merge_flag_t`.  Subsequently,
   its members are no longer prefixed with `GIT_MERGE_TREE_FLAG` but are
   now prefixed with `GIT_MERGE_FLAG`, and the `tree_flags` field of the
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f26f468..d5bf21f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,6 +37,7 @@
 OPTION( ENABLE_TRACE		"Enables tracing support"				OFF )
 OPTION( LIBGIT2_FILENAME	"Name of the produced binary"			OFF )
 
+OPTION( USE_SHA1DC			"Use SHA-1 with collision detection"	OFF )
 OPTION( USE_ICONV			"Link with and use iconv library" 		OFF )
 OPTION( USE_SSH				"Link with libssh to enable SSH support" ON )
 OPTION( USE_GSSAPI			"Link with libgssapi for SPNEGO auth"   OFF )
@@ -237,8 +238,6 @@
 
 IF (WIN32 AND WINHTTP)
 	ADD_DEFINITIONS(-DGIT_WINHTTP)
-	INCLUDE_DIRECTORIES(deps/http-parser)
-	FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
 
 	# Since MinGW does not come with headers or an import library for winhttp,
 	# we have to include a private header and generate our own import library
@@ -291,27 +290,19 @@
 		LINK_LIBRARIES(${CURL_LIBRARIES})
 		LIST(APPEND LIBGIT2_PC_LIBS ${CURL_LDFLAGS})
 	ENDIF()
-
-	FIND_PACKAGE(HTTP_Parser)
-	IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
-		INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS})
-		LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES})
-		LIST(APPEND LIBGIT2_PC_LIBS "-lhttp_parser")
-	ELSE()
-		MESSAGE(STATUS "http-parser was not found or is too old; using bundled 3rd-party sources.")
-		INCLUDE_DIRECTORIES(deps/http-parser)
-		FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
-	ENDIF()
 ENDIF()
 
 # Specify sha1 implementation
-IF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin")
-	ADD_DEFINITIONS(-DWIN32_SHA1)
+IF (USE_SHA1DC)
+	ADD_DEFINITIONS(-DGIT_SHA1_COLLISIONDETECT)
+	FILE(GLOB SRC_SHA1 src/hash/hash_collisiondetect.c src/hash/sha1dc/*)
+ELSEIF (WIN32 AND NOT MINGW)
+	ADD_DEFINITIONS(-DGIT_SHA1_WIN32)
 	FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
 ELSEIF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
-	ADD_DEFINITIONS(-DGIT_COMMON_CRYPTO)
-ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin")
-	ADD_DEFINITIONS(-DOPENSSL_SHA1)
+	ADD_DEFINITIONS(-DGIT_SHA1_COMMON_CRYPTO)
+ELSEIF (OPENSSL_FOUND)
+	ADD_DEFINITIONS(-DGIT_SHA1_OPENSSL)
 	IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
 		LIST(APPEND LIBGIT2_PC_LIBS "-lssl")
 	ELSE()
@@ -332,6 +323,18 @@
 	SET(SRC_REGEX deps/regex/regex.c)
 ENDIF()
 
+# Optional external dependency: http-parser
+FIND_PACKAGE(HTTP_Parser)
+IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
+	INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS})
+	LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES})
+	LIST(APPEND LIBGIT2_PC_LIBS "-lhttp_parser")
+ELSE()
+	MESSAGE(STATUS "http-parser version 2 was not found; using bundled 3rd-party sources.")
+	INCLUDE_DIRECTORIES(deps/http-parser)
+	FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
+ENDIF()
+
 # Optional external dependency: zlib
 FIND_PACKAGE(ZLIB)
 IF (ZLIB_FOUND)
@@ -465,7 +468,10 @@
    # Precompiled headers
 
 ELSE ()
-	SET(CMAKE_C_FLAGS "-D_GNU_SOURCE -Wall -Wextra ${CMAKE_C_FLAGS}")
+	SET(CMAKE_C_FLAGS "-D_GNU_SOURCE ${CMAKE_C_FLAGS}")
+
+	ADD_C_FLAG_IF_SUPPORTED(-Wall)
+	ADD_C_FLAG_IF_SUPPORTED(-Wextra)
 
 	IF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
 		SET(CMAKE_C_FLAGS "-std=c99 -D_POSIX_C_SOURCE=200112L -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS ${CMAKE_C_FLAGS}")
diff --git a/CONVENTIONS.md b/CONVENTIONS.md
index 0be4b33..ffb696a 100644
--- a/CONVENTIONS.md
+++ b/CONVENTIONS.md
@@ -18,11 +18,11 @@
  - If a function returns an object as a return value, that function is
    a getter and the object's lifetime is tied to the parent
    object. Objects which are returned as the first argument as a
-   pointer-to-pointer are owned by the caller and it is repsponsible
+   pointer-to-pointer are owned by the caller and it is responsible
    for freeing it. Strings are returned via `git_buf` in order to
    allow for re-use and safe freeing.
 
- - Most of what libgit2 does relates to I/O so you as a general rule
+ - Most of what libgit2 does relates to I/O so as a general rule
    you should assume that any function can fail due to errors as even
    getting data from the filesystem can result in all sorts of errors
    and complex failure cases.
diff --git a/COPYING b/COPYING
index 1b88b9b..da695eb 100644
--- a/COPYING
+++ b/COPYING
@@ -958,3 +958,36 @@
 That's all there is to it!
 
 ----------------------------------------------------------------------
+
+The bundled SHA1 collision detection code is licensed under the MIT license:
+
+MIT License
+
+Copyright (c) 2017:
+    Marc Stevens
+    Cryptology Group
+    Centrum Wiskunde & Informatica
+    P.O. Box 94079, 1090 GB Amsterdam, Netherlands
+    marc@marc-stevens.nl
+
+    Dan Shumow
+    Microsoft Research
+    danshu@microsoft.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/PROJECTS.md b/PROJECTS.md
index 87ce78f..f53a2e1 100644
--- a/PROJECTS.md
+++ b/PROJECTS.md
@@ -5,7 +5,7 @@
 welcome contributions and we promise we'll try to be nice.
 
 This is a list of libgit2 related projects that new contributors can take
-on.  It includes a number of good starter projects and well as some larger
+on.  It includes a number of good starter projects as well as some larger
 ideas that no one is actively working on.
 
 ## Before You Start
@@ -70,11 +70,11 @@
 might make good smaller projects by themselves.
 
 * Port part of the Git test suite to run against the command line emulation
-  in examples/
-    * Pick a Git command that is emulated in our examples/ area
+  in `examples/`
+    * Pick a Git command that is emulated in our `examples/` area
     * Extract the Git tests that exercise that command
     * Convert the tests to call our emulation
-    * These tests could go in examples/tests/...
+    * These tests could go in `examples/tests/`...
 * Add hooks API to enumerate and manage hooks (not run them at this point)
     * Enumeration of available hooks
     * Lookup API to see which hooks have a script and get the script
diff --git a/README.md b/README.md
index e656a46..c3fb27c 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
@@ -116,6 +124,14 @@
 
 Alternatively you can point the CMake GUI tool to the CMakeLists.txt file and generate platform specific build project or IDE workspace.
 
+Once built, you can run the tests from the `build` directory with the command
+
+	$ make test
+
+Alternatively you can run the test suite directly using,
+
+	$ ./libgit2_clar
+
 To install the library you can specify the install prefix by setting:
 
 	$ cmake .. -DCMAKE_INSTALL_PREFIX=/install/prefix
@@ -236,7 +252,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 0b9e502..430bca8 100644
--- a/THREADING.md
+++ b/THREADING.md
@@ -62,33 +62,38 @@
 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
-locking, as they provide a level of coördination which is impossible
+locking, as they provide a level of coordination which is impossible
 when using this function.
 
 See the
@@ -96,13 +101,10 @@
 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
-direct connection to libgcrypt and thus has not convenience functions for
+direct connection to libgcrypt and thus has no convenience functions for
 it (but libgcrypt has macros). Read libgcrypt's
 [threading documentation for more information](http://www.gnupg.org/documentation/manuals/gcrypt/Multi_002dThreading.html)
 
diff --git a/appveyor.yml b/appveyor.yml
index 9e94c07..fb3fff7 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -2,6 +2,7 @@
 branches:
   only:
   - master
+  - appveyor
   - /^maint.*/
 environment:
   GITTEST_INVASIVE_FS_STRUCTURE: 1
diff --git a/examples/add.c b/examples/add.c
index 0101ab9..e584989 100644
--- a/examples/add.c
+++ b/examples/add.c
@@ -75,15 +75,14 @@
 {
 	struct print_payload p = *(struct print_payload*)(payload);
 	int ret;
-	git_status_t status;
+	unsigned status;
 	(void)matched_pathspec;
 
-	if (git_status_file((unsigned int*)(&status), p.repo, path)) {
-		return -1; //abort
+	if (git_status_file(&status, p.repo, path)) {
+		return -1;
 	}
 
-	if (status & GIT_STATUS_WT_MODIFIED ||
-	         status & GIT_STATUS_WT_NEW) {
+	if (status & GIT_STATUS_WT_MODIFIED || status & GIT_STATUS_WT_NEW) {
 		printf("add '%s'\n", path);
 		ret = 0;
 	} else {
diff --git a/examples/common.c b/examples/common.c
index 0f25f37..96f5eaa 100644
--- a/examples/common.c
+++ b/examples/common.c
@@ -146,6 +146,25 @@
 	return 1;
 }
 
+int match_uint32_arg(
+	uint32_t *out, struct args_info *args, const char *opt)
+{
+	const char *found = match_numeric_arg(args, opt);
+	uint16_t val;
+	char *endptr = NULL;
+
+	if (!found)
+		return 0;
+
+	val = (uint32_t)strtoul(found, &endptr, 0);
+	if (!endptr || *endptr != '\0')
+		fatal("expected number after argument", opt);
+
+	if (out)
+		*out = val;
+	return 1;
+}
+
 static int match_int_internal(
 	int *out, const char *str, int allow_negative, const char *opt)
 {
diff --git a/examples/common.h b/examples/common.h
index b9fa37c..adea0d3 100644
--- a/examples/common.h
+++ b/examples/common.h
@@ -73,6 +73,15 @@
 	uint16_t *out, struct args_info *args, const char *opt);
 
 /**
+ * Check current `args` entry against `opt` string parsing as uint32.  If
+ * `opt` matches exactly, take the next arg as a uint16_t value; if `opt`
+ * is a prefix (equal sign optional), take the remainder of the arg as a
+ * uint32_t value; otherwise return 0.
+ */
+extern int match_uint32_arg(
+	uint32_t *out, struct args_info *args, const char *opt);
+
+/**
  * Check current `args` entry against `opt` string parsing as int.  If
  * `opt` matches exactly, take the next arg as an int value; if it matches
  * as a prefix (equal sign optional), take the remainder of the arg as a
diff --git a/examples/diff.c b/examples/diff.c
index b69cb22..9a4f7a5 100644
--- a/examples/diff.c
+++ b/examples/diff.c
@@ -293,11 +293,11 @@
 		else if (is_prefixed(a, "-B") || is_prefixed(a, "--break-rewrites"))
 			/* TODO: parse thresholds */
 			o->findopts.flags |= GIT_DIFF_FIND_REWRITES;
-		else if (!match_uint16_arg(
+		else if (!match_uint32_arg(
 				&o->diffopts.context_lines, &args, "-U") &&
-			!match_uint16_arg(
+			!match_uint32_arg(
 				&o->diffopts.context_lines, &args, "--unified") &&
-			!match_uint16_arg(
+			!match_uint32_arg(
 				&o->diffopts.interhunk_lines, &args, "--inter-hunk-context") &&
 			!match_uint16_arg(
 				&o->diffopts.id_abbrev, &args, "--abbrev") &&
diff --git a/examples/general.c b/examples/general.c
index 32fdaf4..ff984a3 100644
--- a/examples/general.c
+++ b/examples/general.c
@@ -12,39 +12,58 @@
  * <http://creativecommons.org/publicdomain/zero/1.0/>.
  */
 
-// [**libgit2**][lg] is a portable, pure C implementation of the Git core
-// methods provided as a re-entrant linkable library with a solid API,
-// allowing you to write native speed custom Git applications in any
-// language which supports C bindings.
-//
-// This file is an example of using that API in a real, compilable C file.
-// As the API is updated, this file will be updated to demonstrate the new
-// functionality.
-//
-// If you're trying to write something in C using [libgit2][lg], you should
-// also check out the generated [API documentation][ap]. We try to link to
-// the relevant sections of the API docs in each section in this file.
-//
-// **libgit2** (for the most part) only implements the core plumbing
-// functions, not really the higher level porcelain stuff. For a primer on
-// Git Internals that you will need to know to work with Git at this level,
-// check out [Chapter 10][pg] of the Pro Git book.
-//
-// [lg]: http://libgit2.github.com
-// [ap]: http://libgit2.github.com/libgit2
-// [pg]: https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain
+/**
+ * [**libgit2**][lg] is a portable, pure C implementation of the Git core
+ * methods provided as a re-entrant linkable library with a solid API,
+ * allowing you to write native speed custom Git applications in any
+ * language which supports C bindings.
+ *
+ * This file is an example of using that API in a real, compilable C file.
+ * As the API is updated, this file will be updated to demonstrate the new
+ * functionality.
+ *
+ * If you're trying to write something in C using [libgit2][lg], you should
+ * also check out the generated [API documentation][ap]. We try to link to
+ * the relevant sections of the API docs in each section in this file.
+ *
+ * **libgit2** (for the most part) only implements the core plumbing
+ * functions, not really the higher level porcelain stuff. For a primer on
+ * Git Internals that you will need to know to work with Git at this level,
+ * check out [Chapter 10][pg] of the Pro Git book.
+ *
+ * [lg]: http://libgit2.github.com
+ * [ap]: http://libgit2.github.com/libgit2
+ * [pg]: https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain
+ */
 
-// ### Includes
-
-// Including the `git2.h` header will include all the other libgit2 headers
-// that you need.  It should be the only thing you need to include in order
-// to compile properly and get all the libgit2 API.
+/**
+ * ### Includes
+ *
+ * Including the `git2.h` header will include all the other libgit2 headers
+ * that you need.  It should be the only thing you need to include in order
+ * to compile properly and get all the libgit2 API.
+ */
 #include <git2.h>
 #include <stdio.h>
+#include <string.h>
 
-// Almost all libgit2 functions return 0 on success or negative on error.
-// This is not production quality error checking, but should be sufficient
-// as an example.
+static void oid_parsing(git_oid *out);
+static void object_database(git_repository *repo, git_oid *oid);
+static void commit_writing(git_repository *repo);
+static void commit_parsing(git_repository *repo);
+static void tag_parsing(git_repository *repo);
+static void tree_parsing(git_repository *repo);
+static void blob_parsing(git_repository *repo);
+static void revwalking(git_repository *repo);
+static void index_walking(git_repository *repo);
+static void reference_listing(git_repository *repo);
+static void config_files(const char *repo_path, git_repository *repo);
+
+/**
+ * Almost all libgit2 functions return 0 on success or negative on error.
+ * This is not production quality error checking, but should be sufficient
+ * as an example.
+ */
 static void check_error(int error_code, const char *action)
 {
 	const git_error *error = giterr_last();
@@ -52,479 +71,678 @@
 		return;
 
 	printf("Error %d %s - %s\n", error_code, action,
-		   (error && error->message) ? error->message : "???");
+			(error && error->message) ? error->message : "???");
 
 	exit(1);
 }
 
 int main (int argc, char** argv)
 {
-  // Initialize the library, this will set up any global state which libgit2 needs
-  // including threading and crypto
-  git_libgit2_init();
+	int error;
+	git_oid oid;
+	char *repo_path;
+	git_repository *repo;
 
-  // ### Opening the Repository
+	/**
+	 * Initialize the library, this will set up any global state which libgit2 needs
+	 * including threading and crypto
+	 */
+	git_libgit2_init();
 
-  // There are a couple of methods for opening a repository, this being the
-  // simplest.  There are also [methods][me] for specifying the index file
-  // and work tree locations, here we assume they are in the normal places.
-	//
-	// (Try running this program against tests/resources/testrepo.git.)
-  //
-  // [me]: http://libgit2.github.com/libgit2/#HEAD/group/repository
-  int error;
-  const char *repo_path = (argc > 1) ? argv[1] : "/opt/libgit2-test/.git";
-  git_repository *repo;
+	/**
+	 * ### Opening the Repository
+	 *
+	 * There are a couple of methods for opening a repository, this being the
+	 * simplest.  There are also [methods][me] for specifying the index file
+	 * and work tree locations, here we assume they are in the normal places.
+	 *
+	 * (Try running this program against tests/resources/testrepo.git.)
+	 *
+	 * [me]: http://libgit2.github.com/libgit2/#HEAD/group/repository
+	 */
+	repo_path = (argc > 1) ? argv[1] : "/opt/libgit2-test/.git";
 
-  error = git_repository_open(&repo, repo_path);
-  check_error(error, "opening repository");
+	error = git_repository_open(&repo, repo_path);
+	check_error(error, "opening repository");
 
-  // ### SHA-1 Value Conversions
+	oid_parsing(&oid);
+	object_database(repo, &oid);
+	commit_writing(repo);
+	commit_parsing(repo);
+	tag_parsing(repo);
+	tree_parsing(repo);
+	blob_parsing(repo);
+	revwalking(repo);
+	index_walking(repo);
+	reference_listing(repo);
+	config_files(repo_path, repo);
 
-  // For our first example, we will convert a 40 character hex value to the
-  // 20 byte raw SHA1 value.
-  printf("*Hex to Raw*\n");
-  char hex[] = "4a202b346bb0fb0db7eff3cffeb3c70babbd2045";
+	/**
+	 * Finally, when you're done with the repository, you can free it as well.
+	 */
+	git_repository_free(repo);
 
-  // The `git_oid` is the structure that keeps the SHA value. We will use
-  // this throughout the example for storing the value of the current SHA
-  // key we're working with.
-  git_oid oid;
-  git_oid_fromstr(&oid, hex);
+	return 0;
+}
 
-  // Once we've converted the string into the oid value, we can get the raw
-  // value of the SHA by accessing `oid.id`
+/**
+ * ### SHA-1 Value Conversions
+ */
+static void oid_parsing(git_oid *oid)
+{
+	char out[GIT_OID_HEXSZ+1];
+	char hex[] = "4a202b346bb0fb0db7eff3cffeb3c70babbd2045";
 
-  // Next we will convert the 20 byte raw SHA1 value to a human readable 40
-  // char hex value.
-  printf("\n*Raw to Hex*\n");
-  char out[GIT_OID_HEXSZ+1];
-  out[GIT_OID_HEXSZ] = '\0';
+	printf("*Hex to Raw*\n");
 
-  // If you have a oid, you can easily get the hex value of the SHA as well.
-  git_oid_fmt(out, &oid);
-  printf("SHA hex string: %s\n", out);
+	/**
+	 * For our first example, we will convert a 40 character hex value to the
+	 * 20 byte raw SHA1 value.
+	 *
+	 * The `git_oid` is the structure that keeps the SHA value. We will use
+	 * this throughout the example for storing the value of the current SHA
+	 * key we're working with.
+	 */
+	git_oid_fromstr(oid, hex);
 
-  // ### Working with the Object Database
+	// Once we've converted the string into the oid value, we can get the raw
+	// value of the SHA by accessing `oid.id`
 
-  // **libgit2** provides [direct access][odb] to the object database.  The
-  // object database is where the actual objects are stored in Git. For
-  // working with raw objects, we'll need to get this structure from the
-  // repository.
-  //
-  // [odb]: http://libgit2.github.com/libgit2/#HEAD/group/odb
-  git_odb *odb;
-  git_repository_odb(&odb, repo);
+	// Next we will convert the 20 byte raw SHA1 value to a human readable 40
+	// char hex value.
+	printf("\n*Raw to Hex*\n");
+	out[GIT_OID_HEXSZ] = '\0';
 
-  // #### Raw Object Reading
+	/**
+	 * If you have a oid, you can easily get the hex value of the SHA as well.
+	 */
+	git_oid_fmt(out, oid);
 
-  printf("\n*Raw Object Read*\n");
-  git_odb_object *obj;
-  git_otype otype;
-  const unsigned char *data;
-  const char *str_type;
+	/**
+	 * If you have a oid, you can easily get the hex value of the SHA as well.
+	 */
+	git_oid_fmt(out, oid);
+	printf("SHA hex string: %s\n", out);
+}
 
-  // We can read raw objects directly from the object database if we have
-  // the oid (SHA) of the object.  This allows us to access objects without
-  // knowing their type and inspect the raw bytes unparsed.
-  error = git_odb_read(&obj, odb, &oid);
-  check_error(error, "finding object in repository");
+/**
+ * ### Working with the Object Database
+ *
+ * **libgit2** provides [direct access][odb] to the object database.  The
+ * object database is where the actual objects are stored in Git. For
+ * working with raw objects, we'll need to get this structure from the
+ * repository.
+ *
+ * [odb]: http://libgit2.github.com/libgit2/#HEAD/group/odb
+ */
+static void object_database(git_repository *repo, git_oid *oid)
+{
+	char oid_hex[GIT_OID_HEXSZ+1] = { 0 };
+	const unsigned char *data;
+	const char *str_type;
+	int error;
+	git_odb_object *obj;
+	git_odb *odb;
+	git_otype otype;
 
-  // A raw object only has three properties - the type (commit, blob, tree
-  // or tag), the size of the raw data and the raw, unparsed data itself.
-  // For a commit or tag, that raw data is human readable plain ASCII
-  // text. For a blob it is just file contents, so it could be text or
-  // binary data. For a tree it is a special binary format, so it's unlikely
-  // to be hugely helpful as a raw object.
-  data = (const unsigned char *)git_odb_object_data(obj);
-  otype = git_odb_object_type(obj);
+	git_repository_odb(&odb, repo);
 
-  // We provide methods to convert from the object type which is an enum, to
-  // a string representation of that value (and vice-versa).
-  str_type = git_object_type2string(otype);
-  printf("object length and type: %d, %s\n",
-      (int)git_odb_object_size(obj),
-      str_type);
+	/**
+	 * #### Raw Object Reading
+	 */
 
-  // For proper memory management, close the object when you are done with
-  // it or it will leak memory.
-  git_odb_object_free(obj);
+	printf("\n*Raw Object Read*\n");
 
-  // #### Raw Object Writing
+	/**
+	 * We can read raw objects directly from the object database if we have
+	 * the oid (SHA) of the object.  This allows us to access objects without
+	 * knowing their type and inspect the raw bytes unparsed.
+	 */
+	error = git_odb_read(&obj, odb, oid);
+	check_error(error, "finding object in repository");
 
-  printf("\n*Raw Object Write*\n");
+	/**
+	 * A raw object only has three properties - the type (commit, blob, tree
+	 * or tag), the size of the raw data and the raw, unparsed data itself.
+	 * For a commit or tag, that raw data is human readable plain ASCII
+	 * text. For a blob it is just file contents, so it could be text or
+	 * binary data. For a tree it is a special binary format, so it's unlikely
+	 * to be hugely helpful as a raw object.
+	 */
+	data = (const unsigned char *)git_odb_object_data(obj);
+	otype = git_odb_object_type(obj);
 
-  // You can also write raw object data to Git. This is pretty cool because
-  // it gives you direct access to the key/value properties of Git.  Here
-  // we'll write a new blob object that just contains a simple string.
-  // Notice that we have to specify the object type as the `git_otype` enum.
-  git_odb_write(&oid, odb, "test data", sizeof("test data") - 1, GIT_OBJ_BLOB);
+	/**
+	 * We provide methods to convert from the object type which is an enum, to
+	 * a string representation of that value (and vice-versa).
+	 */
+	str_type = git_object_type2string(otype);
+	printf("object length and type: %d, %s\nobject data: %s\n",
+			(int)git_odb_object_size(obj),
+			str_type, data);
 
-  // Now that we've written the object, we can check out what SHA1 was
-  // generated when the object was written to our database.
-  git_oid_fmt(out, &oid);
-  printf("Written Object: %s\n", out);
+	/**
+	 * For proper memory management, close the object when you are done with
+	 * it or it will leak memory.
+	 */
+	git_odb_object_free(obj);
 
-  // ### Object Parsing
+	/**
+	 * #### Raw Object Writing
+	 */
 
-  // libgit2 has methods to parse every object type in Git so you don't have
-  // to work directly with the raw data. This is much faster and simpler
-  // than trying to deal with the raw data yourself.
+	printf("\n*Raw Object Write*\n");
 
-  // #### Commit Parsing
+	/**
+	 * You can also write raw object data to Git. This is pretty cool because
+	 * it gives you direct access to the key/value properties of Git.  Here
+	 * we'll write a new blob object that just contains a simple string.
+	 * Notice that we have to specify the object type as the `git_otype` enum.
+	 */
+	git_odb_write(oid, odb, "test data", sizeof("test data") - 1, GIT_OBJ_BLOB);
 
-  // [Parsing commit objects][pco] is simple and gives you access to all the
-  // data in the commit - the author (name, email, datetime), committer
-  // (same), tree, message, encoding and parent(s).
-  //
-  // [pco]: http://libgit2.github.com/libgit2/#HEAD/group/commit
+	/**
+	 * Now that we've written the object, we can check out what SHA1 was
+	 * generated when the object was written to our database.
+	 */
+	git_oid_fmt(oid_hex, oid);
+	printf("Written Object: %s\n", oid_hex);
 
-  printf("\n*Commit Parsing*\n");
+	/**
+	 * Free the object database after usage.
+	 */
+	git_odb_free(odb);
+}
 
-  git_commit *commit;
-  git_oid_fromstr(&oid, "8496071c1b46c854b31185ea97743be6a8774479");
+/**
+ * #### Writing Commits
+ *
+ * libgit2 provides a couple of methods to create commit objects easily as
+ * well. There are four different create signatures, we'll just show one
+ * of them here.  You can read about the other ones in the [commit API
+ * docs][cd].
+ *
+ * [cd]: http://libgit2.github.com/libgit2/#HEAD/group/commit
+ */
+static void commit_writing(git_repository *repo)
+{
+	git_oid tree_id, parent_id, commit_id;
+	git_tree *tree;
+	git_commit *parent;
+	git_signature *author, *committer;
+	char oid_hex[GIT_OID_HEXSZ+1] = { 0 };
 
-  error = git_commit_lookup(&commit, repo, &oid);
-  check_error(error, "looking up commit");
+	printf("\n*Commit Writing*\n");
 
-  const git_signature *author, *cmtter;
-  const char *message;
-  time_t ctime;
-  unsigned int parents, p;
+	/**
+	 * Creating signatures for an authoring identity and time is simple.  You
+	 * will need to do this to specify who created a commit and when.  Default
+	 * values for the name and email should be found in the `user.name` and
+	 * `user.email` configuration options.  See the `config` section of this
+	 * example file to see how to access config values.
+	 */
+	git_signature_new(&author,
+			"Scott Chacon", "schacon@gmail.com", 123456789, 60);
+	git_signature_new(&committer,
+			"Scott A Chacon", "scott@github.com", 987654321, 90);
 
-  // Each of the properties of the commit object are accessible via methods,
-  // including commonly needed variations, such as `git_commit_time` which
-  // returns the author time and `git_commit_message` which gives you the
-  // commit message (as a NUL-terminated string).
-  message  = git_commit_message(commit);
-  author   = git_commit_author(commit);
-  cmtter   = git_commit_committer(commit);
-  ctime    = git_commit_time(commit);
+	/**
+	 * Commit objects need a tree to point to and optionally one or more
+	 * parents.  Here we're creating oid objects to create the commit with,
+	 * but you can also use
+	 */
+	git_oid_fromstr(&tree_id, "f60079018b664e4e79329a7ef9559c8d9e0378d1");
+	git_tree_lookup(&tree, repo, &tree_id);
+	git_oid_fromstr(&parent_id, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+	git_commit_lookup(&parent, repo, &parent_id);
 
-  // The author and committer methods return [git_signature] structures,
-  // which give you name, email and `when`, which is a `git_time` structure,
-  // giving you a timestamp and timezone offset.
-  printf("Author: %s (%s)\n", author->name, author->email);
+	/**
+	 * Here we actually create the commit object with a single call with all
+	 * the values we need to create the commit.  The SHA key is written to the
+	 * `commit_id` variable here.
+	 */
+	git_commit_create_v(
+			&commit_id, /* out id */
+			repo,
+			NULL, /* do not update the HEAD */
+			author,
+			committer,
+			NULL, /* use default message encoding */
+			"example commit",
+			tree,
+			1, parent);
 
-  // Commits can have zero or more parents. The first (root) commit will
-  // have no parents, most commits will have one (i.e. the commit it was
-  // based on) and merge commits will have two or more.  Commits can
-  // technically have any number, though it's rare to have more than two.
-  parents  = git_commit_parentcount(commit);
-  for (p = 0;p < parents;p++) {
-    git_commit *parent;
-    git_commit_parent(&parent, commit, p);
-    git_oid_fmt(out, git_commit_id(parent));
-    printf("Parent: %s\n", out);
-    git_commit_free(parent);
-  }
+	/**
+	 * Now we can take a look at the commit SHA we've generated.
+	 */
+	git_oid_fmt(oid_hex, &commit_id);
+	printf("New Commit: %s\n", oid_hex);
 
-  // Don't forget to close the object to prevent memory leaks. You will have
-  // to do this for all the objects you open and parse.
-  git_commit_free(commit);
+	/**
+	 * Free all objects used in the meanwhile.
+	 */
+	git_tree_free(tree);
+	git_commit_free(parent);
+	git_signature_free(author);
+	git_signature_free(committer);
+}
 
-  // #### Writing Commits
+/**
+ * ### Object Parsing
+ *
+ * libgit2 has methods to parse every object type in Git so you don't have
+ * to work directly with the raw data. This is much faster and simpler
+ * than trying to deal with the raw data yourself.
+ */
 
-  // libgit2 provides a couple of methods to create commit objects easily as
-  // well. There are four different create signatures, we'll just show one
-  // of them here.  You can read about the other ones in the [commit API
-  // docs][cd].
-  //
-  // [cd]: http://libgit2.github.com/libgit2/#HEAD/group/commit
+/**
+ * #### Commit Parsing
+ *
+ * [Parsing commit objects][pco] is simple and gives you access to all the
+ * data in the commit - the author (name, email, datetime), committer
+ * (same), tree, message, encoding and parent(s).
+ *
+ * [pco]: http://libgit2.github.com/libgit2/#HEAD/group/commit
+ */
+static void commit_parsing(git_repository *repo)
+{
+	const git_signature *author, *cmtter;
+	git_commit *commit, *parent;
+	git_oid oid;
+	char oid_hex[GIT_OID_HEXSZ+1];
+	const char *message;
+	unsigned int parents, p;
+	int error;
+	time_t time;
 
-  printf("\n*Commit Writing*\n");
-  git_oid tree_id, parent_id, commit_id;
-  git_tree *tree;
-  git_commit *parent;
+	printf("\n*Commit Parsing*\n");
 
-  // Creating signatures for an authoring identity and time is simple.  You
-  // will need to do this to specify who created a commit and when.  Default
-  // values for the name and email should be found in the `user.name` and
-  // `user.email` configuration options.  See the `config` section of this
-  // example file to see how to access config values.
-  git_signature_new((git_signature **)&author,
-      "Scott Chacon", "schacon@gmail.com", 123456789, 60);
-  git_signature_new((git_signature **)&cmtter,
-      "Scott A Chacon", "scott@github.com", 987654321, 90);
+	git_oid_fromstr(&oid, "8496071c1b46c854b31185ea97743be6a8774479");
 
-  // Commit objects need a tree to point to and optionally one or more
-  // parents.  Here we're creating oid objects to create the commit with,
-  // but you can also use
-  git_oid_fromstr(&tree_id, "f60079018b664e4e79329a7ef9559c8d9e0378d1");
-  git_tree_lookup(&tree, repo, &tree_id);
-  git_oid_fromstr(&parent_id, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
-  git_commit_lookup(&parent, repo, &parent_id);
+	error = git_commit_lookup(&commit, repo, &oid);
+	check_error(error, "looking up commit");
 
-  // Here we actually create the commit object with a single call with all
-  // the values we need to create the commit.  The SHA key is written to the
-  // `commit_id` variable here.
-  git_commit_create_v(
-    &commit_id, /* out id */
-    repo,
-    NULL, /* do not update the HEAD */
-    author,
-    cmtter,
-    NULL, /* use default message encoding */
-    "example commit",
-    tree,
-    1, parent);
+	/**
+	 * Each of the properties of the commit object are accessible via methods,
+	 * including commonly needed variations, such as `git_commit_time` which
+	 * returns the author time and `git_commit_message` which gives you the
+	 * commit message (as a NUL-terminated string).
+	 */
+	message  = git_commit_message(commit);
+	author   = git_commit_author(commit);
+	cmtter   = git_commit_committer(commit);
+	time    = git_commit_time(commit);
 
-  // Now we can take a look at the commit SHA we've generated.
-  git_oid_fmt(out, &commit_id);
-  printf("New Commit: %s\n", out);
+	/**
+	 * The author and committer methods return [git_signature] structures,
+	 * which give you name, email and `when`, which is a `git_time` structure,
+	 * giving you a timestamp and timezone offset.
+	 */
+	printf("Author: %s (%s)\nCommitter: %s (%s)\nDate: %s\nMessage: %s\n",
+		author->name, author->email,
+		cmtter->name, cmtter->email,
+		ctime(&time), message);
 
-  // #### Tag Parsing
+	/**
+	 * Commits can have zero or more parents. The first (root) commit will
+	 * have no parents, most commits will have one (i.e. the commit it was
+	 * based on) and merge commits will have two or more.  Commits can
+	 * technically have any number, though it's rare to have more than two.
+	 */
+	parents  = git_commit_parentcount(commit);
+	for (p = 0;p < parents;p++) {
+		memset(oid_hex, 0, sizeof(oid_hex));
 
-  // You can parse and create tags with the [tag management API][tm], which
-  // functions very similarly to the commit lookup, parsing and creation
-  // methods, since the objects themselves are very similar.
-  //
-  // [tm]: http://libgit2.github.com/libgit2/#HEAD/group/tag
-  printf("\n*Tag Parsing*\n");
-  git_tag *tag;
-  const char *tmessage, *tname;
-  git_otype ttype;
+		git_commit_parent(&parent, commit, p);
+		git_oid_fmt(oid_hex, git_commit_id(parent));
+		printf("Parent: %s\n", oid_hex);
+		git_commit_free(parent);
+	}
 
-  // We create an oid for the tag object if we know the SHA and look it up
-  // the same way that we would a commit (or any other object).
-  git_oid_fromstr(&oid, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
+	git_commit_free(commit);
+}
 
-  error = git_tag_lookup(&tag, repo, &oid);
-  check_error(error, "looking up tag");
-
-  // Now that we have the tag object, we can extract the information it
-  // generally contains: the target (usually a commit object), the type of
-  // the target object (usually 'commit'), the name ('v1.0'), the tagger (a
-  // git_signature - name, email, timestamp), and the tag message.
-  git_tag_target((git_object **)&commit, tag);
-  tname = git_tag_name(tag);		// "test"
-  ttype = git_tag_target_type(tag);	// GIT_OBJ_COMMIT (otype enum)
-  tmessage = git_tag_message(tag);	// "tag message\n"
-  printf("Tag Message: %s\n", tmessage);
-
-  git_commit_free(commit);
-
-  // #### Tree Parsing
-
-  // [Tree parsing][tp] is a bit different than the other objects, in that
-  // we have a subtype which is the tree entry.  This is not an actual
-  // object type in Git, but a useful structure for parsing and traversing
-  // tree entries.
-  //
-  // [tp]: http://libgit2.github.com/libgit2/#HEAD/group/tree
-  printf("\n*Tree Parsing*\n");
-
-  const git_tree_entry *entry;
-  git_object *objt;
+/**
+ * #### Tag Parsing
+ *
+ * You can parse and create tags with the [tag management API][tm], which
+ * functions very similarly to the commit lookup, parsing and creation
+ * methods, since the objects themselves are very similar.
+ *
+ * [tm]: http://libgit2.github.com/libgit2/#HEAD/group/tag
+ */
+static void tag_parsing(git_repository *repo)
+{
+	git_commit *commit;
+	git_otype type;
+	git_tag *tag;
+	git_oid oid;
+	const char *name, *message;
+	int error;
 
-  // Create the oid and lookup the tree object just like the other objects.
-  git_oid_fromstr(&oid, "2a741c18ac5ff082a7caaec6e74db3075a1906b5");
-  git_tree_lookup(&tree, repo, &oid);
+	printf("\n*Tag Parsing*\n");
 
-  // Getting the count of entries in the tree so you can iterate over them
-  // if you want to.
-  size_t cnt = git_tree_entrycount(tree); // 3
-  printf("tree entries: %d\n", (int)cnt);
+	/**
+	 * We create an oid for the tag object if we know the SHA and look it up
+	 * the same way that we would a commit (or any other object).
+	 */
+	git_oid_fromstr(&oid, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
 
-  entry = git_tree_entry_byindex(tree, 0);
-  printf("Entry name: %s\n", git_tree_entry_name(entry)); // "hello.c"
+	error = git_tag_lookup(&tag, repo, &oid);
+	check_error(error, "looking up tag");
 
-  // You can also access tree entries by name if you know the name of the
-  // entry you're looking for.
-  entry = git_tree_entry_byname(tree, "README");
-  git_tree_entry_name(entry); // "hello.c"
+	/**
+	 * Now that we have the tag object, we can extract the information it
+	 * generally contains: the target (usually a commit object), the type of
+	 * the target object (usually 'commit'), the name ('v1.0'), the tagger (a
+	 * git_signature - name, email, timestamp), and the tag message.
+	 */
+	git_tag_target((git_object **)&commit, tag);
+	name = git_tag_name(tag);		/* "test" */
+	type = git_tag_target_type(tag);	/* GIT_OBJ_COMMIT (otype enum) */
+	message = git_tag_message(tag);		/* "tag message\n" */
+	printf("Tag Name: %s\nTag Type: %s\nTag Message: %s\n",
+		name, git_object_type2string(type), message);
 
-  // Once you have the entry object, you can access the content or subtree
-  // (or commit, in the case of submodules) that it points to.  You can also
-  // get the mode if you want.
-  git_tree_entry_to_object(&objt, repo, entry); // blob
+	/**
+	 * Free both the commit and tag after usage.
+	 */
+	git_commit_free(commit);
+	git_tag_free(tag);
+}
 
-  // Remember to close the looked-up object once you are done using it
-  git_object_free(objt);
+/**
+ * #### Tree Parsing
+ *
+ * [Tree parsing][tp] is a bit different than the other objects, in that
+ * we have a subtype which is the tree entry.  This is not an actual
+ * object type in Git, but a useful structure for parsing and traversing
+ * tree entries.
+ *
+ * [tp]: http://libgit2.github.com/libgit2/#HEAD/group/tree
+ */
+static void tree_parsing(git_repository *repo)
+{
+	const git_tree_entry *entry;
+	size_t cnt;
+	git_object *obj;
+	git_tree *tree;
+	git_oid oid;
 
-  // #### Blob Parsing
+	printf("\n*Tree Parsing*\n");
 
-  // The last object type is the simplest and requires the least parsing
-  // help. Blobs are just file contents and can contain anything, there is
-  // no structure to it. The main advantage to using the [simple blob
-  // api][ba] is that when you're creating blobs you don't have to calculate
-  // the size of the content.  There is also a helper for reading a file
-  // from disk and writing it to the db and getting the oid back so you
-  // don't have to do all those steps yourself.
-  //
-  // [ba]: http://libgit2.github.com/libgit2/#HEAD/group/blob
+	/**
+	 * Create the oid and lookup the tree object just like the other objects.
+	 */
+	git_oid_fromstr(&oid, "f60079018b664e4e79329a7ef9559c8d9e0378d1");
+	git_tree_lookup(&tree, repo, &oid);
 
-  printf("\n*Blob Parsing*\n");
-  git_blob *blob;
+	/**
+	 * Getting the count of entries in the tree so you can iterate over them
+	 * if you want to.
+	 */
+	cnt = git_tree_entrycount(tree); /* 2 */
+	printf("tree entries: %d\n", (int) cnt);
 
-  git_oid_fromstr(&oid, "1385f264afb75a56a5bec74243be9b367ba4ca08");
-  git_blob_lookup(&blob, repo, &oid);
+	entry = git_tree_entry_byindex(tree, 0);
+	printf("Entry name: %s\n", git_tree_entry_name(entry)); /* "README" */
 
-  // You can access a buffer with the raw contents of the blob directly.
-  // Note that this buffer may not be contain ASCII data for certain blobs
-  // (e.g. binary files): do not consider the buffer a NULL-terminated
-  // string, and use the `git_blob_rawsize` attribute to find out its exact
-  // size in bytes
-  printf("Blob Size: %ld\n", (long)git_blob_rawsize(blob)); // 8
-  git_blob_rawcontent(blob); // "content"
+	/**
+	 * You can also access tree entries by name if you know the name of the
+	 * entry you're looking for.
+	 */
+	entry = git_tree_entry_byname(tree, "README");
+	git_tree_entry_name(entry); /* "README" */
 
-  // ### Revwalking
+	/**
+	 * Once you have the entry object, you can access the content or subtree
+	 * (or commit, in the case of submodules) that it points to.  You can also
+	 * get the mode if you want.
+	 */
+	git_tree_entry_to_object(&obj, repo, entry); /* blob */
 
-  // The libgit2 [revision walking api][rw] provides methods to traverse the
-  // directed graph created by the parent pointers of the commit objects.
-  // Since all commits point back to the commit that came directly before
-  // them, you can walk this parentage as a graph and find all the commits
-  // that were ancestors of (reachable from) a given starting point.  This
-  // can allow you to create `git log` type functionality.
-  //
-  // [rw]: http://libgit2.github.com/libgit2/#HEAD/group/revwalk
+	/**
+	 * Remember to close the looked-up object and tree once you are done using it
+	 */
+	git_object_free(obj);
+	git_tree_free(tree);
+}
 
-  printf("\n*Revwalking*\n");
-  git_revwalk *walk;
-  git_commit *wcommit;
+/**
+ * #### Blob Parsing
+ *
+ * The last object type is the simplest and requires the least parsing
+ * help. Blobs are just file contents and can contain anything, there is
+ * no structure to it. The main advantage to using the [simple blob
+ * api][ba] is that when you're creating blobs you don't have to calculate
+ * the size of the content.  There is also a helper for reading a file
+ * from disk and writing it to the db and getting the oid back so you
+ * don't have to do all those steps yourself.
+ *
+ * [ba]: http://libgit2.github.com/libgit2/#HEAD/group/blob
+ */
+static void blob_parsing(git_repository *repo)
+{
+	git_blob *blob;
+	git_oid oid;
 
-  git_oid_fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+	printf("\n*Blob Parsing*\n");
 
-  // To use the revwalker, create a new walker, tell it how you want to sort
-  // the output and then push one or more starting points onto the walker.
-  // If you want to emulate the output of `git log` you would push the SHA
-  // of the commit that HEAD points to into the walker and then start
-  // traversing them.  You can also 'hide' commits that you want to stop at
-  // or not see any of their ancestors.  So if you want to emulate `git log
-  // branch1..branch2`, you would push the oid of `branch2` and hide the oid
-  // of `branch1`.
-  git_revwalk_new(&walk, repo);
-  git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE);
-  git_revwalk_push(walk, &oid);
+	git_oid_fromstr(&oid, "1385f264afb75a56a5bec74243be9b367ba4ca08");
+	git_blob_lookup(&blob, repo, &oid);
 
-  const git_signature *cauth;
-  const char *cmsg;
+	/**
+	 * You can access a buffer with the raw contents of the blob directly.
+	 * Note that this buffer may not be contain ASCII data for certain blobs
+	 * (e.g. binary files): do not consider the buffer a NULL-terminated
+	 * string, and use the `git_blob_rawsize` attribute to find out its exact
+	 * size in bytes
+	 * */
+	printf("Blob Size: %ld\n", (long)git_blob_rawsize(blob)); /* 8 */
+	git_blob_rawcontent(blob); /* "content" */
 
-  // Now that we have the starting point pushed onto the walker, we start
-  // asking for ancestors. It will return them in the sorting order we asked
-  // for as commit oids.  We can then lookup and parse the committed pointed
-  // at by the returned OID; note that this operation is specially fast
-  // since the raw contents of the commit object will be cached in memory
-  while ((git_revwalk_next(&oid, walk)) == 0) {
-    error = git_commit_lookup(&wcommit, repo, &oid);
-	check_error(error, "looking up commit during revwalk");
+	/**
+	 * Free the blob after usage.
+	 */
+	git_blob_free(blob);
+}
 
-    cmsg  = git_commit_message(wcommit);
-    cauth = git_commit_author(wcommit);
-    printf("%s (%s)\n", cmsg, cauth->email);
+/**
+ * ### Revwalking
+ *
+ * The libgit2 [revision walking api][rw] provides methods to traverse the
+ * directed graph created by the parent pointers of the commit objects.
+ * Since all commits point back to the commit that came directly before
+ * them, you can walk this parentage as a graph and find all the commits
+ * that were ancestors of (reachable from) a given starting point.  This
+ * can allow you to create `git log` type functionality.
+ *
+ * [rw]: http://libgit2.github.com/libgit2/#HEAD/group/revwalk
+ */
+static void revwalking(git_repository *repo)
+{
+	const git_signature *cauth;
+	const char *cmsg;
+	int error;
+	git_revwalk *walk;
+	git_commit *wcommit;
+	git_oid oid;
 
-    git_commit_free(wcommit);
-  }
+	printf("\n*Revwalking*\n");
 
-  // Like the other objects, be sure to free the revwalker when you're done
-  // to prevent memory leaks.  Also, make sure that the repository being
-  // walked it not deallocated while the walk is in progress, or it will
-  // result in undefined behavior
-  git_revwalk_free(walk);
+	git_oid_fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
 
-  // ### Index File Manipulation
+	/**
+	 * To use the revwalker, create a new walker, tell it how you want to sort
+	 * the output and then push one or more starting points onto the walker.
+	 * If you want to emulate the output of `git log` you would push the SHA
+	 * of the commit that HEAD points to into the walker and then start
+	 * traversing them.  You can also 'hide' commits that you want to stop at
+	 * or not see any of their ancestors.  So if you want to emulate `git log
+	 * branch1..branch2`, you would push the oid of `branch2` and hide the oid
+	 * of `branch1`.
+	 */
+	git_revwalk_new(&walk, repo);
+	git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE);
+	git_revwalk_push(walk, &oid);
 
-  // The [index file API][gi] allows you to read, traverse, update and write
-  // the Git index file (sometimes thought of as the staging area).
-  //
-  // [gi]: http://libgit2.github.com/libgit2/#HEAD/group/index
+	/**
+	 * Now that we have the starting point pushed onto the walker, we start
+	 * asking for ancestors. It will return them in the sorting order we asked
+	 * for as commit oids.  We can then lookup and parse the committed pointed
+	 * at by the returned OID; note that this operation is specially fast
+	 * since the raw contents of the commit object will be cached in memory
+	 */
+	while ((git_revwalk_next(&oid, walk)) == 0) {
+		error = git_commit_lookup(&wcommit, repo, &oid);
+		check_error(error, "looking up commit during revwalk");
 
-  printf("\n*Index Walking*\n");
+		cmsg  = git_commit_message(wcommit);
+		cauth = git_commit_author(wcommit);
+		printf("%s (%s)\n", cmsg, cauth->email);
 
-  git_index *index;
-  unsigned int i, ecount;
+		git_commit_free(wcommit);
+	}
 
-  // You can either open the index from the standard location in an open
-  // repository, as we're doing here, or you can open and manipulate any
-  // index file with `git_index_open_bare()`. The index for the repository
-  // will be located and loaded from disk.
-  git_repository_index(&index, repo);
+	/**
+	 * Like the other objects, be sure to free the revwalker when you're done
+	 * to prevent memory leaks.  Also, make sure that the repository being
+	 * walked it not deallocated while the walk is in progress, or it will
+	 * result in undefined behavior
+	 */
+	git_revwalk_free(walk);
+}
 
-  // For each entry in the index, you can get a bunch of information
-  // including the SHA (oid), path and mode which map to the tree objects
-  // that are written out.  It also has filesystem properties to help
-  // determine what to inspect for changes (ctime, mtime, dev, ino, uid,
-  // gid, file_size and flags) All these properties are exported publicly in
-  // the `git_index_entry` struct
-  ecount = git_index_entrycount(index);
-  for (i = 0; i < ecount; ++i) {
-    const git_index_entry *e = git_index_get_byindex(index, i);
+/**
+ * ### Index File Manipulation *
+ * The [index file API][gi] allows you to read, traverse, update and write
+ * the Git index file (sometimes thought of as the staging area).
+ *
+ * [gi]: http://libgit2.github.com/libgit2/#HEAD/group/index
+ */
+static void index_walking(git_repository *repo)
+{
+	git_index *index;
+	unsigned int i, ecount;
 
-    printf("path: %s\n", e->path);
-    printf("mtime: %d\n", (int)e->mtime.seconds);
-    printf("fs: %d\n", (int)e->file_size);
-  }
+	printf("\n*Index Walking*\n");
 
-  git_index_free(index);
+	/**
+	 * You can either open the index from the standard location in an open
+	 * repository, as we're doing here, or you can open and manipulate any
+	 * index file with `git_index_open_bare()`. The index for the repository
+	 * will be located and loaded from disk.
+	 */
+	git_repository_index(&index, repo);
 
-  // ### References
+	/**
+	 * For each entry in the index, you can get a bunch of information
+	 * including the SHA (oid), path and mode which map to the tree objects
+	 * that are written out.  It also has filesystem properties to help
+	 * determine what to inspect for changes (ctime, mtime, dev, ino, uid,
+	 * gid, file_size and flags) All these properties are exported publicly in
+	 * the `git_index_entry` struct
+	 */
+	ecount = git_index_entrycount(index);
+	for (i = 0; i < ecount; ++i) {
+		const git_index_entry *e = git_index_get_byindex(index, i);
 
-  // The [reference API][ref] allows you to list, resolve, create and update
-  // references such as branches, tags and remote references (everything in
-  // the .git/refs directory).
-  //
-  // [ref]: http://libgit2.github.com/libgit2/#HEAD/group/reference
+		printf("path: %s\n", e->path);
+		printf("mtime: %d\n", (int)e->mtime.seconds);
+		printf("fs: %d\n", (int)e->file_size);
+	}
 
-  printf("\n*Reference Listing*\n");
+	git_index_free(index);
+}
 
-  // Here we will implement something like `git for-each-ref` simply listing
-  // out all available references and the object SHA they resolve to.
-  git_strarray ref_list;
-  git_reference_list(&ref_list, repo);
+/**
+ * ### References
+ *
+ * The [reference API][ref] allows you to list, resolve, create and update
+ * references such as branches, tags and remote references (everything in
+ * the .git/refs directory).
+ *
+ * [ref]: http://libgit2.github.com/libgit2/#HEAD/group/reference
+ */
+static void reference_listing(git_repository *repo)
+{
+	git_strarray ref_list;
+	unsigned i;
 
-  const char *refname;
-  git_reference *ref;
+	printf("\n*Reference Listing*\n");
 
-  // Now that we have the list of reference names, we can lookup each ref
-  // one at a time and resolve them to the SHA, then print both values out.
-  for (i = 0; i < ref_list.count; ++i) {
-    refname = ref_list.strings[i];
-    git_reference_lookup(&ref, repo, refname);
+	/**
+	 * Here we will implement something like `git for-each-ref` simply listing
+	 * out all available references and the object SHA they resolve to.
+	 *
+	 * Now that we have the list of reference names, we can lookup each ref
+	 * one at a time and resolve them to the SHA, then print both values out.
+	 */
 
-    switch (git_reference_type(ref)) {
-    case GIT_REF_OID:
-      git_oid_fmt(out, git_reference_target(ref));
-      printf("%s [%s]\n", refname, out);
-      break;
+	git_reference_list(&ref_list, repo);
 
-    case GIT_REF_SYMBOLIC:
-      printf("%s => %s\n", refname, git_reference_symbolic_target(ref));
-      break;
-    default:
-      fprintf(stderr, "Unexpected reference type\n");
-      exit(1);
-    }
-  }
+	for (i = 0; i < ref_list.count; ++i) {
+		git_reference *ref;
+		char oid_hex[GIT_OID_HEXSZ+1] = GIT_OID_HEX_ZERO;
+		const char *refname;
 
-  git_strarray_free(&ref_list);
+		refname = ref_list.strings[i];
+		git_reference_lookup(&ref, repo, refname);
 
-  // ### Config Files
+		switch (git_reference_type(ref)) {
+			case GIT_REF_OID:
+				git_oid_fmt(oid_hex, git_reference_target(ref));
+				printf("%s [%s]\n", refname, oid_hex);
+				break;
 
-  // The [config API][config] allows you to list and updatee config values
-  // in any of the accessible config file locations (system, global, local).
-  //
-  // [config]: http://libgit2.github.com/libgit2/#HEAD/group/config
+			case GIT_REF_SYMBOLIC:
+				printf("%s => %s\n", refname, git_reference_symbolic_target(ref));
+				break;
+			default:
+				fprintf(stderr, "Unexpected reference type\n");
+				exit(1);
+		}
 
-  printf("\n*Config Listing*\n");
+		git_reference_free(ref);
+	}
 
-  const char *email;
-  int32_t j;
+	git_strarray_free(&ref_list);
+}
 
-  git_config *cfg;
+/**
+ * ### Config Files
+ *
+ * The [config API][config] allows you to list and updatee config values
+ * in any of the accessible config file locations (system, global, local).
+ *
+ * [config]: http://libgit2.github.com/libgit2/#HEAD/group/config
+ */
+static void config_files(const char *repo_path, git_repository* repo)
+{
+	const char *email;
+	char config_path[256];
+	int32_t autocorrect;
+	git_config *cfg;
+	git_config *snap_cfg;
 
-  // Open a config object so we can read global values from it.
-  char config_path[256];
-  sprintf(config_path, "%s/config", repo_path);
-  check_error(git_config_open_ondisk(&cfg, config_path), "opening config");
+	printf("\n*Config Listing*\n");
 
-  git_config_get_int32(&j, cfg, "help.autocorrect");
-  printf("Autocorrect: %d\n", j);
+	/**
+	 * Open a config object so we can read global values from it.
+	 */
+	sprintf(config_path, "%s/config", repo_path);
+	check_error(git_config_open_ondisk(&cfg, config_path), "opening config");
 
-  git_config_get_string(&email, cfg, "user.email");
-  printf("Email: %s\n", email);
+	if (git_config_get_int32(&autocorrect, cfg, "help.autocorrect") == 0)
+		printf("Autocorrect: %d\n", autocorrect);
 
-  // Finally, when you're done with the repository, you can free it as well.
-  git_repository_free(repo);
+	check_error(git_repository_config_snapshot(&snap_cfg, repo), "config snapshot");
+	git_config_get_string(&email, snap_cfg, "user.email");
+	printf("Email: %s\n", email);
 
-  return 0;
+	/**
+	 * Remember to free the configurations after usage.
+	 */
+	git_config_free(cfg);
+	git_config_free(snap_cfg);
 }
diff --git a/examples/network/clone.c b/examples/network/clone.c
index caf41cc..540000b 100644
--- a/examples/network/clone.c
+++ b/examples/network/clone.c
@@ -50,7 +50,7 @@
 {
 	(void)payload; // unused
 
-	printf("remote: %*s", len, str);
+	printf("remote: %.*s", len, str);
 	fflush(stdout);
 	return 0;
 }
diff --git a/examples/network/fetch.c b/examples/network/fetch.c
index 177359b..10974a9 100644
--- a/examples/network/fetch.c
+++ b/examples/network/fetch.c
@@ -55,6 +55,8 @@
  */
 static int transfer_progress_cb(const git_transfer_progress *stats, void *payload)
 {
+	(void)payload;
+
 	if (stats->received_objects == stats->total_objects) {
 		printf("Resolving deltas %d/%d\r",
 		       stats->indexed_deltas, stats->total_deltas);
@@ -71,7 +73,6 @@
 {
 	git_remote *remote = NULL;
 	const git_transfer_progress *stats;
-	struct dl_data data;
 	git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
 
 	if (argc < 2) {
@@ -79,14 +80,13 @@
 		return EXIT_FAILURE;
 	}
 
-	// Figure out whether it's a named remote or a URL
+	/* Figure out whether it's a named remote or a URL */
 	printf("Fetching %s for repo %p\n", argv[1], repo);
-	if (git_remote_lookup(&remote, repo, argv[1]) < 0) {
+	if (git_remote_lookup(&remote, repo, argv[1]) < 0)
 		if (git_remote_create_anonymous(&remote, repo, argv[1]) < 0)
-			return -1;
-	}
+			goto on_error;
 
-	// Set up the callbacks (only update_tips for now)
+	/* Set up the callbacks (only update_tips for now) */
 	fetch_opts.callbacks.update_tips = &update_cb;
 	fetch_opts.callbacks.sideband_progress = &progress_cb;
 	fetch_opts.callbacks.transfer_progress = transfer_progress_cb;
@@ -98,7 +98,7 @@
 	 * "fetch".
 	 */
 	if (git_remote_fetch(remote, NULL, &fetch_opts, "fetch") < 0)
-		return -1;
+		goto on_error;
 
 	/**
 	 * If there are local objects (we got a thin pack), then tell
diff --git a/include/git2/branch.h b/include/git2/branch.h
index 34354f4..88fe723 100644
--- a/include/git2/branch.h
+++ b/include/git2/branch.h
@@ -246,6 +246,18 @@
 	const git_reference *branch);
 
 /**
+ * Determine if the current branch is checked out in any linked
+ * repository.
+ *
+ * @param branch Reference to the branch.
+ *
+ * @return 1 if branch is checked out, 0 if it isn't,
+ * error code otherwise.
+ */
+GIT_EXTERN(int) git_branch_is_checked_out(
+	const git_reference *branch);
+
+/**
  * Return the name of remote that the remote tracking branch belongs to.
  *
  * @param out Pointer to the user-allocated git_buf which will be filled with the name of the remote.
diff --git a/include/git2/commit.h b/include/git2/commit.h
index 4cc6374..692b3bd 100644
--- a/include/git2/commit.h
+++ b/include/git2/commit.h
@@ -255,7 +255,8 @@
 /**
  * Get an arbitrary header field
  *
- * @param out the buffer to fill
+ * @param out the buffer to fill; existing content will be
+ * overwritten
  * @param commit the commit to look in
  * @param field the header field to return
  * @return 0 on succeess, GIT_ENOTFOUND if the field does not exist,
@@ -270,8 +271,10 @@
  * `GITERR_INVALID`. If the commit does not have a signature, the
  * error class will be `GITERR_OBJECT`.
  *
- * @param signature the signature block
- * @param signed_data signed data; this is the commit contents minus the signature block
+ * @param signature the signature block; existing content will be
+ * overwritten
+ * @param signed_data signed data; this is the commit contents minus the signature block;
+ * existing content will be overwritten
  * @param repo the repository in which the commit exists
  * @param commit_id the commit from which to extract the data
  * @param field the name of the header field containing the signature
diff --git a/include/git2/common.h b/include/git2/common.h
index 18abe46..c909f86 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;
 
@@ -157,8 +175,11 @@
 	GIT_OPT_SET_SSL_CERT_LOCATIONS,
 	GIT_OPT_SET_USER_AGENT,
 	GIT_OPT_ENABLE_STRICT_OBJECT_CREATION,
+	GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION,
 	GIT_OPT_SET_SSL_CIPHERS,
 	GIT_OPT_GET_USER_AGENT,
+	GIT_OPT_ENABLE_OFS_DELTA,
+	GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION,
 } git_libgit2_opt_t;
 
 /**
@@ -269,13 +290,40 @@
  *		> to ensure that all inputs to the new objects are valid.  For
  *		> example, when this is enabled, the parent(s) and tree inputs
  *		> will be validated when creating a new commit.  This defaults
- *		> to disabled.
+ *		> to enabled.
+ *
+ *	* opts(GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, int enabled)
+ *
+ *		> Validate the target of a symbolic ref when creating it.  For
+ *		> example, `foobar` is not a valid ref, therefore `foobar` is
+ *		> not a valid target for a symbolic ref by default, whereas
+ *		> `refs/heads/foobar` is.  Disabling this bypasses validation
+ *		> so that an arbitrary strings such as `foobar` can be used
+ *		> for a symbolic ref target.  This defaults to enabled.
+ *
  *	* opts(GIT_OPT_SET_SSL_CIPHERS, const char *ciphers)
  *
  *		> Set the SSL ciphers use for HTTPS connections.
  *		>
  *		> - `ciphers` is the list of ciphers that are eanbled.
  *
+ *	* opts(GIT_OPT_ENABLE_OFS_DELTA, int enabled)
+ *
+ *		> Enable or disable the use of "offset deltas" when creating packfiles,
+ *		> and the negotiation of them when talking to a remote server.
+ *		> Offset deltas store a delta base location as an offset into the
+ *		> packfile from the current location, which provides a shorter encoding
+ *		> and thus smaller resultant packfiles.
+ *		> Packfiles containing offset deltas can still be read.
+ *		> This defaults to enabled.
+ *
+ *	* opts(GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION, int enabled)
+ *
+ *		> Enable synchronized writes of new objects using `fsync`
+ *		> (or the platform equivalent) to ensure that new object data
+ *		> is written to permanent storage, not simply cached.  This
+ *		> defaults to disabled.
+ *
  * @param option Option key
  * @param ... value to set the option
  * @return 0 on success, <0 on failure
diff --git a/include/git2/describe.h b/include/git2/describe.h
index 3044d91..971eb35 100644
--- a/include/git2/describe.h
+++ b/include/git2/describe.h
@@ -44,8 +44,8 @@
 typedef struct git_describe_options {
 	unsigned int version;
 
-	unsigned int max_candidates_tags; /** default: 10 */
-	unsigned int describe_strategy; /** default: GIT_DESCRIBE_DEFAULT */
+	unsigned int max_candidates_tags; /**< default: 10 */
+	unsigned int describe_strategy; /**< default: GIT_DESCRIBE_DEFAULT */
 	const char *pattern;
 	/**
 	 * When calculating the distance from the matching tag or
@@ -105,6 +105,9 @@
 
 GIT_EXTERN(int) git_describe_init_format_options(git_describe_format_options *opts, unsigned int version);
 
+/**
+ * A struct that stores the result of a describe operation.
+ */
 typedef struct git_describe_result git_describe_result;
 
 /**
diff --git a/include/git2/errors.h b/include/git2/errors.h
index e959ffd..3b746b7 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -100,6 +100,8 @@
 	GITERR_REBASE,
 	GITERR_FILESYSTEM,
 	GITERR_PATCH,
+	GITERR_WORKTREE,
+	GITERR_SHA1
 } git_error_t;
 
 /**
diff --git a/include/git2/index.h b/include/git2/index.h
index e58b328..35af2e5 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -575,15 +575,16 @@
  * This method will fail in bare index instances.
  *
  * The `pathspec` is a list of file names or shell glob patterns that will
- * matched against files in the repository's working directory.  Each file
- * that matches will be added to the index (either updating an existing
- * entry or adding a new entry).  You can disable glob expansion and force
- * exact matching with the `GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH` flag.
+ * be matched against files in the repository's working directory.  Each
+ * file that matches will be added to the index (either updating an
+ * existing entry or adding a new entry).  You can disable glob expansion
+ * and force exact matching with the `GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH`
+ * flag.
  *
  * Files that are ignored will be skipped (unlike `git_index_add_bypath`).
  * If a file is already tracked in the index, then it *will* be updated
- * even if it is ignored.  Pass the `GIT_INDEX_ADD_FORCE` flag to
- * skip the checking of ignore rules.
+ * even if it is ignored.  Pass the `GIT_INDEX_ADD_FORCE` flag to skip
+ * the checking of ignore rules.
  *
  * To emulate `git add -A` and generate an error if the pathspec contains
  * the exact path of an ignored file (when not using FORCE), add the
diff --git a/include/git2/merge.h b/include/git2/merge.h
index c6f6cba..94ac8b5 100644
--- a/include/git2/merge.h
+++ b/include/git2/merge.h
@@ -290,7 +290,8 @@
 } git_merge_options;
 
 #define GIT_MERGE_OPTIONS_VERSION 1
-#define GIT_MERGE_OPTIONS_INIT {GIT_MERGE_OPTIONS_VERSION}
+#define GIT_MERGE_OPTIONS_INIT { \
+	GIT_MERGE_OPTIONS_VERSION, GIT_MERGE_FIND_RENAMES }
 
 /**
  * Initializes a `git_merge_options` with default values. Equivalent to
diff --git a/include/git2/odb_backend.h b/include/git2/odb_backend.h
index b17cfd8..9199538 100644
--- a/include/git2/odb_backend.h
+++ b/include/git2/odb_backend.h
@@ -39,7 +39,7 @@
  * @param out location to store the odb backend pointer
  * @param objects_dir the Git repository's objects directory
  * @param compression_level zlib compression level to use
- * @param do_fsync whether to do an fsync() after writing (currently ignored)
+ * @param do_fsync whether to do an fsync() after writing
  * @param dir_mode permissions to use creating a directory or 0 for defaults
  * @param file_mode permissions to use creating a file or 0 for defaults
  *
diff --git a/include/git2/oid.h b/include/git2/oid.h
index 8ad51c8..aaa678c 100644
--- a/include/git2/oid.h
+++ b/include/git2/oid.h
@@ -50,17 +50,16 @@
  * Parse a hex formatted null-terminated string into a git_oid.
  *
  * @param out oid structure the result is written into.
- * @param str input hex string; must be at least 4 characters
- *      long and null-terminated.
+ * @param str input hex string; must be null-terminated.
  * @return 0 or an error code
  */
 GIT_EXTERN(int) git_oid_fromstrp(git_oid *out, const char *str);
 
 /**
- * Parse N characters of a hex formatted object id into a git_oid
+ * Parse N characters of a hex formatted object id into a git_oid.
  *
- * If N is odd, N-1 characters will be parsed instead.
- * The remaining space in the git_oid will be set to zero.
+ * If N is odd, the last byte's high nibble will be read in and the
+ * low nibble set to zero.
  *
  * @param out oid structure the result is written into.
  * @param str input hex string of at least size `length`
diff --git a/include/git2/patch.h b/include/git2/patch.h
index 790cb74..4eb9f02 100644
--- a/include/git2/patch.h
+++ b/include/git2/patch.h
@@ -191,7 +191,7 @@
  *
  * @param patch The git_patch object
  * @param hunk_idx Index of the hunk
- * @return Number of lines in hunk or -1 if invalid hunk index
+ * @return Number of lines in hunk or GIT_ENOTFOUND if invalid hunk index
  */
 GIT_EXTERN(int) git_patch_num_lines_in_hunk(
 	const git_patch *patch,
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 c459f42..244794e 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -26,8 +26,6 @@
  */
 GIT_BEGIN_DECL
 
-typedef int (*git_remote_rename_problem_cb)(const char *problematic_refspec, void *payload);
-
 /**
  * Add a remote with the default fetch refspec to the repository's configuration.
  *
@@ -360,6 +358,8 @@
 } git_push_update;
 
 /**
+ * Callback used to inform of upcoming updates.
+ *
  * @param updates an array containing the updates which will be sent
  * as commands to the destination.
  * @param len number of elements in `updates`
@@ -403,7 +403,7 @@
 	 * connection to proceed. Returns 1 to allow the connection, 0
 	 * to disallow it or a negative value to indicate an error.
 	 */
-        git_transport_certificate_check_cb certificate_check;
+	git_transport_certificate_check_cb certificate_check;
 
 	/**
 	 * During the download of new data, this will be regularly
@@ -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.
@@ -796,7 +796,7 @@
 * for the remote will be removed.
 *
 * @param repo the repository in which to act
-* @param name the name of the remove to delete
+* @param name the name of the remote to delete
 * @return 0 on success, or an error code.
 */
 GIT_EXTERN(int) git_remote_delete(git_repository *repo, const char *name);
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 3d70d1b..a396a54 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -35,6 +35,17 @@
  * @return 0 or an error code
  */
 GIT_EXTERN(int) git_repository_open(git_repository **out, const char *path);
+/**
+ * Open working tree as a repository
+ *
+ * Open the working directory of the working tree as a normal
+ * repository that can then be worked on.
+ *
+ * @param out Output pointer containing opened repository
+ * @param wt Working tree to open
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_repository_open_from_worktree(git_repository **out, git_worktree *wt);
 
 /**
  * Create a "fake" repository to wrap an object database
@@ -335,6 +346,17 @@
 GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo);
 
 /**
+ * Retrieve the referenced HEAD for the worktree
+ *
+ * @param out pointer to the reference which will be retrieved
+ * @param repo a repository object
+ * @param name name of the worktree to retrieve HEAD for
+ * @return 0 when successful, error-code otherwise
+ */
+GIT_EXTERN(int) git_repository_head_for_worktree(git_reference **out, git_repository *repo,
+	const char *name);
+
+/**
  * Check if a repository's HEAD is detached
  *
  * A repository's HEAD is detached when it points directly to a commit
@@ -346,6 +368,20 @@
  */
 GIT_EXTERN(int) git_repository_head_detached(git_repository *repo);
 
+/*
+ * Check if a worktree's HEAD is detached
+ *
+ * A worktree's HEAD is detached when it points directly to a
+ * commit instead of a branch.
+ *
+ * @param repo a repository object
+ * @param name name of the worktree to retrieve HEAD for
+ * @return 1 if HEAD is detached, 0 if its not; error code if
+ *  there was an error
+ */
+GIT_EXTERN(int) git_repository_head_detached_for_worktree(git_repository *repo,
+	const char *name);
+
 /**
  * Check if the current branch is unborn
  *
@@ -371,6 +407,42 @@
 GIT_EXTERN(int) git_repository_is_empty(git_repository *repo);
 
 /**
+ * List of items which belong to the git repository layout
+ */
+typedef enum {
+	GIT_REPOSITORY_ITEM_GITDIR,
+	GIT_REPOSITORY_ITEM_WORKDIR,
+	GIT_REPOSITORY_ITEM_COMMONDIR,
+	GIT_REPOSITORY_ITEM_INDEX,
+	GIT_REPOSITORY_ITEM_OBJECTS,
+	GIT_REPOSITORY_ITEM_REFS,
+	GIT_REPOSITORY_ITEM_PACKED_REFS,
+	GIT_REPOSITORY_ITEM_REMOTES,
+	GIT_REPOSITORY_ITEM_CONFIG,
+	GIT_REPOSITORY_ITEM_INFO,
+	GIT_REPOSITORY_ITEM_HOOKS,
+	GIT_REPOSITORY_ITEM_LOGS,
+	GIT_REPOSITORY_ITEM_MODULES,
+	GIT_REPOSITORY_ITEM_WORKTREES
+} git_repository_item_t;
+
+/**
+ * Get the location of a specific repository file or directory
+ *
+ * This function will retrieve the path of a specific repository
+ * item. It will thereby honor things like the repository's
+ * common directory, gitdir, etc. In case a file path cannot
+ * exist for a given item (e.g. the working directory of a bare
+ * repository), an error is returned.
+ *
+ * @param out Buffer to store the path at
+ * @param repo Repository to get path for
+ * @param item The repository item for which to retrieve the path
+ * @return 0 on success, otherwise a negative value
+ */
+GIT_EXTERN(int) git_repository_item_path(git_buf *out, git_repository *repo, git_repository_item_t item);
+
+/**
  * Get the path of this repository
  *
  * This is the path of the `.git` folder for normal repositories,
@@ -393,6 +465,17 @@
 GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo);
 
 /**
+ * Get the path of the shared common directory for this repository
+ *
+ * If the repository is bare is not a worktree, the git directory
+ * path is returned.
+ *
+ * @param repo A repository object
+ * @return the path to the common dir
+ */
+GIT_EXTERN(const char *) git_repository_commondir(git_repository *repo);
+
+/**
  * Set the path to the working directory for this repository
  *
  * The working directory doesn't need to be the same one
@@ -421,6 +504,14 @@
 GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
 
 /**
+ * Check if a repository is a linked work tree
+ *
+ * @param repo Repo to test
+ * @return 1 if the repository is a linked work tree, 0 otherwise.
+ */
+GIT_EXTERN(int) git_repository_is_worktree(git_repository *repo);
+
+/**
  * Get the configuration file for this repository.
  *
  * If a configuration file has not been set, the default
diff --git a/include/git2/reset.h b/include/git2/reset.h
index 7907529..bd29c69 100644
--- a/include/git2/reset.h
+++ b/include/git2/reset.h
@@ -53,7 +53,7 @@
  *
  * @param reset_type Kind of reset operation to perform.
  *
- * @param checkout_opts Checkout options to be used for a HARD reset.
+ * @param checkout_opts Optional checkout options to be used for a HARD reset.
  * The checkout_strategy field will be overridden (based on reset_type).
  * This parameter can be used to propagate notify and progress callbacks.
  *
diff --git a/include/git2/revert.h b/include/git2/revert.h
index 2de1942..82dbadc 100644
--- a/include/git2/revert.h
+++ b/include/git2/revert.h
@@ -75,7 +75,7 @@
  *
  * @param repo the repository to revert
  * @param commit the commit to revert
- * @param given_opts merge flags
+ * @param given_opts the revert options (or null for defaults)
  * @return zero on success, -1 on failure.
  */
 GIT_EXTERN(int) git_revert(
diff --git a/include/git2/stash.h b/include/git2/stash.h
index 733d75a..3af9cde 100644
--- a/include/git2/stash.h
+++ b/include/git2/stash.h
@@ -173,7 +173,7 @@
  * @param repo The owning repository.
  * @param index The position within the stash list. 0 points to the
  *              most recent stashed state.
- * @param options Options to control how stashes are applied.
+ * @param options Optional options to control how stashes are applied.
  *
  * @return 0 on success, GIT_ENOTFOUND if there's no stashed state for the
  *         given index, GIT_EMERGECONFLICT if changes exist in the working
@@ -242,7 +242,7 @@
  * @param repo The owning repository.
  * @param index The position within the stash list. 0 points to the
  *              most recent stashed state.
- * @param options Options to control how stashes are applied.
+ * @param options Optional options to control how stashes are applied.
  *
  * @return 0 on success, GIT_ENOTFOUND if there's no stashed state for the given
  * index, or error code. (see git_stash_apply() above for details)
diff --git a/include/git2/submodule.h b/include/git2/submodule.h
index 540ecf5..b2b3039 100644
--- a/include/git2/submodule.h
+++ b/include/git2/submodule.h
@@ -134,9 +134,7 @@
 	 * checkout, set the `checkout_strategy` to
 	 * `GIT_CHECKOUT_NONE`. Generally you will want the use
 	 * GIT_CHECKOUT_SAFE to update files in the working
-	 * directory. Use the `clone_checkout_strategy` field
-	 * to set the checkout strategy that will be used in
-	 * the case where update needs to clone the repository.
+	 * directory. 
 	 */
 	git_checkout_options checkout_opts;
 
@@ -149,13 +147,6 @@
 	git_fetch_options fetch_opts;
 
 	/**
-	 * The checkout strategy to use when the sub repository needs to
-	 * be cloned. Use GIT_CHECKOUT_SAFE to create all files
-	 * in the working directory for the newly cloned repository.
-	 */
-	unsigned int clone_checkout_strategy;
-
-	/**
 	 * Allow fetching from the submodule's default remote if the target
 	 * commit isn't found. Enabled by default.
 	 */
@@ -166,7 +157,7 @@
 #define GIT_SUBMODULE_UPDATE_OPTIONS_INIT \
 	{ GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, \
 		{ GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE }, \
-	GIT_FETCH_OPTIONS_INIT, GIT_CHECKOUT_SAFE, 1 }
+	GIT_FETCH_OPTIONS_INIT, 1 }
 
 /**
  * Initializes a `git_submodule_update_options` with default values.
diff --git a/include/git2/sys/merge.h b/include/git2/sys/merge.h
index 0319410..eed106c 100644
--- a/include/git2/sys/merge.h
+++ b/include/git2/sys/merge.h
@@ -36,23 +36,23 @@
 typedef struct git_merge_driver_source git_merge_driver_source;
 
 /** Get the repository that the source data is coming from. */
-GIT_EXTERN(git_repository *) git_merge_driver_source_repo(
+GIT_EXTERN(const git_repository *) git_merge_driver_source_repo(
 	const git_merge_driver_source *src);
 
 /** Gets the ancestor of the file to merge. */
-GIT_EXTERN(git_index_entry *) git_merge_driver_source_ancestor(
+GIT_EXTERN(const git_index_entry *) git_merge_driver_source_ancestor(
 	const git_merge_driver_source *src);
 
 /** Gets the ours side of the file to merge. */
-GIT_EXTERN(git_index_entry *) git_merge_driver_source_ours(
+GIT_EXTERN(const git_index_entry *) git_merge_driver_source_ours(
 	const git_merge_driver_source *src);
 
 /** Gets the theirs side of the file to merge. */
-GIT_EXTERN(git_index_entry *) git_merge_driver_source_theirs(
+GIT_EXTERN(const git_index_entry *) git_merge_driver_source_theirs(
 	const git_merge_driver_source *src);
 
 /** Gets the merge file options that the merge was invoked with */
-GIT_EXTERN(git_merge_file_options *) git_merge_driver_source_file_options(
+GIT_EXTERN(const git_merge_file_options *) git_merge_driver_source_file_options(
 	const git_merge_driver_source *src);
 
 
diff --git a/include/git2/sys/repository.h b/include/git2/sys/repository.h
index 800396c..0c91421 100644
--- a/include/git2/sys/repository.h
+++ b/include/git2/sys/repository.h
@@ -135,6 +135,35 @@
  */
 GIT_EXTERN(int) git_repository_set_bare(git_repository *repo);
 
+/**
+ * Load and cache all submodules.
+ *
+ * Because the `.gitmodules` file is unstructured, loading submodules is an
+ * O(N) operation.  Any operation (such as `git_rebase_init`) that requires
+ * accessing all submodules is O(N^2) in the number of submodules, if it
+ * has to look each one up individually.  This function loads all submodules
+ * and caches them so that subsequent calls to `git_submodule_lookup` are O(1).
+ *
+ * @param repo the repository whose submodules will be cached.
+ */
+GIT_EXTERN(int) git_repository_submodule_cache_all(
+	git_repository *repo);
+
+/**
+ * Clear the submodule cache.
+ *
+ * Clear the submodule cache populated by `git_repository_submodule_cache_all`.
+ * If there is no cache, do nothing.
+ *
+ * The cache incorporates data from the repository's configuration, as well
+ * as the state of the working tree, the index, and HEAD.  So any time any
+ * of these has changed, the cache might become invalid.
+ *
+ * @param repo the repository whose submodule cache will be cleared
+ */
+GIT_EXTERN(int) git_repository_submodule_cache_clear(
+	git_repository *repo);
+
 /** @} */
 GIT_END_DECL
 #endif
diff --git a/include/git2/transaction.h b/include/git2/transaction.h
index 64abb0c..00ca139 100644
--- a/include/git2/transaction.h
+++ b/include/git2/transaction.h
@@ -8,6 +8,14 @@
 #define INCLUDE_git_transaction_h__
 
 #include "common.h"
+
+/**
+ * @file git2/transaction.h
+ * @brief Git transactional reference routines
+ * @defgroup git_transaction Git transactional reference routines
+ * @ingroup Git
+ * @{
+ */
 GIT_BEGIN_DECL
 
 /**
@@ -107,5 +115,6 @@
  */
 GIT_EXTERN(void) git_transaction_free(git_transaction *tx);
 
+/** @} */
 GIT_END_DECL
 #endif
diff --git a/include/git2/transport.h b/include/git2/transport.h
index 0ec2416..0c371bf 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -321,13 +321,13 @@
 /**
  * Signature of a function which acquires a credential object.
  *
- * - cred: The newly created credential object.
- * - url: The resource for which we are demanding a credential.
- * - username_from_url: The username that was embedded in a "user\@host"
+ * @param cred The newly created credential object.
+ * @param url The resource for which we are demanding a credential.
+ * @param username_from_url The username that was embedded in a "user\@host"
  *                          remote url, or NULL if not included.
- * - allowed_types: A bitmask stating which cred types are OK to return.
- * - payload: The payload provided when specifying this callback.
- * - returns 0 for success, < 0 to indicate an error, > 0 to indicate
+ * @param allowed_types A bitmask stating which cred types are OK to return.
+ * @param payload The payload provided when specifying this callback.
+ * @return 0 for success, < 0 to indicate an error, > 0 to indicate
  *       no credential was acquired
  */
 typedef int (*git_cred_acquire_cb)(
diff --git a/include/git2/tree.h b/include/git2/tree.h
index 2e4735c..4740b1f 100644
--- a/include/git2/tree.h
+++ b/include/git2/tree.h
@@ -375,6 +375,19 @@
 GIT_EXTERN(int) git_treebuilder_write(
 	git_oid *id, git_treebuilder *bld);
 
+/**
+ * Write the contents of the tree builder as a tree object
+ * using a shared git_buf.
+ *
+ * @see git_treebuilder_write
+ *
+ * @param oid Pointer to store the OID of the newly written tree
+ * @param bld Tree builder to write
+ * @param tree Shared buffer for writing the tree. Will be grown as necessary.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_treebuilder_write_with_buffer(
+	git_oid *oid, git_treebuilder *bld, git_buf *tree);
 
 /** Callback for the tree traversal method */
 typedef int (*git_treewalk_cb)(
diff --git a/include/git2/types.h b/include/git2/types.h
index 6f41014..dfdaa29 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -104,6 +104,9 @@
  */
 typedef struct git_repository git_repository;
 
+/** Representation of a working tree */
+typedef struct git_worktree git_worktree;
+
 /** Representation of a generic object in a repository */
 typedef struct git_object git_object;
 
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/include/git2/worktree.h b/include/git2/worktree.h
new file mode 100644
index 0000000..4c4f928
--- /dev/null
+++ b/include/git2/worktree.h
@@ -0,0 +1,173 @@
+/*
+ * 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_git_worktree_h__
+#define INCLUDE_git_worktree_h__
+
+#include "common.h"
+#include "buffer.h"
+#include "types.h"
+#include "strarray.h"
+
+/**
+ * @file git2/worktrees.h
+ * @brief Git worktree related functions
+ * @defgroup git_commit Git worktree related functions
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * List names of linked working trees
+ *
+ * The returned list should be released with `git_strarray_free`
+ * when no longer needed.
+ *
+ * @param out pointer to the array of working tree names
+ * @param repo the repo to use when listing working trees
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_worktree_list(git_strarray *out, git_repository *repo);
+
+/**
+ * Lookup a working tree by its name for a given repository
+ *
+ * @param out Output pointer to looked up worktree or `NULL`
+ * @param repo The repository containing worktrees
+ * @param name Name of the working tree to look up
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name);
+
+/**
+ * Open a worktree of a given repository
+ *
+ * If a repository is not the main tree but a worktree, this
+ * function will look up the worktree inside the parent
+ * repository and create a new `git_worktree` structure.
+ *
+ * @param out Out-pointer for the newly allocated worktree
+ * @param repo Repository to look up worktree for
+ */
+GIT_EXTERN(int) git_worktree_open_from_repository(git_worktree **out, git_repository *repo);
+
+/**
+ * Free a previously allocated worktree
+ *
+ * @param wt worktree handle to close. If NULL nothing occurs.
+ */
+GIT_EXTERN(void) git_worktree_free(git_worktree *wt);
+
+/**
+ * Check if worktree is valid
+ *
+ * A valid worktree requires both the git data structures inside
+ * the linked parent repository and the linked working copy to be
+ * present.
+ *
+ * @param wt Worktree to check
+ * @return 0 when worktree is valid, error-code otherwise
+ */
+GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt);
+
+/**
+ * Add a new working tree
+ *
+ * Add a new working tree for the repository, that is create the
+ * required data structures inside the repository and check out
+ * the current HEAD at `path`
+ *
+ * @param out Output pointer containing new working tree
+ * @param repo Repository to create working tree for
+ * @param name Name of the working tree
+ * @param path Path to create working tree at
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_worktree_add(git_worktree **out, git_repository *repo, const char *name, const char *path);
+
+/**
+ * Lock worktree if not already locked
+ *
+ * Lock a worktree, optionally specifying a reason why the linked
+ * working tree is being locked.
+ *
+ * @param wt Worktree to lock
+ * @param reason Reason why the working tree is being locked
+ * @return 0 on success, non-zero otherwise
+ */
+GIT_EXTERN(int) git_worktree_lock(git_worktree *wt, char *reason);
+
+/**
+ * Unlock a locked worktree
+ *
+ * @param wt Worktree to unlock
+ * @return 0 on success, 1 if worktree was not locked, error-code
+ *  otherwise
+ */
+GIT_EXTERN(int) git_worktree_unlock(git_worktree *wt);
+
+/**
+ * Check if worktree is locked
+ *
+ * A worktree may be locked if the linked working tree is stored
+ * on a portable device which is not available.
+ *
+ * @param reason Buffer to store reason in. If NULL no reason is stored.
+ * @param wt Worktree to check
+ * @return 0 when the working tree not locked, a value greater
+ *  than zero if it is locked, less than zero if there was an
+ *  error
+ */
+GIT_EXTERN(int) git_worktree_is_locked(git_buf *reason, const git_worktree *wt);
+
+/**
+ * Flags which can be passed to git_worktree_prune to alter its
+ * behavior.
+ */
+typedef enum {
+	/* Prune working tree even if working tree is valid */
+	GIT_WORKTREE_PRUNE_VALID = 1u << 0,
+	/* Prune working tree even if it is locked */
+	GIT_WORKTREE_PRUNE_LOCKED = 1u << 1,
+	/* Prune checked out working tree */
+	GIT_WORKTREE_PRUNE_WORKING_TREE = 1u << 2,
+} git_worktree_prune_t;
+
+/**
+ * Is the worktree prunable with the given set of flags?
+ *
+ * A worktree is not prunable in the following scenarios:
+ *
+ * - the worktree is linking to a valid on-disk worktree. The
+ *   GIT_WORKTREE_PRUNE_VALID flag will cause this check to be
+ *   ignored.
+ * - the worktree is not valid but locked. The
+ *   GIT_WORKRTEE_PRUNE_LOCKED flag will cause this check to be
+ *   ignored.
+ *
+ * If the worktree is not valid and not locked or if the above
+ * flags have been passed in, this function will return a
+ * positive value.
+ */
+GIT_EXTERN(int) git_worktree_is_prunable(git_worktree *wt, unsigned flags);
+
+/**
+ * Prune working tree
+ *
+ * Prune the working tree, that is remove the git data
+ * structures on disk. The repository will only be pruned of
+ * `git_worktree_is_prunable` succeeds.
+ *
+ * @param wt Worktree to prune
+ * @param flags git_worktree_prune_t flags
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_worktree_prune(git_worktree *wt, unsigned flags);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/script/appveyor-mingw.sh b/script/appveyor-mingw.sh
index 1988018..d171a72 100755
--- a/script/appveyor-mingw.sh
+++ b/script/appveyor-mingw.sh
@@ -7,15 +7,17 @@
     curl -LsSO http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-win32/sjlj/$f
   fi
   7z x $f > /dev/null
-  mv mingw32 /MinGW
+  export PATH=`pwd`/mingw32/bin:$PATH
 else
   f=x86_64-4.9.2-release-win32-seh-rt_v3-rev1.7z
   if ! [ -e $f ]; then
     curl -LsSO http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/$f
   fi
   7z x $f > /dev/null
-  mv mingw64 /MinGW
+  export PATH=`pwd`/mingw64/bin:$PATH
 fi
 cd build
+gcc --version
+cmake --version
 cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON .. -G"$GENERATOR"
 cmake --build . --config RelWithDebInfo
diff --git a/script/cibuild.sh b/script/cibuild.sh
index b13ad88..403df22 100755
--- a/script/cibuild.sh
+++ b/script/cibuild.sh
@@ -20,7 +20,7 @@
 mkdir _build
 cd _build
 # shellcheck disable=SC2086
-cmake .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS || exit $?
+cmake .. -DBUILD_EXAMPLES=ON -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS || exit $?
 make -j2 install || exit $?
 
 # If this platform doesn't support test execution, bail out now
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..595f5f3 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;
 		}
 
@@ -193,7 +193,7 @@
 	line_num = hunk->hunk.new_start ? hunk->hunk.new_start - 1 : 0;
 
 	if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) {
-		error = apply_err("Hunk at line %d did not apply",
+		error = apply_err("hunk at line %d did not apply",
 			hunk->hunk.new_start);
 		goto done;
 	}
diff --git a/src/attr.c b/src/attr.c
index d43a15f..999f413 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -7,8 +7,6 @@
 #include "git2/oid.h"
 #include <ctype.h>
 
-GIT__USE_STRMAP
-
 const char *git_attr__true  = "[internal]__TRUE__";
 const char *git_attr__false = "[internal]__FALSE__";
 const char *git_attr__unset = "[internal]__UNSET__";
@@ -209,7 +207,7 @@
 				if (git_strmap_exists(seen, assign->name))
 					continue;
 
-				git_strmap_insert(seen, assign->name, assign, error);
+				git_strmap_insert(seen, assign->name, assign, &error);
 				if (error < 0)
 					goto cleanup;
 
@@ -292,7 +290,7 @@
 	int error = 0;
 	const char *workdir = git_repository_workdir(repo);
 	git_index *idx = NULL;
-	git_buf sys = GIT_BUF_INIT;
+	git_buf path = GIT_BUF_INIT;
 
 	if (attr_session && attr_session->init_setup)
 		return 0;
@@ -304,40 +302,45 @@
 	 * definitions will be available for later file parsing
 	 */
 
-	error = system_attr_file(&sys, attr_session);
+	error = system_attr_file(&path, attr_session);
 
 	if (error == 0)
 		error = preload_attr_file(
-			repo, attr_session, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr);
+			repo, attr_session, GIT_ATTR_FILE__FROM_FILE, NULL, path.ptr);
 
 	if (error != GIT_ENOTFOUND)
-		return error;
-
-	git_buf_free(&sys);
+		goto out;
 
 	if ((error = preload_attr_file(
 			repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
 			NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0)
-		return error;
+		goto out;
+
+	if ((error = git_repository_item_path(&path,
+			repo, GIT_REPOSITORY_ITEM_INFO)) < 0)
+		goto out;
 
 	if ((error = preload_attr_file(
 			repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
-			git_repository_path(repo), GIT_ATTR_FILE_INREPO)) < 0)
-		return error;
+			path.ptr, GIT_ATTR_FILE_INREPO)) < 0)
+		goto out;
 
 	if (workdir != NULL &&
 		(error = preload_attr_file(
 			repo, attr_session, GIT_ATTR_FILE__FROM_FILE, workdir, GIT_ATTR_FILE)) < 0)
-		return error;
+		goto out;
 
 	if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
 		(error = preload_attr_file(
 			repo, attr_session, GIT_ATTR_FILE__FROM_INDEX, NULL, GIT_ATTR_FILE)) < 0)
-		return error;
+		goto out;
 
 	if (attr_session)
 		attr_session->init_setup = 1;
 
+out:
+	git_buf_free(&path);
+
 	return error;
 }
 
@@ -472,7 +475,7 @@
 	git_vector *files)
 {
 	int error = 0;
-	git_buf dir = GIT_BUF_INIT;
+	git_buf dir = GIT_BUF_INIT, attrfile = GIT_BUF_INIT;
 	const char *workdir = git_repository_workdir(repo);
 	attr_walk_up_info info = { NULL };
 
@@ -494,9 +497,13 @@
 	 * - $GIT_PREFIX/etc/gitattributes
 	 */
 
+	error = git_repository_item_path(&attrfile, repo, GIT_REPOSITORY_ITEM_INFO);
+	if (error < 0)
+		goto cleanup;
+
 	error = push_attr_file(
 		repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
-		git_repository_path(repo), GIT_ATTR_FILE_INREPO);
+		attrfile.ptr, GIT_ATTR_FILE_INREPO);
 	if (error < 0)
 		goto cleanup;
 
@@ -538,6 +545,7 @@
  cleanup:
 	if (error < 0)
 		release_attr_files(files);
+	git_buf_free(&attrfile);
 	git_buf_free(&dir);
 
 	return error;
diff --git a/src/attr_file.c b/src/attr_file.c
index 11d1493..0bb761d 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -30,7 +30,7 @@
 	GITERR_CHECK_ALLOC(attrs);
 
 	if (git_mutex_init(&attrs->lock) < 0) {
-		giterr_set(GITERR_OS, "Failed to initialize lock");
+		giterr_set(GITERR_OS, "failed to initialize lock");
 		git__free(attrs);
 		return -1;
 	}
@@ -49,7 +49,7 @@
 	git_attr_rule *rule;
 
 	if (need_lock && git_mutex_lock(&file->lock) < 0) {
-		giterr_set(GITERR_OS, "Failed to lock attribute file");
+		giterr_set(GITERR_OS, "failed to lock attribute file");
 		return -1;
 	}
 
@@ -140,7 +140,7 @@
 		break;
 	}
 	default:
-		giterr_set(GITERR_INVALID, "Unknown file source %d", source);
+		giterr_set(GITERR_INVALID, "unknown file source %d", source);
 		return -1;
 	}
 
@@ -212,7 +212,7 @@
 	}
 
 	default:
-		giterr_set(GITERR_INVALID, "Invalid file type %d", file->source);
+		giterr_set(GITERR_INVALID, "invalid file type %d", file->source);
 		return -1;
 	}
 }
@@ -238,7 +238,7 @@
 		context = attrs->entry->path;
 
 	if (git_mutex_lock(&attrs->lock) < 0) {
-		giterr_set(GITERR_OS, "Failed to lock attribute file");
+		giterr_set(GITERR_OS, "failed to lock attribute file");
 		return -1;
 	}
 
diff --git a/src/attr_file.h b/src/attr_file.h
index 388ecf4..a9af240 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -15,7 +15,7 @@
 #include "fileops.h"
 
 #define GIT_ATTR_FILE			".gitattributes"
-#define GIT_ATTR_FILE_INREPO	"info/attributes"
+#define GIT_ATTR_FILE_INREPO	"attributes"
 #define GIT_ATTR_FILE_SYSTEM	"gitattributes"
 #define GIT_ATTR_FILE_XDG		"attributes"
 
diff --git a/src/attrcache.c b/src/attrcache.c
index a571106..4df14ee 100644
--- a/src/attrcache.c
+++ b/src/attrcache.c
@@ -5,14 +5,12 @@
 #include "sysdir.h"
 #include "ignore.h"
 
-GIT__USE_STRMAP
-
 GIT_INLINE(int) attr_cache_lock(git_attr_cache *cache)
 {
 	GIT_UNUSED(cache); /* avoid warning if threading is off */
 
 	if (git_mutex_lock(&cache->lock) < 0) {
-		giterr_set(GITERR_OS, "Unable to get attr cache lock");
+		giterr_set(GITERR_OS, "unable to get attr cache lock");
 		return -1;
 	}
 	return 0;
@@ -82,7 +80,7 @@
 		&entry, git_repository_workdir(repo), path, &cache->pool);
 
 	if (!error) {
-		git_strmap_insert(cache->files, entry->path, entry, error);
+		git_strmap_insert(cache->files, entry->path, entry, &error);
 		if (error > 0)
 			error = 0;
 	}
@@ -105,8 +103,11 @@
 	GIT_REFCOUNT_OWN(file, entry);
 	GIT_REFCOUNT_INC(file);
 
-	old = git__compare_and_swap(
-		&entry->file[file->source], entry->file[file->source], file);
+	/*
+	 * Replace the existing value if another thread has
+	 * created it in the meantime.
+	 */
+	old = git__swap(entry->file[file->source], file);
 
 	if (old) {
 		GIT_REFCOUNT_OWN(old, NULL);
@@ -121,20 +122,22 @@
 {
 	int error = 0;
 	git_attr_file_entry *entry;
+	git_attr_file *old = NULL;
 
 	if (!file)
 		return 0;
+
 	if ((error = attr_cache_lock(cache)) < 0)
 		return error;
 
 	if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL)
-		file = git__compare_and_swap(&entry->file[file->source], file, NULL);
+		old = git__compare_and_swap(&entry->file[file->source], file, NULL);
 
 	attr_cache_unlock(cache);
 
-	if (file) {
-		GIT_REFCOUNT_OWN(file, NULL);
-		git_attr_file__free(file);
+	if (old) {
+		GIT_REFCOUNT_OWN(old, NULL);
+		git_attr_file__free(old);
 	}
 
 	return error;
@@ -309,7 +312,7 @@
 	if (!cache)
 		return;
 
-	unlock = (git_mutex_lock(&cache->lock) == 0);
+	unlock = (attr_cache_lock(cache) == 0);
 
 	if (cache->files != NULL) {
 		git_attr_file_entry *entry;
@@ -345,13 +348,13 @@
 	cache->cfg_excl_file = NULL;
 
 	if (unlock)
-		git_mutex_unlock(&cache->lock);
+		attr_cache_unlock(cache);
 	git_mutex_free(&cache->lock);
 
 	git__free(cache);
 }
 
-int git_attr_cache__do_init(git_repository *repo)
+int git_attr_cache__init(git_repository *repo)
 {
 	int ret = 0;
 	git_attr_cache *cache = git_repository_attr_cache(repo);
@@ -365,7 +368,7 @@
 
 	/* set up lock */
 	if (git_mutex_init(&cache->lock) < 0) {
-		giterr_set(GITERR_OS, "Unable to initialize lock for attr cache");
+		giterr_set(GITERR_OS, "unable to initialize lock for attr cache");
 		git__free(cache);
 		return -1;
 	}
@@ -429,11 +432,11 @@
 	if (macro->assigns.length == 0)
 		return 0;
 
-	if (git_mutex_lock(&cache->lock) < 0) {
-		giterr_set(GITERR_OS, "Unable to get attr cache lock");
+	if (attr_cache_lock(cache) < 0) {
+		giterr_set(GITERR_OS, "unable to get attr cache lock");
 		error = -1;
 	} else {
-		git_strmap_insert(macros, macro->match.pattern, macro, error);
+		git_strmap_insert(macros, macro->match.pattern, macro, &error);
 		git_mutex_unlock(&cache->lock);
 	}
 
diff --git a/src/attrcache.h b/src/attrcache.h
index 44e1ffd..b91edd3 100644
--- a/src/attrcache.h
+++ b/src/attrcache.h
@@ -22,10 +22,7 @@
 	git_pool  pool;
 } git_attr_cache;
 
-extern int git_attr_cache__do_init(git_repository *repo);
-
-#define git_attr_cache__init(REPO) \
-	(git_repository_attr_cache(REPO) ? 0 : git_attr_cache__do_init(REPO))
+extern int git_attr_cache__init(git_repository *repo);
 
 /* get file - loading and reload as needed */
 extern int git_attr_cache__get(
diff --git a/src/blame_git.c b/src/blame_git.c
index 96785c7..735b62d 100644
--- a/src/blame_git.c
+++ b/src/blame_git.c
@@ -478,14 +478,15 @@
  * The blobs of origin and porigin exactly match, so everything origin is
  * suspected for can be blamed on the parent.
  */
-static void pass_whole_blame(git_blame *blame,
+static int pass_whole_blame(git_blame *blame,
 		git_blame__origin *origin, git_blame__origin *porigin)
 {
 	git_blame__entry *e;
 
-	if (!porigin->blob)
-		git_object_lookup((git_object**)&porigin->blob, blame->repository,
-				git_blob_id(origin->blob), GIT_OBJ_BLOB);
+	if (!porigin->blob &&
+	    git_object_lookup((git_object**)&porigin->blob, blame->repository,
+				git_blob_id(origin->blob), GIT_OBJ_BLOB) < 0)
+		return -1;
 	for (e=blame->ent; e; e=e->next) {
 		if (!same_suspect(e->suspect, origin))
 			continue;
@@ -493,6 +494,8 @@
 		origin_decref(e->suspect);
 		e->suspect = porigin;
 	}
+
+	return 0;
 }
 
 static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt)
@@ -543,7 +546,8 @@
 		}
 		if (porigin->blob && origin->blob &&
 		    !git_oid_cmp(git_blob_id(porigin->blob), git_blob_id(origin->blob))) {
-			pass_whole_blame(blame, origin, porigin);
+			error = pass_whole_blame(blame, origin, porigin);
+				goto finish;
 			origin_decref(porigin);
 			goto finish;
 		}
diff --git a/src/blob.c b/src/blob.c
index 1926c9e..19d3039 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -96,7 +96,7 @@
 	p_close(fd);
 
 	if (written != file_size || read_len < 0) {
-		giterr_set(GITERR_OS, "Failed to read file into stream");
+		giterr_set(GITERR_OS, "failed to read file into stream");
 		error = -1;
 	}
 
@@ -142,7 +142,7 @@
 
 	read_len = p_readlink(path, link_data, link_size);
 	if (read_len != (ssize_t)link_size) {
-		giterr_set(GITERR_OS, "Failed to create blob.  Can't read symlink '%s'", path);
+		giterr_set(GITERR_OS, "failed to create blob: cannot read symlink '%s'", path);
 		git__free(link_data);
 		return -1;
 	}
@@ -186,7 +186,7 @@
 		goto done;
 
 	if (S_ISDIR(st.st_mode)) {
-		giterr_set(GITERR_ODB, "cannot create blob from '%s'; it is a directory", content_path);
+		giterr_set(GITERR_ODB, "cannot create blob from '%s': it is a directory", content_path);
 		error = GIT_EDIRECTORY;
 		goto done;
 	}
@@ -326,8 +326,8 @@
 	stream->parent.close = blob_writestream_close;
 	stream->parent.free  = blob_writestream_free;
 
-	if ((error = git_buf_joinpath(&path,
-				      git_repository_path(repo), GIT_OBJECTS_DIR "streamed")) < 0)
+	if ((error = git_repository_item_path(&path, repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0
+		|| (error = git_buf_joinpath(&path, path.ptr, "streamed")) < 0)
 		goto cleanup;
 
 	if ((error = git_filebuf_open_withsize(&stream->fbuf, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY,
diff --git a/src/branch.c b/src/branch.c
index 51c35d7..7d5e9cb 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -13,6 +13,7 @@
 #include "refs.h"
 #include "remote.h"
 #include "annotated_commit.h"
+#include "worktree.h"
 
 #include "git2/branch.h"
 
@@ -33,7 +34,7 @@
 		/* OOM */;
 	else if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0)
 		giterr_set(
-			GITERR_REFERENCE, "Cannot locate %s branch '%s'",
+			GITERR_REFERENCE, "cannot locate %s branch '%s'",
 			is_remote ? "remote-tracking" : "local", branch_name);
 
 	*branch_reference_out = branch; /* will be NULL on error */
@@ -46,7 +47,7 @@
 {
 	giterr_set(
 		GITERR_INVALID,
-		"Reference '%s' is not a local branch.", reference_name);
+		"reference '%s' is not a local branch.", reference_name);
 	return -1;
 }
 
@@ -58,16 +59,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,11 +77,11 @@
 		if (error < 0)
 			goto cleanup;
 
-		is_head = error;
+		is_unmovable_head = error;
 	}
 
-	if (is_head && force) {
-		giterr_set(GITERR_REFERENCE, "Cannot force update branch '%s' as it is "
+	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;
 		goto cleanup;
@@ -125,6 +127,62 @@
 		repository, branch_name, commit->commit, commit->description, force);
 }
 
+int git_branch_is_checked_out(
+	const git_reference *branch)
+{
+	git_buf path = GIT_BUF_INIT, buf = GIT_BUF_INIT;
+	git_strarray worktrees;
+	git_reference *ref = NULL;
+	git_repository *repo;
+	const char *worktree;
+	int found = false;
+	size_t i;
+
+	assert(branch && git_reference_is_branch(branch));
+
+	repo = git_reference_owner(branch);
+
+	if (git_worktree_list(&worktrees, repo) < 0)
+		return -1;
+
+	for (i = 0; i < worktrees.count; i++) {
+		worktree = worktrees.strings[i];
+
+		if (git_repository_head_for_worktree(&ref, repo, worktree) < 0)
+			continue;
+
+		if (git__strcmp(ref->name, branch->name) == 0) {
+			found = true;
+			git_reference_free(ref);
+			break;
+		}
+
+		git_reference_free(ref);
+	}
+	git_strarray_free(&worktrees);
+
+	if (found)
+		return found;
+
+	/* Check HEAD of parent */
+	if (git_buf_joinpath(&path, repo->commondir, GIT_HEAD_FILE) < 0)
+		goto out;
+	if (git_futils_readbuffer(&buf, path.ptr) < 0)
+		goto out;
+	if (git__prefixcmp(buf.ptr, "ref: ") == 0)
+		git_buf_consume(&buf, buf.ptr + strlen("ref: "));
+	git_buf_rtrim(&buf);
+
+	found = git__strcmp(buf.ptr, branch->name) == 0;
+
+out:
+	git_buf_free(&buf);
+	git_buf_free(&path);
+
+	return found;
+}
+
+
 int git_branch_delete(git_reference *branch)
 {
 	int is_head;
@@ -134,7 +192,7 @@
 	assert(branch);
 
 	if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) {
-		giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.",
+		giterr_set(GITERR_INVALID, "reference '%s' is not a valid branch.",
 			git_reference_name(branch));
 		return GIT_ENOTFOUND;
 	}
@@ -143,11 +201,17 @@
 		return is_head;
 
 	if (is_head) {
-		giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is "
+		giterr_set(GITERR_REFERENCE, "cannot delete branch '%s' as it is "
 			"the current HEAD of the repository.", git_reference_name(branch));
 		return -1;
 	}
 
+	if (git_reference_is_branch(branch) && git_branch_is_checked_out(branch)) {
+		giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is "
+			"the current HEAD of a linked repository.", git_reference_name(branch));
+		return -1;
+	}
+
 	if (git_buf_join(&config_section, '.', "branch",
 			git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
 		goto on_error;
@@ -305,7 +369,7 @@
 		branch_name += strlen(GIT_REFS_REMOTES_DIR);
 	} else {
 		giterr_set(GITERR_INVALID,
-				"Reference '%s' is neither a local nor a remote branch.", ref->name);
+				"reference '%s' is neither a local nor a remote branch.", ref->name);
 		return -1;
 	}
 	*out = branch_name;
@@ -435,7 +499,7 @@
 
 	/* Verify that this is a remote branch */
 	if (!git_reference__is_remote(refname)) {
-		giterr_set(GITERR_INVALID, "Reference '%s' is not a remote branch.",
+		giterr_set(GITERR_INVALID, "reference '%s' is not a remote branch.",
 			refname);
 		error = GIT_ERROR;
 		goto cleanup;
@@ -462,7 +526,7 @@
 				git_remote_free(remote);
 
 				giterr_set(GITERR_REFERENCE,
-					"Reference '%s' is ambiguous", refname);
+					"reference '%s' is ambiguous", refname);
 				error = GIT_EAMBIGUOUS;
 				goto cleanup;
 			}
@@ -476,7 +540,7 @@
 		error = git_buf_puts(buf, remote_name);
 	} else {
 		giterr_set(GITERR_REFERENCE,
-			"Could not determine remote for '%s'", refname);
+			"could not determine remote for '%s'", refname);
 		error = GIT_ENOTFOUND;
 	}
 
@@ -565,7 +629,7 @@
 		local = 0;
 	else {
 		giterr_set(GITERR_REFERENCE,
-			"Cannot set upstream for branch '%s'", shortname);
+			"cannot set upstream for branch '%s'", shortname);
 		return GIT_ENOTFOUND;
 	}
 
diff --git a/src/buffer.c b/src/buffer.c
index d135ebe..fdb732d 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -962,14 +962,14 @@
 			case '0': case '1': case '2': case '3':
 				if (j == buf->size-3) {
 					giterr_set(GITERR_INVALID,
-						"Truncated quoted character \\%c", ch);
+						"truncated quoted character \\%c", ch);
 					return -1;
 				}
 
 				if (buf->ptr[j+1] < '0' || buf->ptr[j+1] > '7' ||
 					buf->ptr[j+2] < '0' || buf->ptr[j+2] > '7') {
 					giterr_set(GITERR_INVALID,
-						"Truncated quoted character \\%c%c%c",
+						"truncated quoted character \\%c%c%c",
 						buf->ptr[j], buf->ptr[j+1], buf->ptr[j+2]);
 					return -1;
 				}
@@ -981,7 +981,7 @@
 				break;
 
 			default:
-				giterr_set(GITERR_INVALID, "Invalid quoted character \\%c", ch);
+				giterr_set(GITERR_INVALID, "invalid quoted character \\%c", ch);
 				return -1;
 			}
 		}
@@ -995,6 +995,6 @@
 	return 0;
 
 invalid:
-	giterr_set(GITERR_INVALID, "Invalid quoted line");
+	giterr_set(GITERR_INVALID, "invalid quoted line");
 	return -1;
 }
diff --git a/src/buffer.h b/src/buffer.h
index cdfca6d..a76b2d7 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -66,7 +66,8 @@
  * library, when providing git_buf's, may wish to provide a NULL ptr for
  * ease of handling.  The buffer routines, however, expect a non-NULL ptr
  * always.  This helper method simply handles NULL input, converting to a
- * git_buf__initbuf.
+ * git_buf__initbuf. If a buffer with a non-NULL ptr is passed in, this method
+ * assures that the buffer is '\0'-terminated.
  */
 extern void git_buf_sanitize(git_buf *buf);
 
diff --git a/src/cache.c b/src/cache.c
index ca5173c..c92a3a7 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -15,8 +15,6 @@
 #include "object.h"
 #include "git2/oid.h"
 
-GIT__USE_OIDMAP
-
 bool git_cache__enabled = true;
 ssize_t git_cache__max_storage = (256 * 1024 * 1024);
 git_atomic_ssize git_cache__current_storage = {0};
@@ -47,13 +45,13 @@
 {
 	git_cached_obj *object;
 
-	if (kh_size(cache->map) == 0)
+	if (git_cache_size(cache) == 0)
 		return;
 
-	printf("Cache %p: %d items cached, %"PRIdZ" bytes\n",
-		cache, kh_size(cache->map), cache->used_memory);
+	printf("Cache %p: %"PRIuZ" items cached, %"PRIdZ" bytes\n",
+		cache, git_cache_size(cache), cache->used_memory);
 
-	kh_foreach_value(cache->map, object, {
+	git_oidmap_foreach_value(cache->map, object, {
 		char oid_str[9];
 		printf(" %s%c %s (%"PRIuZ")\n",
 			git_object_type2string(object->type),
@@ -70,7 +68,7 @@
 	cache->map = git_oidmap_alloc();
 	GITERR_CHECK_ALLOC(cache->map);
 	if (git_rwlock_init(&cache->lock)) {
-		giterr_set(GITERR_OS, "Failed to initialize cache rwlock");
+		giterr_set(GITERR_OS, "failed to initialize cache rwlock");
 		return -1;
 	}
 	return 0;
@@ -81,14 +79,14 @@
 {
 	git_cached_obj *evict = NULL;
 
-	if (kh_size(cache->map) == 0)
+	if (git_cache_size(cache) == 0)
 		return;
 
-	kh_foreach_value(cache->map, evict, {
+	git_oidmap_foreach_value(cache->map, evict, {
 		git_cached_obj_decref(evict);
 	});
 
-	kh_clear(oid, cache->map);
+	git_oidmap_clear(cache->map);
 	git_atomic_ssize_add(&git_cache__current_storage, -cache->used_memory);
 	cache->used_memory = 0;
 }
@@ -119,22 +117,22 @@
 	ssize_t evicted_memory = 0;
 
 	/* do not infinite loop if there's not enough entries to evict  */
-	if (evict_count > kh_size(cache->map)) {
+	if (evict_count > git_cache_size(cache)) {
 		clear_cache(cache);
 		return;
 	}
 
 	while (evict_count > 0) {
-		khiter_t pos = seed++ % kh_end(cache->map);
+		khiter_t pos = seed++ % git_oidmap_end(cache->map);
 
-		if (kh_exist(cache->map, pos)) {
-			git_cached_obj *evict = kh_val(cache->map, pos);
+		if (git_oidmap_has_data(cache->map, pos)) {
+			git_cached_obj *evict = git_oidmap_value_at(cache->map, pos);
 
 			evict_count--;
 			evicted_memory += evict->size;
 			git_cached_obj_decref(evict);
 
-			kh_del(oid, cache->map, pos);
+			git_oidmap_delete_at(cache->map, pos);
 		}
 	}
 
@@ -156,9 +154,9 @@
 	if (!git_cache__enabled || git_rwlock_rdlock(&cache->lock) < 0)
 		return NULL;
 
-	pos = kh_get(oid, cache->map, oid);
-	if (pos != kh_end(cache->map)) {
-		entry = kh_val(cache->map, pos);
+	pos = git_oidmap_lookup_index(cache->map, oid);
+	if (git_oidmap_valid_index(cache->map, pos)) {
+		entry = git_oidmap_value_at(cache->map, pos);
 
 		if (flags && entry->flags != flags) {
 			entry = NULL;
@@ -193,16 +191,14 @@
 	if (git_cache__current_storage.val > git_cache__max_storage)
 		cache_evict_entries(cache);
 
-	pos = kh_get(oid, cache->map, &entry->oid);
+	pos = git_oidmap_lookup_index(cache->map, &entry->oid);
 
 	/* not found */
-	if (pos == kh_end(cache->map)) {
+	if (!git_oidmap_valid_index(cache->map, pos)) {
 		int rval;
 
-		pos = kh_put(oid, cache->map, &entry->oid, &rval);
+		git_oidmap_insert(cache->map, &entry->oid, entry, &rval);
 		if (rval >= 0) {
-			kh_key(cache->map, pos) = &entry->oid;
-			kh_val(cache->map, pos) = entry;
 			git_cached_obj_incref(entry);
 			cache->used_memory += entry->size;
 			git_atomic_ssize_add(&git_cache__current_storage, (ssize_t)entry->size);
@@ -210,7 +206,7 @@
 	}
 	/* found */
 	else {
-		git_cached_obj *stored_entry = kh_val(cache->map, pos);
+		git_cached_obj *stored_entry = git_oidmap_value_at(cache->map, pos);
 
 		if (stored_entry->flags == entry->flags) {
 			git_cached_obj_decref(entry);
@@ -221,8 +217,8 @@
 			git_cached_obj_decref(stored_entry);
 			git_cached_obj_incref(entry);
 
-			kh_key(cache->map, pos) = &entry->oid;
-			kh_val(cache->map, pos) = entry;
+			git_oidmap_set_key_at(cache->map, pos, &entry->oid);
+			git_oidmap_set_value_at(cache->map, pos, entry);
 		} else {
 			/* NO OP */
 		}
diff --git a/src/cache.h b/src/cache.h
index 6971237..0f0bfcf 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -53,7 +53,7 @@
 
 GIT_INLINE(size_t) git_cache_size(git_cache *cache)
 {
-	return (size_t)kh_size(cache->map);
+	return (size_t)git_oidmap_size(cache->map);
 }
 
 GIT_INLINE(void) git_cached_obj_incref(void *_obj)
diff --git a/src/checkout.c b/src/checkout.c
index b3427fb..9d1eed5 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -35,8 +35,6 @@
 #include "pool.h"
 #include "strmap.h"
 
-GIT__USE_STRMAP
-
 /* See docs/checkout-internals.md for more information */
 
 enum {
@@ -1021,13 +1019,13 @@
 	*theirs_out = NULL;
 
 	if (!name_entry->ancestor) {
-		giterr_set(GITERR_INDEX, "A NAME entry exists without an ancestor");
+		giterr_set(GITERR_INDEX, "a NAME entry exists without an ancestor");
 		error = -1;
 		goto done;
 	}
 
 	if (!name_entry->ours && !name_entry->theirs) {
-		giterr_set(GITERR_INDEX, "A NAME entry exists without an ours or theirs");
+		giterr_set(GITERR_INDEX, "a NAME entry exists without an ours or theirs");
 		error = -1;
 		goto done;
 	}
@@ -1035,7 +1033,7 @@
 	if ((ancestor = checkout_conflicts_search_ancestor(data,
 		name_entry->ancestor)) == NULL) {
 		giterr_set(GITERR_INDEX,
-			"A NAME entry referenced ancestor entry '%s' which does not exist in the main index",
+			"a NAME entry referenced ancestor entry '%s' which does not exist in the main index",
 			name_entry->ancestor);
 		error = -1;
 		goto done;
@@ -1047,7 +1045,7 @@
 		else if ((ours = checkout_conflicts_search_branch(data, name_entry->ours)) == NULL ||
 			ours->ours == NULL) {
 			giterr_set(GITERR_INDEX,
-				"A NAME entry referenced our entry '%s' which does not exist in the main index",
+				"a NAME entry referenced our entry '%s' which does not exist in the main index",
 				name_entry->ours);
 			error = -1;
 			goto done;
@@ -1062,7 +1060,7 @@
 		else if ((theirs = checkout_conflicts_search_branch(data, name_entry->theirs)) == NULL ||
 			theirs->theirs == NULL) {
 			giterr_set(GITERR_INDEX,
-				"A NAME entry referenced their entry '%s' which does not exist in the main index",
+				"a NAME entry referenced their entry '%s' which does not exist in the main index",
 				name_entry->theirs);
 			error = -1;
 			goto done;
@@ -1161,7 +1159,7 @@
 		if ((error = git_index_find(&j, index, path)) < 0) {
 			if (error == GIT_ENOTFOUND)
 				giterr_set(GITERR_INDEX,
-					"Index inconsistency, could not find entry for expected conflict '%s'", path);
+					"index inconsistency, could not find entry for expected conflict '%s'", path);
 
 			goto done;
 		}
@@ -1169,7 +1167,7 @@
 		for (; j < len; j++) {
 			if ((entry = git_index_get_byindex(index, j)) == NULL) {
 				giterr_set(GITERR_INDEX,
-					"Index inconsistency, truncated index while loading expected conflict '%s'", path);
+					"index inconsistency, truncated index while loading expected conflict '%s'", path);
 				error = -1;
 				goto done;
 			}
@@ -1254,14 +1252,14 @@
 
 	if (action & CHECKOUT_ACTION__REMOVE) {
 		if (!git_path_isvalid(repo, delta->old_file.path, flags)) {
-			giterr_set(GITERR_CHECKOUT, "Cannot remove invalid path '%s'", delta->old_file.path);
+			giterr_set(GITERR_CHECKOUT, "cannot remove invalid path '%s'", delta->old_file.path);
 			return -1;
 		}
 	}
 
 	if (action & ~CHECKOUT_ACTION__REMOVE) {
 		if (!git_path_isvalid(repo, delta->new_file.path, flags)) {
-			giterr_set(GITERR_CHECKOUT, "Cannot checkout to invalid path '%s'", delta->new_file.path);
+			giterr_set(GITERR_CHECKOUT, "cannot checkout to invalid path '%s'", delta->new_file.path);
 			return -1;
 		}
 	}
@@ -1430,7 +1428,7 @@
 			 */
 			error = git_futils_rmdir_r(path, NULL, GIT_RMDIR_REMOVE_FILES);
 		} else if (errno != ENOENT) {
-			giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
+			giterr_set(GITERR_OS, "failed to stat '%s'", path);
 			return GIT_EEXISTS;
 		} else {
 			giterr_clear();
@@ -1454,7 +1452,7 @@
 	int ret;
 
 	if ((ret = p_write(stream->fd, buffer, len)) < 0)
-		giterr_set(GITERR_OS, "Could not write to '%s'", stream->path);
+		giterr_set(GITERR_OS, "could not write to '%s'", stream->path);
 
 	return ret;
 }
@@ -1503,7 +1501,7 @@
 		mode = GIT_FILEMODE_BLOB;
 
 	if ((fd = p_open(path, flags, mode)) < 0) {
-		giterr_set(GITERR_OS, "Could not open '%s' for writing", path);
+		giterr_set(GITERR_OS, "could not open '%s' for writing", path);
 		return fd;
 	}
 
@@ -1540,7 +1538,7 @@
 		data->perfdata.stat_calls++;
 
 		if ((error = p_stat(path, st)) < 0) {
-			giterr_set(GITERR_OS, "Error statting '%s'", path);
+			giterr_set(GITERR_OS, "failed to stat '%s'", path);
 			return error;
 		}
 
@@ -1567,7 +1565,7 @@
 
 	if (data->can_symlink) {
 		if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0)
-			giterr_set(GITERR_OS, "Could not create symlink %s", path);
+			giterr_set(GITERR_OS, "could not create symlink %s", path);
 	} else {
 		error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path);
 	}
@@ -1576,7 +1574,7 @@
 		data->perfdata.stat_calls++;
 
 		if ((error = p_lstat(path, st)) < 0)
-			giterr_set(GITERR_CHECKOUT, "Could not stat symlink %s", path);
+			giterr_set(GITERR_CHECKOUT, "could not stat symlink %s", path);
 
 		st->st_mode = GIT_FILEMODE_LINK;
 	}
@@ -1621,7 +1619,7 @@
 	data->perfdata.stat_calls++;
 	if (p_stat(fullpath->ptr, &st) < 0) {
 		giterr_set(
-			GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path);
+			GITERR_CHECKOUT, "could not stat submodule %s\n", file->path);
 		return GIT_ENOTFOUND;
 	}
 
@@ -1694,7 +1692,7 @@
 			return 0;
 
 		/* otherwise, stat error and no update */
-		giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
+		giterr_set(GITERR_OS, "failed to stat '%s'", path);
 		return -1;
 	}
 
@@ -1966,7 +1964,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;
 	}
 
@@ -2097,7 +2095,7 @@
 		goto done;
 
 	if (result.path == NULL || result.mode == 0) {
-		giterr_set(GITERR_CHECKOUT, "Could not merge contents of file");
+		giterr_set(GITERR_CHECKOUT, "could not merge contents of file");
 		error = GIT_ECONFLICT;
 		goto done;
 	}
@@ -2321,8 +2319,6 @@
 	git__free(data->pfx);
 	data->pfx = NULL;
 
-	git_strmap_free(data->mkdir_map);
-
 	git_buf_free(&data->target_path);
 	git_buf_free(&data->tmp);
 
@@ -2330,6 +2326,7 @@
 	data->index = NULL;
 
 	git_strmap_free(data->mkdir_map);
+	data->mkdir_map = NULL;
 
 	git_attr_session__free(&data->attr_session);
 }
@@ -2345,7 +2342,7 @@
 	memset(data, 0, sizeof(*data));
 
 	if (!repo) {
-		giterr_set(GITERR_CHECKOUT, "Cannot checkout nothing");
+		giterr_set(GITERR_CHECKOUT, "cannot checkout nothing");
 		return -1;
 	}
 
@@ -2469,7 +2466,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;
@@ -2553,6 +2550,10 @@
 		GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
 	baseline_opts.start = data.pfx;
 	baseline_opts.end = data.pfx;
+	if (opts && (opts->checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)) {
+		baseline_opts.pathlist.count = opts->paths.count;
+		baseline_opts.pathlist.strings = opts->paths.strings;
+	}
 
 	if (data.opts.baseline_index) {
 		if ((error = git_iterator_for_index(
@@ -2647,7 +2648,7 @@
 
 	if (!index && !repo) {
 		giterr_set(GITERR_CHECKOUT,
-			"Must provide either repository or index to checkout");
+			"must provide either repository or index to checkout");
 		return -1;
 	}
 
@@ -2655,7 +2656,7 @@
 		git_index_owner(index) &&
 		git_index_owner(index) != repo) {
 		giterr_set(GITERR_CHECKOUT,
-			"Index to checkout does not match repository");
+			"index to checkout does not match repository");
 		return -1;
 	} else if(index && repo && !git_index_owner(index)) {
 		GIT_REFCOUNT_OWN(index, repo);
@@ -2694,12 +2695,12 @@
 
 	if (!treeish && !repo) {
 		giterr_set(GITERR_CHECKOUT,
-			"Must provide either repository or tree to checkout");
+			"must provide either repository or tree to checkout");
 		return -1;
 	}
 	if (treeish && repo && git_object_owner(treeish) != repo) {
 		giterr_set(GITERR_CHECKOUT,
-			"Object to checkout does not match repository");
+			"object to checkout does not match repository");
 		return -1;
 	}
 
@@ -2709,7 +2710,7 @@
 	if (treeish) {
 		if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
 			giterr_set(
-				GITERR_CHECKOUT, "Provided object cannot be peeled to a tree");
+				GITERR_CHECKOUT, "provided object cannot be peeled to a tree");
 			return -1;
 		}
 	}
diff --git a/src/cherrypick.c b/src/cherrypick.c
index c929751..d8b6858 100644
--- a/src/cherrypick.c
+++ b/src/cherrypick.c
@@ -28,7 +28,7 @@
 	git_buf file_path = GIT_BUF_INIT;
 	int error = 0;
 
-	if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_CHERRYPICK_HEAD_FILE)) >= 0 &&
+	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_CHERRYPICK_HEAD_FILE)) >= 0 &&
 		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) >= 0 &&
 		(error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0)
 		error = git_filebuf_commit(&file);
@@ -49,7 +49,7 @@
 	git_buf file_path = GIT_BUF_INIT;
 	int error = 0;
 
-	if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
+	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 ||
 		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) < 0 ||
 		(error = git_filebuf_printf(&file, "%s", commit_msg)) < 0)
 		goto cleanup;
@@ -130,13 +130,13 @@
 	if (git_commit_parentcount(cherrypick_commit) > 1) {
 		if (!mainline)
 			return cherrypick_seterr(cherrypick_commit,
-				"Mainline branch is not specified but %s is a merge commit");
+				"mainline branch is not specified but %s is a merge commit");
 
 		parent = mainline;
 	} else {
 		if (mainline)
 			return cherrypick_seterr(cherrypick_commit,
-				"Mainline branch specified but %s is not a merge commit");
+				"mainline branch specified but %s is not a merge commit");
 
 		parent = git_commit_parentcount(cherrypick_commit);
 	}
diff --git a/src/clone.c b/src/clone.c
index 0d4756e..16ddfac 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -513,9 +513,8 @@
 		return error;
 	}
 
-	git_buf_joinpath(&src_odb, git_repository_path(src), GIT_OBJECTS_DIR);
-	git_buf_joinpath(&dst_odb, git_repository_path(repo), GIT_OBJECTS_DIR);
-	if (git_buf_oom(&src_odb) || git_buf_oom(&dst_odb)) {
+	if (git_repository_item_path(&src_odb, src, GIT_REPOSITORY_ITEM_OBJECTS) < 0
+		|| git_repository_item_path(&dst_odb, repo, GIT_REPOSITORY_ITEM_OBJECTS) < 0) {
 		error = -1;
 		goto cleanup;
 	}
diff --git a/src/commit.c b/src/commit.c
index 99a8085..4a34005 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -159,6 +159,9 @@
 	if (git_repository_odb__weakptr(&odb, repo) < 0)
 		goto cleanup;
 
+	if (git_odb__freshen(odb, tree) < 0)
+		goto cleanup;
+
 	if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJ_COMMIT) < 0)
 		goto cleanup;
 
@@ -459,15 +462,16 @@
 	buffer = buffer_start + header_len + 1;
 
 	/* extract commit message */
-	if (buffer <= buffer_end) {
+	if (buffer <= buffer_end)
 		commit->raw_message = git__strndup(buffer, buffer_end - buffer);
-		GITERR_CHECK_ALLOC(commit->raw_message);
-	}
+	else
+		commit->raw_message = git__strdup("");
+	GITERR_CHECK_ALLOC(commit->raw_message);
 
 	return 0;
 
 bad_buffer:
-	giterr_set(GITERR_OBJECT, "Failed to parse bad commit object");
+	giterr_set(GITERR_OBJECT, "failed to parse bad commit object");
 	return -1;
 }
 
@@ -597,7 +601,7 @@
 
 	parent_id = git_commit_parent_id(commit, n);
 	if (parent_id == NULL) {
-		giterr_set(GITERR_INVALID, "Parent %u does not exist", n);
+		giterr_set(GITERR_INVALID, "parent %u does not exist", n);
 		return GIT_ENOTFOUND;
 	}
 
@@ -641,7 +645,7 @@
 {
 	const char *eol, *buf = commit->raw_header;
 
-	git_buf_sanitize(out);
+	git_buf_clear(out);
 
 	while ((eol = strchr(buf, '\n'))) {
 		/* We can skip continuations here */
@@ -705,8 +709,8 @@
 	const char *h, *eol;
 	int error;
 
-	git_buf_sanitize(signature);
-	git_buf_sanitize(signed_data);
+	git_buf_clear(signature);
+	git_buf_clear(signed_data);
 
 	if (!field)
 		field = "gpgsig";
@@ -765,8 +769,9 @@
 		if (git_buf_oom(signature))
 			goto oom;
 
+		error = git_buf_puts(signed_data, eol+1);
 		git_odb_object_free(obj);
-		return git_buf_puts(signed_data, eol+1);
+		return error;
 	}
 
 	giterr_set(GITERR_OBJECT, "this commit is not signed");
diff --git a/src/commit_list.c b/src/commit_list.c
index a1681ff..3bba58c 100644
--- a/src/commit_list.c
+++ b/src/commit_list.c
@@ -61,7 +61,7 @@
 	git_oid_fmt(commit_oid, &commit->oid);
 	commit_oid[GIT_OID_HEXSZ] = '\0';
 
-	giterr_set(GITERR_ODB, "Failed to parse commit %s - %s", commit_oid, msg);
+	giterr_set(GITERR_ODB, "failed to parse commit %s - %s", commit_oid, msg);
 
 	return -1;
 }
@@ -191,7 +191,7 @@
 		return error;
 
 	if (obj->cached.type != GIT_OBJ_COMMIT) {
-		giterr_set(GITERR_INVALID, "Object is no commit object");
+		giterr_set(GITERR_INVALID, "object is no commit object");
 		error = -1;
 	} else
 		error = commit_quick_parse(
diff --git a/src/common.h b/src/common.h
index 51fb918..e566aea 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
@@ -187,7 +188,7 @@
 	if (actual > 0 && actual <= expected_max)
 		return 0;
 
-	giterr_set(GITERR_INVALID, "Invalid version %d on %s", actual, name);
+	giterr_set(GITERR_INVALID, "invalid version %d on %s", actual, name);
 	return -1;
 }
 #define GITERR_CHECK_VERSION(S,V,N) if (giterr__check_version(S,V,N) < 0) return -1
diff --git a/src/config.c b/src/config.c
index 403b709..0d73ad2 100644
--- a/src/config.c
+++ b/src/config.c
@@ -109,7 +109,7 @@
 
 	res = p_stat(path, &st);
 	if (res < 0 && errno != ENOENT) {
-		giterr_set(GITERR_CONFIG, "Error stat'ing config file '%s'", path);
+		giterr_set(GITERR_CONFIG, "failed to stat '%s'", path);
 		return -1;
 	}
 
@@ -203,7 +203,7 @@
 
 	if (pos == -1) {
 		giterr_set(GITERR_CONFIG,
-			"No config file exists for the given level '%i'", (int)level);
+			"no config file exists for the given level '%i'", (int)level);
 		return GIT_ENOTFOUND;
 	}
 
@@ -218,7 +218,7 @@
 
 	GIT_UNUSED(new_raw);
 
-	giterr_set(GITERR_CONFIG, "A file with the same level (%i) has already been added to the config", (int)(*old)->level);
+	giterr_set(GITERR_CONFIG, "a file with the same level (%i) has already been added to the config", (int)(*old)->level);
 	return GIT_EEXISTS;
 }
 
@@ -579,7 +579,7 @@
 static int config_error_nofiles(const char *name)
 {
 	giterr_set(GITERR_CONFIG,
-		"Cannot set value for '%s' when no config files exist", name);
+		"cannot set value for '%s' when no config files exist", name);
 	return GIT_ENOTFOUND;
 }
 
@@ -620,7 +620,7 @@
 	file_internal *internal;
 
 	if (!value) {
-		giterr_set(GITERR_CONFIG, "The value to set cannot be NULL");
+		giterr_set(GITERR_CONFIG, "the value to set cannot be NULL");
 		return -1;
 	}
 
@@ -674,7 +674,7 @@
 
 static int config_error_notfound(const char *name)
 {
-	giterr_set(GITERR_CONFIG, "Config value '%s' was not found", name);
+	giterr_set(GITERR_CONFIG, "config value '%s' was not found", name);
 	return GIT_ENOTFOUND;
 }
 
@@ -1236,7 +1236,7 @@
 	}
 
 fail_parse:
-	giterr_set(GITERR_CONFIG, "Failed to map '%s'", value);
+	giterr_set(GITERR_CONFIG, "failed to map '%s'", value);
 	return -1;
 }
 
@@ -1270,7 +1270,7 @@
 		return 0;
 	}
 
-	giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value);
+	giterr_set(GITERR_CONFIG, "failed to parse '%s' as a boolean value", value);
 	return -1;
 }
 
@@ -1313,7 +1313,7 @@
 	}
 
 fail_parse:
-	giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value ? value : "(null)");
+	giterr_set(GITERR_CONFIG, "failed to parse '%s' as an integer", value ? value : "(null)");
 	return -1;
 }
 
@@ -1333,7 +1333,7 @@
 	return 0;
 
 fail_parse:
-	giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value ? value : "(null)");
+	giterr_set(GITERR_CONFIG, "failed to parse '%s' as a 32-bit integer", value ? value : "(null)");
 	return -1;
 }
 
@@ -1398,7 +1398,7 @@
 
 invalid:
 	git__free(name);
-	giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in);
+	giterr_set(GITERR_CONFIG, "invalid config item name '%s'", in);
 	return GIT_EINVALIDSPEC;
 }
 
@@ -1461,7 +1461,7 @@
 			replace.ptr, strchr(replace.ptr, '.'))) < 0)
 	{
 		giterr_set(
-			GITERR_CONFIG, "Invalid config section '%s'", new_section_name);
+			GITERR_CONFIG, "invalid config section '%s'", new_section_name);
 		goto cleanup;
 	}
 
diff --git a/src/config_cache.c b/src/config_cache.c
index dbea871..8407222 100644
--- a/src/config_cache.c
+++ b/src/config_cache.c
@@ -78,6 +78,7 @@
 	{"core.logallrefupdates", NULL, 0, GIT_LOGALLREFUPDATES_DEFAULT },
 	{"core.protecthfs", NULL, 0, GIT_PROTECTHFS_DEFAULT },
 	{"core.protectntfs", NULL, 0, GIT_PROTECTNTFS_DEFAULT },
+	{"core.fsyncobjectfiles", NULL, 0, GIT_FSYNCOBJECTFILES_DEFAULT },
 };
 
 int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar)
diff --git a/src/config_file.c b/src/config_file.c
index 9ff021e..50c5a3d 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -21,8 +21,6 @@
 #include <sys/types.h>
 #include <regex.h>
 
-GIT__USE_STRMAP
-
 typedef struct cvar_t {
 	struct cvar_t *next;
 	git_config_entry *entry;
@@ -126,7 +124,7 @@
 
 static void set_parse_error(struct reader *reader, int col, const char *error_str)
 {
-	giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
+	giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%d, column %d)",
 		error_str, reader->file_path, reader->line_number, col);
 }
 
@@ -179,7 +177,7 @@
 
 	pos = git_strmap_lookup_index(values, var->entry->name);
 	if (!git_strmap_valid_index(values, pos)) {
-		git_strmap_insert(values, var->entry->name, var, error);
+		git_strmap_insert(values, var->entry->name, var, &error);
 	} else {
 		existing = git_strmap_value_at(values, pos);
 		while (existing->next != NULL) {
@@ -233,7 +231,7 @@
 	refcounted_strmap *map;
 
 	if (git_mutex_lock(&h->values_mutex) < 0) {
-	    giterr_set(GITERR_OS, "Failed to lock config backend");
+	    giterr_set(GITERR_OS, "failed to lock config backend");
 	    return NULL;
 	}
 
@@ -322,7 +320,7 @@
 		goto out;
 
 	if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) {
-		giterr_set(GITERR_OS, "Failed to lock config backend");
+		giterr_set(GITERR_OS, "failed to lock config backend");
 		goto out;
 	}
 
@@ -479,7 +477,7 @@
 		cvar_t *existing = git_strmap_value_at(values, pos);
 
 		if (existing->next != NULL) {
-			giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
+			giterr_set(GITERR_CONFIG, "multivar incompatible with simple set");
 			ret = -1;
 			goto out;
 		}
@@ -611,7 +609,7 @@
 
 	if (!git_strmap_valid_index(values, pos)) {
 		refcounted_strmap_free(map);
-		giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
+		giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name);
 		return GIT_ENOTFOUND;
 	}
 
@@ -619,7 +617,7 @@
 	refcounted_strmap_free(map);
 
 	if (var->next != NULL) {
-		giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
+		giterr_set(GITERR_CONFIG, "cannot delete multivar with a single delete");
 		return -1;
 	}
 
@@ -651,7 +649,7 @@
 	if (!git_strmap_valid_index(values, pos)) {
 		refcounted_strmap_free(map);
 		git__free(key);
-		giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
+		giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name);
 		return GIT_ENOTFOUND;
 	}
 
@@ -1043,8 +1041,9 @@
 	GITERR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len);
 	GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
 
-	git_buf_grow(&buf, alloc_len);
-	git_buf_printf(&buf, "%s.", base_name);
+	if (git_buf_grow(&buf, alloc_len) < 0 ||
+	    git_buf_printf(&buf, "%s.", base_name) < 0)
+		goto end_parse;
 
 	rpos = 0;
 
@@ -1084,6 +1083,11 @@
 	} while (line + rpos < last_quote);
 
 end_parse:
+	if (git_buf_oom(&buf)) {
+		git_buf_free(&buf);
+		return -1;
+	}
+
 	if (line[rpos] != '"' || line[rpos + 1] != ']') {
 		set_parse_error(reader, rpos, "Unexpected text after closing quotes");
 		git_buf_free(&buf);
@@ -1325,7 +1329,7 @@
 				*fixed++ = escaped[esc - escapes];
 			} else {
 				git__free(str);
-				giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);
+				giterr_set(GITERR_CONFIG, "invalid escape at %s", ptr);
 				return -1;
 			}
 		}
@@ -1639,7 +1643,7 @@
 	struct parse_data parse_data;
 
 	if (depth >= MAX_INCLUDE_DEPTH) {
-		giterr_set(GITERR_CONFIG, "Maximum config include depth reached");
+		giterr_set(GITERR_CONFIG, "maximum config include depth reached");
 		return -1;
 	}
 
diff --git a/src/crlf.c b/src/crlf.c
index 11895b1..b8ae5cd 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -218,7 +218,7 @@
 		return "\r\n";
 
 line_ending_error:
-	giterr_set(GITERR_INVALID, "Invalid input to line ending filter");
+	giterr_set(GITERR_INVALID, "invalid input to line ending filter");
 	return NULL;
 }
 
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/delta.c b/src/delta.c
index dc45697..073cba7 100644
--- a/src/delta.c
+++ b/src/delta.c
@@ -131,7 +131,7 @@
 	GITERR_CHECK_ALLOC_ADD(&index_len, index_len, hash_len);
 
 	if (!git__is_ulong(index_len)) {
-		giterr_set(GITERR_NOMEMORY, "Overly large delta");
+		giterr_set(GITERR_NOMEMORY, "overly large delta");
 		return -1;
 	}
 
@@ -544,12 +544,12 @@
 	* base object, resulting in data corruption or segfault.
 	*/
 	if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) {
-		giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
+		giterr_set(GITERR_INVALID, "failed to apply delta: base size does not match given data");
 		return -1;
 	}
 
 	if (hdr_sz(&res_sz, &delta, delta_end) < 0) {
-		giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
+		giterr_set(GITERR_INVALID, "failed to apply delta: base size does not match given data");
 		return -1;
 	}
 
@@ -614,6 +614,6 @@
 	*out = NULL;
 	*out_len = 0;
 
-	giterr_set(GITERR_INVALID, "Failed to apply delta");
+	giterr_set(GITERR_INVALID, "failed to apply delta");
 	return -1;
 }
diff --git a/src/describe.c b/src/describe.c
index fc48fbd..4a1e253 100644
--- a/src/describe.c
+++ b/src/describe.c
@@ -19,8 +19,6 @@
 #include "vector.h"
 #include "repository.h"
 
-GIT__USE_OIDMAP
-
 /* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */
 
 struct commit_name {
@@ -127,7 +125,7 @@
 		if (!found) {
 			int ret;
 
-			git_oidmap_insert(names, &e->peeled, e, ret);
+			git_oidmap_insert(names, &e->peeled, e, &ret);
 			if (ret < 0)
 				return -1;
 		}
@@ -335,14 +333,14 @@
 {
 	if (n->prio == 2 && !n->tag) {
 		if (git_tag_lookup(&n->tag, repo, &n->sha1) < 0) {
-			giterr_set(GITERR_TAG, "Annotated tag '%s' not available", n->path);
+			giterr_set(GITERR_TAG, "annotated tag '%s' not available", n->path);
 			return -1;
 		}
 	}
 
 	if (n->tag && !n->name_checked) {
 		if (!git_tag_name(n->tag)) {
-			giterr_set(GITERR_TAG, "Annotated tag '%s' has no embedded name", n->path);
+			giterr_set(GITERR_TAG, "annotated tag '%s' has no embedded name", n->path);
 			return -1;
 		}
 
@@ -471,7 +469,7 @@
 	if (!data->opts->max_candidates_tags) {
 		error = describe_not_found(
 			git_commit_id(commit),
-			"Cannot describe - no tag exactly matches '%s'");
+			"cannot describe - no tag exactly matches '%s'");
 
 		goto cleanup;
 	}
@@ -564,15 +562,15 @@
 		}
 		if (unannotated_cnt) {
 			error = describe_not_found(git_commit_id(commit), 
-				"Cannot describe - "
-				"No annotated tags can describe '%s'."
-			    "However, there were unannotated tags.");
+				"cannot describe - "
+				"no annotated tags can describe '%s'; "
+			    "however, there were unannotated tags.");
 			goto cleanup;
 		}
 		else {
 			error = describe_not_found(git_commit_id(commit), 
-				"Cannot describe - "
-				"No tags can describe '%s'.");
+				"cannot describe - "
+				"no tags can describe '%s'.");
 			goto cleanup;
 		}
 	}
@@ -695,8 +693,8 @@
 				goto cleanup;
 
 	if (git_oidmap_size(data.names) == 0 && !opts->show_commit_oid_as_fallback) {
-		giterr_set(GITERR_DESCRIBE, "Cannot describe - "
-			"No reference found, cannot describe anything.");
+		giterr_set(GITERR_DESCRIBE, "cannot describe - "
+			"no reference found, cannot describe anything.");
 		error = -1;
 		goto cleanup;
 	}
@@ -793,7 +791,7 @@
 
 
 	if (opts.always_use_long_format && opts.abbreviated_size == 0) {
-		giterr_set(GITERR_DESCRIBE, "Cannot describe - "
+		giterr_set(GITERR_DESCRIBE, "cannot describe - "
 			"'always_use_long_format' is incompatible with a zero"
 			"'abbreviated_size'");
 		return -1;
diff --git a/src/diff.c b/src/diff.c
index 317d495..a93bd4c 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -120,6 +120,41 @@
 	return 0;
 }
 
+int git_diff_foreach(
+	git_diff *diff,
+	git_diff_file_cb file_cb,
+	git_diff_binary_cb binary_cb,
+	git_diff_hunk_cb hunk_cb,
+	git_diff_line_cb data_cb,
+	void *payload)
+{
+	int error = 0;
+	git_diff_delta *delta;
+	size_t idx;
+
+	assert(diff);
+
+	git_vector_foreach(&diff->deltas, idx, delta) {
+		git_patch *patch;
+
+		/* check flags against patch status */
+		if (git_diff_delta__should_skip(&diff->opts, delta))
+			continue;
+
+		if ((error = git_patch_from_diff(&patch, diff, idx)) != 0)
+			break;
+
+		error = git_patch__invoke_callbacks(patch, file_cb, binary_cb,
+						    hunk_cb, data_cb, payload);
+		git_patch_free(patch);
+
+		if (error)
+			break;
+	}
+
+	return error;
+}
+
 int git_diff_format_email__append_header_tobuf(
 	git_buf *out,
 	const git_oid *id,
diff --git a/src/diff_driver.c b/src/diff_driver.c
index 1a7f09a..9109f31 100644
--- a/src/diff_driver.c
+++ b/src/diff_driver.c
@@ -16,8 +16,6 @@
 #include "config.h"
 #include "repository.h"
 
-GIT__USE_STRMAP
-
 typedef enum {
 	DIFF_DRIVER_AUTO = 0,
 	DIFF_DRIVER_BINARY = 1,
@@ -151,7 +149,7 @@
 	}
 
 	if (!repo->diff_drivers)
-		giterr_set(GITERR_REPOSITORY, "Unable to create diff driver registry");
+		giterr_set(GITERR_REPOSITORY, "unable to create diff driver registry");
 
 	return repo->diff_drivers;
 }
@@ -217,7 +215,7 @@
 		goto done;
 	}
 
-	git_strmap_insert(reg->drivers, drv->name, drv, error);
+	git_strmap_insert(reg->drivers, drv->name, drv, &error);
 	if (error > 0)
 		error = 0;
 
@@ -331,7 +329,7 @@
 		goto done;
 
 	/* store driver in registry */
-	git_strmap_insert(reg->drivers, drv->name, drv, error);
+	git_strmap_insert(reg->drivers, drv->name, drv, &error);
 	if (error < 0)
 		goto done;
 	error = 0;
diff --git a/src/diff_file.c b/src/diff_file.c
index cc10290..d5fc5e9 100644
--- a/src/diff_file.c
+++ b/src/diff_file.c
@@ -304,7 +304,7 @@
 
 	read_len = p_readlink(git_buf_cstr(path), fc->map.data, alloc_len);
 	if (read_len < 0) {
-		giterr_set(GITERR_OS, "Failed to read symlink '%s'", fc->file->path);
+		giterr_set(GITERR_OS, "failed to read symlink '%s'", fc->file->path);
 		return -1;
 	}
 
diff --git a/src/diff_generate.c b/src/diff_generate.c
index 06f9b19..f6cc04f 100644
--- a/src/diff_generate.c
+++ b/src/diff_generate.c
@@ -624,7 +624,7 @@
 		error = git_odb__hashlink(out, full_path.ptr);
 		diff->base.perf.oid_calculations++;
 	} else if (!git__is_sizet(entry.file_size)) {
-		giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'",
+		giterr_set(GITERR_OS, "file size overflow (for 32-bits) on '%s'",
 			entry.path);
 		error = -1;
 	} else if (!(error = git_filter_list_load(&fl,
@@ -1587,7 +1587,7 @@
 		char commit_oidstr[GIT_OID_HEXSZ + 1];
 
 		error = -1;
-		giterr_set(GITERR_INVALID, "Commit %s is a merge commit",
+		giterr_set(GITERR_INVALID, "commit %s is a merge commit",
 			git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit)));
 		goto on_error;
 	}
diff --git a/src/diff_parse.c b/src/diff_parse.c
index e640063..24a8a4a 100644
--- a/src/diff_parse.c
+++ b/src/diff_parse.c
@@ -37,7 +37,6 @@
 
 	GIT_REFCOUNT_INC(diff);
 	diff->base.type = GIT_DIFF_TYPE_PARSED;
-	diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE;
 	diff->base.strcomp = git__strcmp;
 	diff->base.strncomp = git__strncmp;
 	diff->base.pfxcomp = git__prefixcmp;
@@ -45,6 +44,13 @@
 	diff->base.patch_fn = git_patch_parsed_from_diff;
 	diff->base.free_fn = diff_parsed_free;
 
+	if (git_diff_init_options(&diff->base.opts, GIT_DIFF_OPTIONS_VERSION) < 0) {
+		git__free(&diff);
+		return NULL;
+	}
+
+	diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE;
+
 	git_pool_init(&diff->base.pool, 1);
 
 	if (git_vector_init(&diff->patches, 0, NULL) < 0 ||
diff --git a/src/diff_print.c b/src/diff_print.c
index fd1a186..5aa8a37 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -222,7 +222,7 @@
 
 	if (pi->id_strlen > id_abbrev) {
 		giterr_set(GITERR_PATCH,
-			"The patch input contains %d id characters (cannot print %d)",
+			"the patch input contains %d id characters (cannot print %d)",
 			id_abbrev, pi->id_strlen);
 		return -1;
 	}
@@ -273,7 +273,7 @@
 	if (delta->old_file.mode &&
 			id_strlen > delta->old_file.id_abbrev) {
 		giterr_set(GITERR_PATCH,
-			"The patch input contains %d id characters (cannot print %d)",
+			"the patch input contains %d id characters (cannot print %d)",
 			delta->old_file.id_abbrev, id_strlen);
 		return -1;
 	}
@@ -281,7 +281,7 @@
 	if ((delta->new_file.mode &&
 			id_strlen > delta->new_file.id_abbrev)) {
 		giterr_set(GITERR_PATCH,
-			"The patch input contains %d id characters (cannot print %d)",
+			"the patch input contains %d id characters (cannot print %d)",
 			delta->new_file.id_abbrev, id_strlen);
 		return -1;
 	}
@@ -680,7 +680,7 @@
 		print_file = diff_print_one_name_status;
 		break;
 	default:
-		giterr_set(GITERR_INVALID, "Unknown diff output format (%d)", format);
+		giterr_set(GITERR_INVALID, "unknown diff output format (%d)", format);
 		return -1;
 	}
 
@@ -708,7 +708,7 @@
 	GIT_UNUSED(delta); GIT_UNUSED(hunk);
 
 	if (!output) {
-		giterr_set(GITERR_INVALID, "Buffer pointer must be provided");
+		giterr_set(GITERR_INVALID, "buffer pointer must be provided");
 		return -1;
 	}
 
diff --git a/src/diff_stats.c b/src/diff_stats.c
index 9c186d7..2005712 100644
--- a/src/diff_stats.c
+++ b/src/diff_stats.c
@@ -300,15 +300,24 @@
 	}
 
 	if (format & GIT_DIFF_STATS_FULL || format & GIT_DIFF_STATS_SHORT) {
-		error = git_buf_printf(
-			out, " %" PRIuZ " file%s changed, %" PRIuZ
-			" insertion%s(+), %" PRIuZ " deletion%s(-)\n",
-			stats->files_changed, stats->files_changed != 1 ? "s" : "",
-			stats->insertions, stats->insertions != 1 ? "s" : "",
-			stats->deletions, stats->deletions != 1 ? "s" : "");
+		git_buf_printf(
+			out, " %" PRIuZ " file%s changed",
+			stats->files_changed, stats->files_changed != 1 ? "s" : "");
 
-		if (error < 0)
-			return error;
+		if (stats->insertions || stats->deletions == 0)
+			git_buf_printf(
+				out, ", %" PRIuZ " insertion%s(+)",
+				stats->insertions, stats->insertions != 1 ? "s" : "");
+
+		if (stats->deletions || stats->insertions == 0)
+			git_buf_printf(
+				out, ", %" PRIuZ " deletion%s(-)",
+				stats->deletions, stats->deletions != 1 ? "s" : "");
+
+		git_buf_putc(out, '\n');
+
+		if (git_buf_oom(out))
+			return -1;
 	}
 
 	if (format & GIT_DIFF_STATS_INCLUDE_SUMMARY) {
@@ -334,4 +343,3 @@
 	git__free(stats->filestats);
 	git__free(stats);
 }
-
diff --git a/src/diff_tform.c b/src/diff_tform.c
index e8848bd..b004ddd 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -131,7 +131,7 @@
 	if (ignore_case != ((from->opts.flags & GIT_DIFF_IGNORE_CASE) != 0) ||
 		reversed    != ((from->opts.flags & GIT_DIFF_REVERSE) != 0)) {
 		giterr_set(GITERR_INVALID,
-			"Attempt to merge diffs created with conflicting options");
+			"attempt to merge diffs created with conflicting options");
 		return -1;
 	}
 
@@ -553,8 +553,8 @@
 
 	*score = -1;
 
-	/* don't try to compare files of different types */
-	if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode))
+	/* don't try to compare things that aren't files */
+	if (!GIT_MODE_ISBLOB(a_file->mode) || !GIT_MODE_ISBLOB(b_file->mode))
 		return 0;
 
 	/* if exact match is requested, force calculation of missing OIDs now */
diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c
index 5bd6381..60c4d85 100644
--- a/src/diff_xdiff.c
+++ b/src/diff_xdiff.c
@@ -50,7 +50,7 @@
 	return 0;
 
 fail:
-	giterr_set(GITERR_INVALID, "Malformed hunk header from xdiff");
+	giterr_set(GITERR_INVALID, "malformed hunk header from xdiff");
 	return -1;
 }
 
@@ -99,7 +99,7 @@
 		info->new_lineno += (int)line->num_lines;
 		break;
 	default:
-		giterr_set(GITERR_INVALID, "Unknown diff line origin %02x",
+		giterr_set(GITERR_INVALID, "unknown diff line origin %02x",
 			(unsigned int)line->origin);
 		return -1;
 	}
diff --git a/src/fetch.c b/src/fetch.c
index 4d89575..f408a51 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -113,7 +113,7 @@
 	remote->need_pack = 0;
 
 	if (filter_wants(remote, opts) < 0) {
-		giterr_set(GITERR_NET, "Failed to filter the reference list for wants");
+		giterr_set(GITERR_NET, "failed to filter the reference list for wants");
 		return -1;
 	}
 
diff --git a/src/fetchhead.c b/src/fetchhead.c
index a95ea4c..6e6f3eb 100644
--- a/src/fetchhead.c
+++ b/src/fetchhead.c
@@ -115,7 +115,7 @@
 
 	assert(repo && fetchhead_refs);
 
-	if (git_buf_joinpath(&path, repo->path_repository, GIT_FETCH_HEAD_FILE) < 0)
+	if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0)
 		return -1;
 
 	if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) {
@@ -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,15 +163,15 @@
 
 	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;
 	}
 
 	if (git_oid_fromstr(oid, oid_str) < 0) {
 		const git_error *oid_err = giterr_last();
-		const char *err_msg = oid_err ? oid_err->message : "Invalid object ID";
+		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;
 			}
 
@@ -249,7 +249,7 @@
 
 	assert(repo && cb);
 
-	if (git_buf_joinpath(&path, repo->path_repository, GIT_FETCH_HEAD_FILE) < 0)
+	if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0)
 		return -1;
 
 	if ((error = git_futils_readbuffer(&file, git_buf_cstr(&path))) < 0)
@@ -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/filebuf.c b/src/filebuf.c
index 5823994..80250cc 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -23,7 +23,7 @@
 {
 	switch (file->last_error) {
 	case BUFERR_WRITE:
-		giterr_set(GITERR_OS, "Failed to write out file");
+		giterr_set(GITERR_OS, "failed to write out file");
 		return -1;
 
 	case BUFERR_MEM:
@@ -48,7 +48,7 @@
 		else {
 			giterr_clear(); /* actual OS error code just confuses */
 			giterr_set(GITERR_OS,
-				"Failed to lock file '%s' for writing", file->path_lock);
+				"failed to lock file '%s' for writing", file->path_lock);
 			return GIT_ELOCKED;
 		}
 	}
@@ -75,7 +75,7 @@
 		source = p_open(file->path_original, O_RDONLY);
 		if (source < 0) {
 			giterr_set(GITERR_OS,
-				"Failed to open file '%s' for reading",
+				"failed to open file '%s' for reading",
 				file->path_original);
 			return -1;
 		}
@@ -90,10 +90,10 @@
 		p_close(source);
 
 		if (read_bytes < 0) {
-			giterr_set(GITERR_OS, "Failed to read file '%s'", file->path_original);
+			giterr_set(GITERR_OS, "failed to read file '%s'", file->path_original);
 			return -1;
 		} else if (error < 0) {
-			giterr_set(GITERR_OS, "Failed to write file '%s'", file->path_lock);
+			giterr_set(GITERR_OS, "failed to write file '%s'", file->path_lock);
 			return -1;
 		}
 	}
@@ -246,7 +246,7 @@
 
 		root = git_path_root(target.ptr);
 		if (root >= 0) {
-			if ((error = git_buf_puts(&curpath, target.ptr)) < 0)
+			if ((error = git_buf_sets(&curpath, target.ptr)) < 0)
 				goto cleanup;
 		} else {
 			git_buf dir = GIT_BUF_INIT;
@@ -291,6 +291,9 @@
 	if (flags & GIT_FILEBUF_DO_NOT_BUFFER)
 		file->do_not_buffer = true;
 
+	if (flags & GIT_FILEBUF_FSYNC)
+		file->do_fsync = true;
+
 	file->buf_size = size;
 	file->buf_pos = 0;
 	file->fd = -1;
@@ -316,7 +319,7 @@
 	if (compression != 0) {
 		/* Initialize the ZLib stream */
 		if (deflateInit(&file->zs, compression) != Z_OK) {
-			giterr_set(GITERR_ZLIB, "Failed to initialize zlib");
+			giterr_set(GITERR_ZLIB, "failed to initialize zlib");
 			goto cleanup;
 		}
 
@@ -425,18 +428,26 @@
 
 	file->fd_is_open = false;
 
+	if (file->do_fsync && p_fsync(file->fd) < 0) {
+		giterr_set(GITERR_OS, "failed to fsync '%s'", file->path_lock);
+		goto on_error;
+	}
+
 	if (p_close(file->fd) < 0) {
-		giterr_set(GITERR_OS, "Failed to close file at '%s'", file->path_lock);
+		giterr_set(GITERR_OS, "failed to close file at '%s'", file->path_lock);
 		goto on_error;
 	}
 
 	file->fd = -1;
 
 	if (p_rename(file->path_lock, file->path_original) < 0) {
-		giterr_set(GITERR_OS, "Failed to rename lockfile to '%s'", file->path_original);
+		giterr_set(GITERR_OS, "failed to rename lockfile to '%s'", file->path_original);
 		goto on_error;
 	}
 
+	if (file->do_fsync && git_futils_fsync_parent(file->path_original) < 0)
+		goto on_error;
+
 	file->did_rename = true;
 
 	git_filebuf_cleanup(file);
@@ -571,7 +582,7 @@
 		res = p_stat(file->path_original, &st);
 
 	if (res < 0) {
-		giterr_set(GITERR_OS, "Could not get stat info for '%s'",
+		giterr_set(GITERR_OS, "could not get stat info for '%s'",
 			file->path_original);
 		return res;
 	}
diff --git a/src/filebuf.h b/src/filebuf.h
index 467708d..c65aea7 100644
--- a/src/filebuf.h
+++ b/src/filebuf.h
@@ -20,7 +20,8 @@
 #define GIT_FILEBUF_FORCE				(1 << 3)
 #define GIT_FILEBUF_TEMPORARY			(1 << 4)
 #define GIT_FILEBUF_DO_NOT_BUFFER		(1 << 5)
-#define GIT_FILEBUF_DEFLATE_SHIFT		(6)
+#define GIT_FILEBUF_FSYNC				(1 << 6)
+#define GIT_FILEBUF_DEFLATE_SHIFT		(7)
 
 #define GIT_FILELOCK_EXTENSION ".lock\0"
 #define GIT_FILELOCK_EXTLENGTH 6
@@ -47,6 +48,7 @@
 	bool created_lock;
 	bool did_rename;
 	bool do_not_buffer;
+	bool do_fsync;
 	int last_error;
 };
 
diff --git a/src/fileops.c b/src/fileops.c
index fcc0301..28f414f 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -13,8 +13,6 @@
 #include "win32/findfile.h"
 #endif
 
-GIT__USE_STRMAP
-
 int git_futils_mkpath2file(const char *file_path, const mode_t mode)
 {
 	return git_futils_mkdir(
@@ -37,13 +35,13 @@
 
 	if ((fd = p_mkstemp(path_out->ptr)) < 0) {
 		giterr_set(GITERR_OS,
-			"Failed to create temporary file '%s'", path_out->ptr);
+			"failed to create temporary file '%s'", path_out->ptr);
 		return -1;
 	}
 
 	if (p_chmod(path_out->ptr, (mode & ~mask))) {
 		giterr_set(GITERR_OS,
-			"Failed to set permissions on file '%s'", path_out->ptr);
+			"failed to set permissions on file '%s'", path_out->ptr);
 		return -1;
 	}
 
@@ -59,7 +57,7 @@
 
 	fd = p_creat(path, mode);
 	if (fd < 0) {
-		giterr_set(GITERR_OS, "Failed to create file '%s'", path);
+		giterr_set(GITERR_OS, "failed to create file '%s'", path);
 		return -1;
 	}
 
@@ -72,8 +70,16 @@
 		O_EXCL | O_BINARY | O_CLOEXEC, mode);
 
 	if (fd < 0) {
-		giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
-		return errno == EEXIST ? GIT_ELOCKED : -1;
+		int error = errno;
+		giterr_set(GITERR_OS, "failed to create locked file '%s'", path);
+		switch (error) {
+		case EEXIST:
+			return GIT_ELOCKED;
+		case ENOENT:
+			return GIT_ENOTFOUND;
+		default:
+			return -1;
+		}
 	}
 
 	return fd;
@@ -100,7 +106,7 @@
 	struct stat sb;
 
 	if (p_fstat(fd, &sb)) {
-		giterr_set(GITERR_OS, "Failed to stat file descriptor");
+		giterr_set(GITERR_OS, "failed to stat file descriptor");
 		return -1;
 	}
 
@@ -129,7 +135,7 @@
 	git_buf_clear(buf);
 
 	if (!git__is_ssizet(len)) {
-		giterr_set(GITERR_INVALID, "Read too large.");
+		giterr_set(GITERR_INVALID, "read too large");
 		return -1;
 	}
 
@@ -141,7 +147,7 @@
 	read_size = p_read(fd, buf->ptr, len);
 
 	if (read_size != (ssize_t)len) {
-		giterr_set(GITERR_OS, "Failed to read descriptor");
+		giterr_set(GITERR_OS, "failed to read descriptor");
 		git_buf_free(buf);
 		return -1;
 	}
@@ -176,7 +182,7 @@
 	}
 
 	if (!git__is_sizet(st.st_size+1)) {
-		giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
+		giterr_set(GITERR_OS, "invalid regular file stat for '%s'", path);
 		return -1;
 	}
 
@@ -190,28 +196,29 @@
 
 	p_close(fd);
 
-	if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) {
-		git_buf_free(&buf);
-		return error;
-	}
+	if (checksum) {
+		if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) {
+			git_buf_free(&buf);
+			return error;
+		}
 
-	/*
-	 * If we were given a checksum, we only want to use it if it's different
-	 */
-	if (checksum && !git_oid__cmp(checksum, &checksum_new)) {
-		git_buf_free(&buf);
-		if (updated)
-			*updated = 0;
+		/*
+		 * If we were given a checksum, we only want to use it if it's different
+		 */
+		if (!git_oid__cmp(checksum, &checksum_new)) {
+			git_buf_free(&buf);
+			if (updated)
+				*updated = 0;
 
-		return 0;
+			return 0;
+		}
+
+		git_oid_cpy(checksum, &checksum_new);
 	}
 
 	/*
 	 * If we're here, the file did change, or the user didn't have an old version
 	 */
-	if (checksum)
-		git_oid_cpy(checksum, &checksum_new);
-
 	if (updated != NULL)
 		*updated = 1;
 
@@ -229,26 +236,43 @@
 int git_futils_writebuffer(
 	const git_buf *buf,	const char *path, int flags, mode_t mode)
 {
-	int fd, error = 0;
+	int fd, do_fsync = 0, error = 0;
 
-	if (flags <= 0)
+	if (!flags)
 		flags = O_CREAT | O_TRUNC | O_WRONLY;
+
+	if ((flags & O_FSYNC) != 0)
+		do_fsync = 1;
+
+	flags &= ~O_FSYNC;
+
 	if (!mode)
 		mode = GIT_FILEMODE_BLOB;
 
 	if ((fd = p_open(path, flags, mode)) < 0) {
-		giterr_set(GITERR_OS, "Could not open '%s' for writing", path);
+		giterr_set(GITERR_OS, "could not open '%s' for writing", path);
 		return fd;
 	}
 
 	if ((error = p_write(fd, git_buf_cstr(buf), git_buf_len(buf))) < 0) {
-		giterr_set(GITERR_OS, "Could not write to '%s'", path);
+		giterr_set(GITERR_OS, "could not write to '%s'", path);
 		(void)p_close(fd);
 		return error;
 	}
 
-	if ((error = p_close(fd)) < 0)
-		giterr_set(GITERR_OS, "Error while closing '%s'", path);
+	if (do_fsync && (error = p_fsync(fd)) < 0) {
+		giterr_set(GITERR_OS, "could not fsync '%s'", path);
+		p_close(fd);
+		return error;
+	}
+
+	if ((error = p_close(fd)) < 0) {
+		giterr_set(GITERR_OS, "error while closing '%s'", path);
+		return error;
+	}
+
+	if (do_fsync && (flags & O_CREAT))
+		error = git_futils_fsync_parent(path);
 
 	return error;
 }
@@ -259,7 +283,7 @@
 		return -1;
 
 	if (p_rename(from, to) < 0) {
-		giterr_set(GITERR_OS, "Failed to rename '%s' to '%s'", from, to);
+		giterr_set(GITERR_OS, "failed to rename '%s' to '%s'", from, to);
 		return -1;
 	}
 
@@ -282,7 +306,7 @@
 
 	len = git_futils_filesize(fd);
 	if (!git__is_sizet(len)) {
-		giterr_set(GITERR_OS, "File `%s` too large to mmap", path);
+		giterr_set(GITERR_OS, "file `%s` too large to mmap", path);
 		return -1;
 	}
 
@@ -306,14 +330,14 @@
 	/* with exclusive create, existing dir is an error */
 	if ((flags & GIT_MKDIR_EXCL) != 0) {
 		giterr_set(GITERR_FILESYSTEM,
-			"Failed to make directory '%s': directory exists", path);
+			"failed to make directory '%s': directory exists", path);
 		return GIT_EEXISTS;
 	}
 
 	if ((S_ISREG(st->st_mode) && (flags & GIT_MKDIR_REMOVE_FILES)) ||
 		(S_ISLNK(st->st_mode) && (flags & GIT_MKDIR_REMOVE_SYMLINKS))) {
 		if (p_unlink(path) < 0) {
-			giterr_set(GITERR_OS, "Failed to remove %s '%s'",
+			giterr_set(GITERR_OS, "failed to remove %s '%s'",
 				S_ISLNK(st->st_mode) ? "symlink" : "file", path);
 			return GIT_EEXISTS;
 		}
@@ -321,7 +345,7 @@
 		opts->perfdata.mkdir_calls++;
 
 		if (p_mkdir(path, mode) < 0) {
-			giterr_set(GITERR_OS, "Failed to make directory '%s'", path);
+			giterr_set(GITERR_OS, "failed to make directory '%s'", path);
 			return GIT_EEXISTS;
 		}
 	}
@@ -331,14 +355,14 @@
 		opts->perfdata.stat_calls++;
 
 		if (p_stat(path, st) < 0) {
-			giterr_set(GITERR_OS, "Failed to make directory '%s'", path);
+			giterr_set(GITERR_OS, "failed to make directory '%s'", path);
 			return GIT_EEXISTS;
 		}
 	}
 
 	else if (!S_ISDIR(st->st_mode)) {
 		giterr_set(GITERR_FILESYSTEM,
-			"Failed to make directory '%s': directory exists", path);
+			"failed to make directory '%s': directory exists", path);
 		return GIT_EEXISTS;
 	}
 
@@ -561,7 +585,7 @@
 retry_lstat:
 		if (p_lstat(make_path.ptr, &st) < 0) {
 			if (mkdir_attempted || errno != ENOENT) {
-				giterr_set(GITERR_OS, "Cannot access component in path '%s'", make_path.ptr);
+				giterr_set(GITERR_OS, "cannot access component in path '%s'", make_path.ptr);
 				error = -1;
 				goto done;
 			}
@@ -572,7 +596,7 @@
 			if (p_mkdir(make_path.ptr, mode) < 0) {
 				if (errno == EEXIST)
 					goto retry_lstat;
-				giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
+				giterr_set(GITERR_OS, "failed to make directory '%s'", make_path.ptr);
 				error = -1;
 				goto done;
 			}
@@ -599,7 +623,7 @@
 
 			memcpy(cache_path, make_path.ptr, make_path.size + 1);
 
-			git_strmap_insert(opts->dir_map, cache_path, cache_path, error);
+			git_strmap_insert(opts->dir_map, cache_path, cache_path, &error);
 			if (error < 0)
 				goto done;
 		}
@@ -613,7 +637,7 @@
 		opts->perfdata.stat_calls++;
 
 		if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
-			giterr_set(GITERR_OS, "Path is not a directory '%s'",
+			giterr_set(GITERR_OS, "path is not a directory '%s'",
 				make_path.ptr);
 			error = GIT_ENOTFOUND;
 		}
@@ -636,10 +660,10 @@
 static int futils__error_cannot_rmdir(const char *path, const char *filemsg)
 {
 	if (filemsg)
-		giterr_set(GITERR_OS, "Could not remove directory. File '%s' %s",
+		giterr_set(GITERR_OS, "could not remove directory '%s': %s",
 				   path, filemsg);
 	else
-		giterr_set(GITERR_OS, "Could not remove directory '%s'", path);
+		giterr_set(GITERR_OS, "could not remove directory '%s'", path);
 
 	return -1;
 }
@@ -807,7 +831,7 @@
 		error = p_write(ofd, buffer, len);
 
 	if (len < 0) {
-		giterr_set(GITERR_OS, "Read error while copying file");
+		giterr_set(GITERR_OS, "read error while copying file");
 		error = (int)len;
 	}
 
@@ -863,14 +887,14 @@
 
 	read_len = p_readlink(from, link_data, link_size);
 	if (read_len != (ssize_t)link_size) {
-		giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", from);
+		giterr_set(GITERR_OS, "failed to read symlink data for '%s'", from);
 		error = -1;
 	}
 	else {
 		link_data[read_len] = '\0';
 
 		if (p_symlink(link_data, to) < 0) {
-			giterr_set(GITERR_OS, "Could not symlink '%s' as '%s'",
+			giterr_set(GITERR_OS, "could not symlink '%s' as '%s'",
 				link_data, to);
 			error = -1;
 		}
@@ -966,7 +990,7 @@
 			return 0;
 
 		if (p_unlink(info->to.ptr) < 0) {
-			giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'",
+			giterr_set(GITERR_OS, "cannot overwrite existing file '%s'",
 				info->to.ptr);
 			return GIT_EEXISTS;
 		}
@@ -1101,3 +1125,33 @@
 		memset(stamp, 0, sizeof(*stamp));
 	}
 }
+
+int git_futils_fsync_dir(const char *path)
+{
+#ifdef GIT_WIN32
+	GIT_UNUSED(path);
+	return 0;
+#else
+	int fd, error = -1;
+
+	if ((fd = p_open(path, O_RDONLY)) < 0) {
+		giterr_set(GITERR_OS, "failed to open directory '%s' for fsync", path);
+		return -1;
+	}
+
+	if ((error = p_fsync(fd)) < 0)
+		giterr_set(GITERR_OS, "failed to fsync directory '%s'", path);
+
+	p_close(fd);
+	return error;
+#endif
+}
+
+int git_futils_fsync_parent(const char *path)
+{
+	char *parent = git_path_dirname(path);
+	int error = git_futils_fsync_dir(parent);
+
+	git__free(parent);
+	return error;
+}
diff --git a/src/fileops.h b/src/fileops.h
index 54e3bd4..46886b0 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -25,6 +25,13 @@
 	git_buf *obj, const char *path, git_oid *checksum, int *updated);
 extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len);
 
+/* Additional constants for `git_futils_writebuffer`'s `open_flags`.  We
+ * support these internally and they will be removed before the `open` call.
+ */
+#ifndef O_FSYNC
+# define O_FSYNC (1 << 31)
+#endif
+
 extern int git_futils_writebuffer(
 	const git_buf *buf, const char *path, int open_flags, mode_t mode);
 
@@ -45,12 +52,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);
@@ -356,4 +363,22 @@
 extern void git_futils_filestamp_set_from_stat(
 	git_futils_filestamp *stamp, struct stat *st);
 
+/**
+ * `fsync` the parent directory of the given path, if `fsync` is
+ * supported for directories on this platform.
+ *
+ * @param path Path of the directory to sync.
+ * @return 0 on success, -1 on error
+ */
+extern int git_futils_fsync_dir(const char *path);
+
+/**
+ * `fsync` the parent directory of the given path, if `fsync` is
+ * supported for directories on this platform.
+ *
+ * @param path Path of the file whose parent directory should be synced.
+ * @return 0 on success, -1 on error
+ */
+extern int git_futils_fsync_parent(const char *path);
+
 #endif /* INCLUDE_fileops_h__ */
diff --git a/src/filter.c b/src/filter.c
index a0628d7..0d8831e 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -296,7 +296,7 @@
 
 	/* cannot unregister default filters */
 	if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) {
-		giterr_set(GITERR_FILTER, "Cannot unregister filter '%s'", name);
+		giterr_set(GITERR_FILTER, "cannot unregister filter '%s'", name);
 		return -1;
 	}
 
@@ -306,7 +306,7 @@
 	}
 
 	if ((fdef = filter_registry_lookup(&pos, name)) == NULL) {
-		giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name);
+		giterr_set(GITERR_FILTER, "cannot find filter '%s' to unregister", name);
 		error = GIT_ENOTFOUND;
 		goto done;
 	}
@@ -645,7 +645,7 @@
 	git_rwlock_rdunlock(&filter_registry.lock);
 
 	if (fdef == NULL) {
-		giterr_set(GITERR_FILTER, "Cannot use an unregistered filter");
+		giterr_set(GITERR_FILTER, "cannot use an unregistered filter");
 		return -1;
 	}
 
@@ -758,7 +758,7 @@
 	git_off_t rawsize = git_blob_rawsize(blob);
 
 	if (!git__is_sizet(rawsize)) {
-		giterr_set(GITERR_OS, "Blob is too large to filter");
+		giterr_set(GITERR_OS, "blob is too large to filter");
 		return -1;
 	}
 
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/hash.h b/src/hash.h
index 0bc02a8..0db0339 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -16,11 +16,13 @@
 int git_hash_ctx_init(git_hash_ctx *ctx);
 void git_hash_ctx_cleanup(git_hash_ctx *ctx);
 
-#if defined(GIT_COMMON_CRYPTO)
+#if defined(GIT_SHA1_COLLISIONDETECT)
+# include "hash/hash_collisiondetect.h"
+#elif defined(GIT_SHA1_COMMON_CRYPTO)
 # include "hash/hash_common_crypto.h"
-#elif defined(OPENSSL_SHA1)
+#elif defined(GIT_SHA1_OPENSSL)
 # include "hash/hash_openssl.h"
-#elif defined(WIN32_SHA1)
+#elif defined(GIT_SHA1_WIN32)
 # include "hash/hash_win32.h"
 #else
 # include "hash/hash_generic.h"
diff --git a/src/hash/hash_collisiondetect.h b/src/hash/hash_collisiondetect.h
new file mode 100644
index 0000000..2bb27ba
--- /dev/null
+++ b/src/hash/hash_collisiondetect.h
@@ -0,0 +1,57 @@
+/*
+ * 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_hash_collisiondetect_h__
+#define INCLUDE_hash_collisiondetect_h__
+
+#include "hash.h"
+#include "sha1dc/sha1.h"
+
+struct git_hash_ctx {
+	SHA1_CTX c;
+};
+
+#define git_hash_global_init() 0
+#define git_hash_ctx_init(ctx) git_hash_init(ctx)
+#define git_hash_ctx_cleanup(ctx)
+
+GIT_INLINE(int) git_hash_init(git_hash_ctx *ctx)
+{
+	assert(ctx);
+	SHA1DCInit(&ctx->c);
+	return 0;
+}
+
+GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
+{
+    const char *p = data;
+
+	assert(ctx);
+
+	/* We expect a size_t, but sha1dc only takes an int */
+	while (len > INT_MAX) {
+		SHA1DCUpdate(&ctx->c, p, INT_MAX);
+		p += INT_MAX;
+		len -= INT_MAX;
+	}
+
+	SHA1DCUpdate(&ctx->c, p, len);
+	return 0;
+}
+
+GIT_INLINE(int) git_hash_final(git_oid *out, git_hash_ctx *ctx)
+{
+	assert(ctx);
+	if (SHA1DCFinal(out->id, &ctx->c)) {
+		giterr_set(GITERR_SHA1, "SHA1 collision attack detected");
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif /* INCLUDE_hash_collisiondetect_h__ */
diff --git a/src/hash/sha1dc/sha1.c b/src/hash/sha1dc/sha1.c
new file mode 100644
index 0000000..c846a16
--- /dev/null
+++ b/src/hash/sha1dc/sha1.c
@@ -0,0 +1,1149 @@
+/***
+* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow (danshu@microsoft.com) 
+* Distributed under the MIT Software License.
+* See accompanying file LICENSE.txt or copy at
+* https://opensource.org/licenses/MIT
+***/
+
+#include <string.h>
+#include <memory.h>
+#include <stdio.h>
+
+#include "sha1.h"
+#include "ubc_check.h"
+
+#define rotate_right(x,n) (((x)>>(n))|((x)<<(32-(n))))
+#define rotate_left(x,n)  (((x)<<(n))|((x)>>(32-(n))))
+
+#define sha1_f1(b,c,d) ((d)^((b)&((c)^(d))))
+#define sha1_f2(b,c,d) ((b)^(c)^(d))
+#define sha1_f3(b,c,d) (((b) & ((c)|(d))) | ((c)&(d)))
+#define sha1_f4(b,c,d) ((b)^(c)^(d))
+
+#define HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, m, t) \
+	{ e += rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; b = rotate_left(b, 30); }
+#define HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, m, t) \
+	{ e += rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; b = rotate_left(b, 30); }
+#define HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, m, t) \
+	{ e += rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; b = rotate_left(b, 30); }
+#define HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, m, t) \
+	{ e += rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; b = rotate_left(b, 30); }
+
+#define HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, m, t) \
+	{ b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; }
+#define HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, m, t) \
+	{ b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; }
+#define HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, m, t) \
+	{ b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; }
+#define HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, m, t) \
+	{ b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; }
+
+#define SHA1_STORE_STATE(i) states[i][0] = a; states[i][1] = b; states[i][2] = c; states[i][3] = d; states[i][4] = e;
+
+
+
+void sha1_message_expansion(uint32_t W[80])
+{
+	unsigned i;
+
+	for (i = 16; i < 80; ++i)
+		W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
+}
+
+void sha1_compression(uint32_t ihv[5], const uint32_t m[16])
+{
+	uint32_t a, b, c, d, e, W[80];
+	unsigned i;
+
+	memcpy(W, m, 16 * 4);
+	for (i = 16; i < 80; ++i)
+		W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
+
+	a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4];
+
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19);
+
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39);
+
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59);
+
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79);
+
+	ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e;
+}
+
+
+
+void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80])
+{
+	uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4];
+
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18);
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19);
+
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32);
+ 	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38);
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39);
+
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58);
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59);
+
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78);
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79);
+
+	ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e;
+}
+
+
+
+void sha1_compression_states(uint32_t ihv[5], const uint32_t W[80], uint32_t states[80][5])
+{
+	uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4];
+
+#ifdef DOSTORESTATE00
+	SHA1_STORE_STATE(0)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0);
+
+#ifdef DOSTORESTATE01
+	SHA1_STORE_STATE(1)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1);
+
+#ifdef DOSTORESTATE02
+	SHA1_STORE_STATE(2)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2);
+
+#ifdef DOSTORESTATE03
+	SHA1_STORE_STATE(3)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3);
+
+#ifdef DOSTORESTATE04
+	SHA1_STORE_STATE(4)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4);
+
+#ifdef DOSTORESTATE05
+	SHA1_STORE_STATE(5)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5);
+
+#ifdef DOSTORESTATE06
+	SHA1_STORE_STATE(6)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6);
+
+#ifdef DOSTORESTATE07
+	SHA1_STORE_STATE(7)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7);
+
+#ifdef DOSTORESTATE08
+	SHA1_STORE_STATE(8)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8);
+
+#ifdef DOSTORESTATE09
+	SHA1_STORE_STATE(9)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9);
+
+#ifdef DOSTORESTATE10
+	SHA1_STORE_STATE(10)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10);
+
+#ifdef DOSTORESTATE11
+	SHA1_STORE_STATE(11)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11);
+
+#ifdef DOSTORESTATE12
+	SHA1_STORE_STATE(12)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12);
+
+#ifdef DOSTORESTATE13
+	SHA1_STORE_STATE(13)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13);
+
+#ifdef DOSTORESTATE14
+	SHA1_STORE_STATE(14)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14);
+
+#ifdef DOSTORESTATE15
+	SHA1_STORE_STATE(15)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15);
+
+#ifdef DOSTORESTATE16
+	SHA1_STORE_STATE(16)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16);
+
+#ifdef DOSTORESTATE17
+	SHA1_STORE_STATE(17)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17);
+
+#ifdef DOSTORESTATE18
+	SHA1_STORE_STATE(18)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18);
+
+#ifdef DOSTORESTATE19
+	SHA1_STORE_STATE(19)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19);
+
+
+
+#ifdef DOSTORESTATE20
+	SHA1_STORE_STATE(20)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20);
+
+#ifdef DOSTORESTATE21
+	SHA1_STORE_STATE(21)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21);
+	
+#ifdef DOSTORESTATE22
+	SHA1_STORE_STATE(22)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22);
+	
+#ifdef DOSTORESTATE23
+	SHA1_STORE_STATE(23)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23);
+
+#ifdef DOSTORESTATE24
+	SHA1_STORE_STATE(24)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24);
+
+#ifdef DOSTORESTATE25
+	SHA1_STORE_STATE(25)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25);
+
+#ifdef DOSTORESTATE26
+	SHA1_STORE_STATE(26)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26);
+
+#ifdef DOSTORESTATE27
+	SHA1_STORE_STATE(27)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27);
+	
+#ifdef DOSTORESTATE28
+	SHA1_STORE_STATE(28)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28);
+	
+#ifdef DOSTORESTATE29
+	SHA1_STORE_STATE(29)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29);
+	
+#ifdef DOSTORESTATE30
+	SHA1_STORE_STATE(30)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30);
+	
+#ifdef DOSTORESTATE31
+	SHA1_STORE_STATE(31)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31);
+	
+#ifdef DOSTORESTATE32
+	SHA1_STORE_STATE(32)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32);
+
+#ifdef DOSTORESTATE33
+	SHA1_STORE_STATE(33)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33);
+
+#ifdef DOSTORESTATE34
+	SHA1_STORE_STATE(34)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34);
+
+#ifdef DOSTORESTATE35
+	SHA1_STORE_STATE(35)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35);
+	
+#ifdef DOSTORESTATE36
+	SHA1_STORE_STATE(36)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36);
+	
+#ifdef DOSTORESTATE37
+	SHA1_STORE_STATE(37)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37);
+	
+#ifdef DOSTORESTATE38
+	SHA1_STORE_STATE(38)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38);
+	
+#ifdef DOSTORESTATE39
+	SHA1_STORE_STATE(39)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39);
+
+
+
+#ifdef DOSTORESTATE40
+	SHA1_STORE_STATE(40)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40);
+
+#ifdef DOSTORESTATE41
+	SHA1_STORE_STATE(41)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41);
+
+#ifdef DOSTORESTATE42
+	SHA1_STORE_STATE(42)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42);
+
+#ifdef DOSTORESTATE43
+	SHA1_STORE_STATE(43)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43);
+
+#ifdef DOSTORESTATE44
+	SHA1_STORE_STATE(44)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44);
+
+#ifdef DOSTORESTATE45
+	SHA1_STORE_STATE(45)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45);
+
+#ifdef DOSTORESTATE46
+	SHA1_STORE_STATE(46)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46);
+
+#ifdef DOSTORESTATE47
+	SHA1_STORE_STATE(47)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47);
+
+#ifdef DOSTORESTATE48
+	SHA1_STORE_STATE(48)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48);
+
+#ifdef DOSTORESTATE49
+	SHA1_STORE_STATE(49)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49);
+
+#ifdef DOSTORESTATE50
+	SHA1_STORE_STATE(50)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50);
+
+#ifdef DOSTORESTATE51
+	SHA1_STORE_STATE(51)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51);
+
+#ifdef DOSTORESTATE52
+	SHA1_STORE_STATE(52)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52);
+
+#ifdef DOSTORESTATE53
+	SHA1_STORE_STATE(53)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53);
+
+#ifdef DOSTORESTATE54
+	SHA1_STORE_STATE(54)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54);
+
+#ifdef DOSTORESTATE55
+	SHA1_STORE_STATE(55)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55);
+
+#ifdef DOSTORESTATE56
+	SHA1_STORE_STATE(56)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56);
+
+#ifdef DOSTORESTATE57
+	SHA1_STORE_STATE(57)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57);
+
+#ifdef DOSTORESTATE58
+	SHA1_STORE_STATE(58)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58);
+
+#ifdef DOSTORESTATE59
+	SHA1_STORE_STATE(59)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59);
+	
+
+
+
+#ifdef DOSTORESTATE60
+	SHA1_STORE_STATE(60)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60);
+
+#ifdef DOSTORESTATE61
+	SHA1_STORE_STATE(61)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61);
+
+#ifdef DOSTORESTATE62
+	SHA1_STORE_STATE(62)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62);
+
+#ifdef DOSTORESTATE63
+	SHA1_STORE_STATE(63)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63);
+
+#ifdef DOSTORESTATE64
+	SHA1_STORE_STATE(64)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64);
+
+#ifdef DOSTORESTATE65
+	SHA1_STORE_STATE(65)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65);
+
+#ifdef DOSTORESTATE66
+	SHA1_STORE_STATE(66)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66);
+
+#ifdef DOSTORESTATE67
+	SHA1_STORE_STATE(67)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67);
+
+#ifdef DOSTORESTATE68
+	SHA1_STORE_STATE(68)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68);
+
+#ifdef DOSTORESTATE69
+	SHA1_STORE_STATE(69)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69);
+
+#ifdef DOSTORESTATE70
+	SHA1_STORE_STATE(70)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70);
+
+#ifdef DOSTORESTATE71
+	SHA1_STORE_STATE(71)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71);
+
+#ifdef DOSTORESTATE72
+	SHA1_STORE_STATE(72)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72);
+
+#ifdef DOSTORESTATE73
+	SHA1_STORE_STATE(73)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73);
+
+#ifdef DOSTORESTATE74
+	SHA1_STORE_STATE(74)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74);
+
+#ifdef DOSTORESTATE75
+	SHA1_STORE_STATE(75)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75);
+
+#ifdef DOSTORESTATE76
+	SHA1_STORE_STATE(76)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76);
+
+#ifdef DOSTORESTATE77
+	SHA1_STORE_STATE(77)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77);
+
+#ifdef DOSTORESTATE78
+	SHA1_STORE_STATE(78)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78);
+
+#ifdef DOSTORESTATE79
+	SHA1_STORE_STATE(79)
+#endif
+	HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79);
+
+
+
+	ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e;
+}
+
+
+
+
+#define SHA1_RECOMPRESS(t) \
+void sha1recompress_fast_ ## t (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]) \
+{ \
+	uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4]; \
+	if (t > 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 79); \
+	if (t > 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 78); \
+	if (t > 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 77); \
+	if (t > 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 76); \
+	if (t > 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 75); \
+	if (t > 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 74); \
+	if (t > 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 73); \
+	if (t > 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 72); \
+	if (t > 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 71); \
+	if (t > 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 70); \
+	if (t > 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 69); \
+	if (t > 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 68); \
+	if (t > 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 67); \
+	if (t > 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 66); \
+	if (t > 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 65); \
+	if (t > 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 64); \
+	if (t > 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 63); \
+	if (t > 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 62); \
+	if (t > 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 61); \
+	if (t > 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 60); \
+	if (t > 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 59); \
+	if (t > 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 58); \
+	if (t > 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 57); \
+	if (t > 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 56); \
+	if (t > 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 55); \
+	if (t > 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 54); \
+	if (t > 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 53); \
+	if (t > 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 52); \
+	if (t > 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 51); \
+	if (t > 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 50); \
+	if (t > 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 49); \
+	if (t > 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 48); \
+	if (t > 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 47); \
+	if (t > 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 46); \
+	if (t > 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 45); \
+	if (t > 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 44); \
+	if (t > 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 43); \
+	if (t > 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 42); \
+	if (t > 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 41); \
+	if (t > 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 40); \
+	if (t > 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 39); \
+	if (t > 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 38); \
+	if (t > 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 37); \
+	if (t > 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 36); \
+	if (t > 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 35); \
+	if (t > 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 34); \
+	if (t > 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 33); \
+	if (t > 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 32); \
+	if (t > 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 31); \
+	if (t > 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 30); \
+	if (t > 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 29); \
+	if (t > 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 28); \
+	if (t > 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 27); \
+	if (t > 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 26); \
+	if (t > 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 25); \
+	if (t > 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 24); \
+	if (t > 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 23); \
+	if (t > 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 22); \
+	if (t > 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 21); \
+	if (t > 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 20); \
+	if (t > 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 19); \
+	if (t > 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 18); \
+	if (t > 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 17); \
+	if (t > 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 16); \
+	if (t > 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 15); \
+	if (t > 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 14); \
+	if (t > 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 13); \
+	if (t > 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 12); \
+	if (t > 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 11); \
+	if (t > 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 10); \
+	if (t > 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 9); \
+	if (t > 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 8); \
+	if (t > 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 7); \
+	if (t > 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 6); \
+	if (t > 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 5); \
+	if (t > 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 4); \
+	if (t > 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 3); \
+	if (t > 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 2); \
+	if (t > 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 1); \
+	if (t > 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 0); \
+	ihvin[0] = a; ihvin[1] = b; ihvin[2] = c; ihvin[3] = d; ihvin[4] = e; \
+	a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; \
+	if (t <= 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 0); \
+	if (t <= 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 1); \
+	if (t <= 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 2); \
+	if (t <= 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 3); \
+	if (t <= 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 4); \
+	if (t <= 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 5); \
+	if (t <= 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 6); \
+	if (t <= 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 7); \
+	if (t <= 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 8); \
+	if (t <= 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 9); \
+	if (t <= 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 10); \
+	if (t <= 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 11); \
+	if (t <= 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 12); \
+	if (t <= 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 13); \
+	if (t <= 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 14); \
+	if (t <= 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 15); \
+	if (t <= 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 16); \
+	if (t <= 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 17); \
+	if (t <= 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 18); \
+	if (t <= 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 19); \
+	if (t <= 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 20); \
+	if (t <= 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 21); \
+	if (t <= 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 22); \
+	if (t <= 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 23); \
+	if (t <= 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 24); \
+	if (t <= 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 25); \
+	if (t <= 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 26); \
+	if (t <= 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 27); \
+	if (t <= 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 28); \
+	if (t <= 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 29); \
+	if (t <= 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 30); \
+	if (t <= 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 31); \
+	if (t <= 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 32); \
+	if (t <= 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 33); \
+	if (t <= 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 34); \
+	if (t <= 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 35); \
+	if (t <= 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 36); \
+	if (t <= 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 37); \
+	if (t <= 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 38); \
+	if (t <= 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 39); \
+	if (t <= 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 40); \
+	if (t <= 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 41); \
+	if (t <= 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 42); \
+	if (t <= 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 43); \
+	if (t <= 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 44); \
+	if (t <= 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 45); \
+	if (t <= 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 46); \
+	if (t <= 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 47); \
+	if (t <= 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 48); \
+	if (t <= 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 49); \
+	if (t <= 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 50); \
+	if (t <= 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 51); \
+	if (t <= 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 52); \
+	if (t <= 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 53); \
+	if (t <= 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 54); \
+	if (t <= 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 55); \
+	if (t <= 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 56); \
+	if (t <= 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 57); \
+	if (t <= 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 58); \
+	if (t <= 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 59); \
+	if (t <= 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 60); \
+	if (t <= 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 61); \
+	if (t <= 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 62); \
+	if (t <= 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 63); \
+	if (t <= 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 64); \
+	if (t <= 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 65); \
+	if (t <= 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 66); \
+	if (t <= 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 67); \
+	if (t <= 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 68); \
+	if (t <= 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 69); \
+	if (t <= 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 70); \
+	if (t <= 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 71); \
+	if (t <= 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 72); \
+	if (t <= 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 73); \
+	if (t <= 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 74); \
+	if (t <= 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 75); \
+	if (t <= 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 76); \
+	if (t <= 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 77); \
+	if (t <= 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 78); \
+	if (t <= 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 79); \
+	ihvout[0] = ihvin[0] + a; ihvout[1] = ihvin[1] + b; ihvout[2] = ihvin[2] + c; ihvout[3] = ihvin[3] + d; ihvout[4] = ihvin[4] + e; \
+} 
+
+SHA1_RECOMPRESS(0)
+SHA1_RECOMPRESS(1)
+SHA1_RECOMPRESS(2)
+SHA1_RECOMPRESS(3)
+SHA1_RECOMPRESS(4)
+SHA1_RECOMPRESS(5)
+SHA1_RECOMPRESS(6)
+SHA1_RECOMPRESS(7)
+SHA1_RECOMPRESS(8)
+SHA1_RECOMPRESS(9)
+
+SHA1_RECOMPRESS(10)
+SHA1_RECOMPRESS(11)
+SHA1_RECOMPRESS(12)
+SHA1_RECOMPRESS(13)
+SHA1_RECOMPRESS(14)
+SHA1_RECOMPRESS(15)
+SHA1_RECOMPRESS(16)
+SHA1_RECOMPRESS(17)
+SHA1_RECOMPRESS(18)
+SHA1_RECOMPRESS(19)
+
+SHA1_RECOMPRESS(20)
+SHA1_RECOMPRESS(21)
+SHA1_RECOMPRESS(22)
+SHA1_RECOMPRESS(23)
+SHA1_RECOMPRESS(24)
+SHA1_RECOMPRESS(25)
+SHA1_RECOMPRESS(26)
+SHA1_RECOMPRESS(27)
+SHA1_RECOMPRESS(28)
+SHA1_RECOMPRESS(29)
+
+SHA1_RECOMPRESS(30)
+SHA1_RECOMPRESS(31)
+SHA1_RECOMPRESS(32)
+SHA1_RECOMPRESS(33)
+SHA1_RECOMPRESS(34)
+SHA1_RECOMPRESS(35)
+SHA1_RECOMPRESS(36)
+SHA1_RECOMPRESS(37)
+SHA1_RECOMPRESS(38)
+SHA1_RECOMPRESS(39)
+
+SHA1_RECOMPRESS(40)
+SHA1_RECOMPRESS(41)
+SHA1_RECOMPRESS(42)
+SHA1_RECOMPRESS(43)
+SHA1_RECOMPRESS(44)
+SHA1_RECOMPRESS(45)
+SHA1_RECOMPRESS(46)
+SHA1_RECOMPRESS(47)
+SHA1_RECOMPRESS(48)
+SHA1_RECOMPRESS(49)
+
+SHA1_RECOMPRESS(50)
+SHA1_RECOMPRESS(51)
+SHA1_RECOMPRESS(52)
+SHA1_RECOMPRESS(53)
+SHA1_RECOMPRESS(54)
+SHA1_RECOMPRESS(55)
+SHA1_RECOMPRESS(56)
+SHA1_RECOMPRESS(57)
+SHA1_RECOMPRESS(58)
+SHA1_RECOMPRESS(59)
+
+SHA1_RECOMPRESS(60)
+SHA1_RECOMPRESS(61)
+SHA1_RECOMPRESS(62)
+SHA1_RECOMPRESS(63)
+SHA1_RECOMPRESS(64)
+SHA1_RECOMPRESS(65)
+SHA1_RECOMPRESS(66)
+SHA1_RECOMPRESS(67)
+SHA1_RECOMPRESS(68)
+SHA1_RECOMPRESS(69)
+
+SHA1_RECOMPRESS(70)
+SHA1_RECOMPRESS(71)
+SHA1_RECOMPRESS(72)
+SHA1_RECOMPRESS(73)
+SHA1_RECOMPRESS(74)
+SHA1_RECOMPRESS(75)
+SHA1_RECOMPRESS(76)
+SHA1_RECOMPRESS(77)
+SHA1_RECOMPRESS(78)
+SHA1_RECOMPRESS(79)
+
+sha1_recompression_type sha1_recompression_step[80] =
+{
+	sha1recompress_fast_0, sha1recompress_fast_1, sha1recompress_fast_2, sha1recompress_fast_3, sha1recompress_fast_4, sha1recompress_fast_5, sha1recompress_fast_6, sha1recompress_fast_7, sha1recompress_fast_8, sha1recompress_fast_9,
+	sha1recompress_fast_10, sha1recompress_fast_11, sha1recompress_fast_12, sha1recompress_fast_13, sha1recompress_fast_14, sha1recompress_fast_15, sha1recompress_fast_16, sha1recompress_fast_17, sha1recompress_fast_18, sha1recompress_fast_19,
+	sha1recompress_fast_20, sha1recompress_fast_21, sha1recompress_fast_22, sha1recompress_fast_23, sha1recompress_fast_24, sha1recompress_fast_25, sha1recompress_fast_26, sha1recompress_fast_27, sha1recompress_fast_28, sha1recompress_fast_29,
+	sha1recompress_fast_30, sha1recompress_fast_31, sha1recompress_fast_32, sha1recompress_fast_33, sha1recompress_fast_34, sha1recompress_fast_35, sha1recompress_fast_36, sha1recompress_fast_37, sha1recompress_fast_38, sha1recompress_fast_39,
+	sha1recompress_fast_40, sha1recompress_fast_41, sha1recompress_fast_42, sha1recompress_fast_43, sha1recompress_fast_44, sha1recompress_fast_45, sha1recompress_fast_46, sha1recompress_fast_47, sha1recompress_fast_48, sha1recompress_fast_49,
+	sha1recompress_fast_50, sha1recompress_fast_51, sha1recompress_fast_52, sha1recompress_fast_53, sha1recompress_fast_54, sha1recompress_fast_55, sha1recompress_fast_56, sha1recompress_fast_57, sha1recompress_fast_58, sha1recompress_fast_59,
+	sha1recompress_fast_60, sha1recompress_fast_61, sha1recompress_fast_62, sha1recompress_fast_63, sha1recompress_fast_64, sha1recompress_fast_65, sha1recompress_fast_66, sha1recompress_fast_67, sha1recompress_fast_68, sha1recompress_fast_69,
+	sha1recompress_fast_70, sha1recompress_fast_71, sha1recompress_fast_72, sha1recompress_fast_73, sha1recompress_fast_74, sha1recompress_fast_75, sha1recompress_fast_76, sha1recompress_fast_77, sha1recompress_fast_78, sha1recompress_fast_79,
+};
+
+
+
+
+
+void sha1_process(SHA1_CTX* ctx, const uint32_t block[16]) 
+{
+	unsigned i, j;
+	uint32_t ubc_dv_mask[DVMASKSIZE];
+	uint32_t ihvtmp[5];
+	for (i=0; i < DVMASKSIZE; ++i)
+		ubc_dv_mask[i]=0;
+	ctx->ihv1[0] = ctx->ihv[0];
+	ctx->ihv1[1] = ctx->ihv[1];
+	ctx->ihv1[2] = ctx->ihv[2];
+	ctx->ihv1[3] = ctx->ihv[3];
+	ctx->ihv1[4] = ctx->ihv[4];
+	memcpy(ctx->m1, block, 64);
+	sha1_message_expansion(ctx->m1);
+	if (ctx->detect_coll && ctx->ubc_check)
+	{
+		ubc_check(ctx->m1, ubc_dv_mask);
+	}
+	sha1_compression_states(ctx->ihv, ctx->m1, ctx->states);
+	if (ctx->detect_coll)
+	{
+		for (i = 0; sha1_dvs[i].dvType != 0; ++i) 
+		{
+			if ((0 == ctx->ubc_check) || (((uint32_t)(1) << sha1_dvs[i].maskb) & ubc_dv_mask[sha1_dvs[i].maski]))
+			{
+				for (j = 0; j < 80; ++j)
+					ctx->m2[j] = ctx->m1[j] ^ sha1_dvs[i].dm[j];
+				(sha1_recompression_step[sha1_dvs[i].testt])(ctx->ihv2, ihvtmp, ctx->m2, ctx->states[sha1_dvs[i].testt]);
+				// to verify SHA-1 collision detection code with collisions for reduced-step SHA-1
+				if ((ihvtmp[0] == ctx->ihv[0] && ihvtmp[1] == ctx->ihv[1] && ihvtmp[2] == ctx->ihv[2] && ihvtmp[3] == ctx->ihv[3] && ihvtmp[4] == ctx->ihv[4])
+					|| (ctx->reduced_round_coll && ctx->ihv1[0] == ctx->ihv2[0] && ctx->ihv1[1] == ctx->ihv2[1] && ctx->ihv1[2] == ctx->ihv2[2] && ctx->ihv1[3] == ctx->ihv2[3] && ctx->ihv1[4] == ctx->ihv2[4]))
+				{
+					ctx->found_collision = 1;
+					// TODO: call callback
+					if (ctx->callback != NULL)
+						ctx->callback(ctx->total - 64, ctx->ihv1, ctx->ihv2, ctx->m1, ctx->m2);
+
+					if (ctx->safe_hash) 
+					{
+						sha1_compression_W(ctx->ihv, ctx->m1);
+						sha1_compression_W(ctx->ihv, ctx->m1);
+					}
+
+					break;
+				}
+			}
+		}
+	}
+}
+
+
+
+
+
+void swap_bytes(uint32_t val[16]) 
+{
+	unsigned i;
+	for (i = 0; i < 16; ++i) 
+	{
+		val[i] = ((val[i] << 8) & 0xFF00FF00) | ((val[i] >> 8) & 0xFF00FF);
+		val[i] = (val[i] << 16) | (val[i] >> 16);
+	}
+}
+
+void SHA1DCInit(SHA1_CTX* ctx) 
+{
+	static const union { unsigned char bytes[4]; uint32_t value; } endianness = { { 0, 1, 2, 3 } };
+	static const uint32_t littleendian = 0x03020100;
+	ctx->total = 0;
+	ctx->ihv[0] = 0x67452301;
+	ctx->ihv[1] = 0xEFCDAB89;
+	ctx->ihv[2] = 0x98BADCFE;
+	ctx->ihv[3] = 0x10325476;
+	ctx->ihv[4] = 0xC3D2E1F0;
+	ctx->found_collision = 0;
+	ctx->safe_hash = 1;
+	ctx->ubc_check = 1;
+	ctx->detect_coll = 1;
+	ctx->reduced_round_coll = 0;
+	ctx->bigendian = (endianness.value != littleendian);
+	ctx->callback = NULL;
+}
+
+void SHA1DCSetSafeHash(SHA1_CTX* ctx, int safehash)
+{
+	if (safehash)
+		ctx->safe_hash = 1;
+	else
+		ctx->safe_hash = 0;
+}
+
+
+void SHA1DCSetUseUBC(SHA1_CTX* ctx, int ubc_check)
+{
+	if (ubc_check)
+		ctx->ubc_check = 1;
+	else
+		ctx->ubc_check = 0;
+}
+
+void SHA1DCSetUseDetectColl(SHA1_CTX* ctx, int detect_coll)
+{
+	if (detect_coll)
+		ctx->detect_coll = 1;
+	else
+		ctx->detect_coll = 0;
+}
+
+void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX* ctx, int reduced_round_coll)
+{
+	if (reduced_round_coll)
+		ctx->reduced_round_coll = 1;
+	else
+		ctx->reduced_round_coll = 0;
+}
+
+void SHA1DCSetCallback(SHA1_CTX* ctx, collision_block_callback callback)
+{
+	ctx->callback = callback;
+}
+
+void SHA1DCUpdate(SHA1_CTX* ctx, const char* buf, unsigned len) 
+{
+	unsigned left, fill;
+	if (len == 0) 
+		return;
+
+	left = ctx->total & 63;
+	fill = 64 - left;
+
+	if (left && len >= fill) 
+	{
+		ctx->total += fill;
+		memcpy(ctx->buffer + left, buf, fill);
+		if (!ctx->bigendian)
+			swap_bytes((uint32_t*)(ctx->buffer));
+		sha1_process(ctx, (uint32_t*)(ctx->buffer));
+		buf += fill;
+		len -= fill;
+		left = 0;
+	}
+	while (len >= 64) 
+	{
+		ctx->total += 64;
+		if (!ctx->bigendian) 
+		{
+			memcpy(ctx->buffer, buf, 64);
+			swap_bytes((uint32_t*)(ctx->buffer));
+			sha1_process(ctx, (uint32_t*)(ctx->buffer));
+		}
+		else
+			sha1_process(ctx, (uint32_t*)(buf));
+		buf += 64;
+		len -= 64;
+	}
+	if (len > 0) 
+	{
+		ctx->total += len;
+		memcpy(ctx->buffer + left, buf, len);
+	}
+}
+
+static const unsigned char sha1_padding[64] =
+{
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+int SHA1DCFinal(unsigned char output[20], SHA1_CTX *ctx)
+{
+	uint32_t last = ctx->total & 63;
+	uint32_t padn = (last < 56) ? (56 - last) : (120 - last);
+	uint64_t total;
+	SHA1DCUpdate(ctx, (const char*)(sha1_padding), padn);
+	
+	total = ctx->total - padn;
+	total <<= 3;
+	ctx->buffer[56] = (unsigned char)(total >> 56);
+	ctx->buffer[57] = (unsigned char)(total >> 48);
+	ctx->buffer[58] = (unsigned char)(total >> 40);
+	ctx->buffer[59] = (unsigned char)(total >> 32);
+	ctx->buffer[60] = (unsigned char)(total >> 24);
+	ctx->buffer[61] = (unsigned char)(total >> 16);
+	ctx->buffer[62] = (unsigned char)(total >> 8);
+	ctx->buffer[63] = (unsigned char)(total);
+	if (!ctx->bigendian)
+		swap_bytes((uint32_t*)(ctx->buffer));
+	sha1_process(ctx, (uint32_t*)(ctx->buffer));
+	output[0] = (unsigned char)(ctx->ihv[0] >> 24);
+	output[1] = (unsigned char)(ctx->ihv[0] >> 16);
+	output[2] = (unsigned char)(ctx->ihv[0] >> 8);
+	output[3] = (unsigned char)(ctx->ihv[0]);
+	output[4] = (unsigned char)(ctx->ihv[1] >> 24);
+	output[5] = (unsigned char)(ctx->ihv[1] >> 16);
+	output[6] = (unsigned char)(ctx->ihv[1] >> 8);
+	output[7] = (unsigned char)(ctx->ihv[1]);
+	output[8] = (unsigned char)(ctx->ihv[2] >> 24);
+	output[9] = (unsigned char)(ctx->ihv[2] >> 16);
+	output[10] = (unsigned char)(ctx->ihv[2] >> 8);
+	output[11] = (unsigned char)(ctx->ihv[2]);
+	output[12] = (unsigned char)(ctx->ihv[3] >> 24);
+	output[13] = (unsigned char)(ctx->ihv[3] >> 16);
+	output[14] = (unsigned char)(ctx->ihv[3] >> 8);
+	output[15] = (unsigned char)(ctx->ihv[3]);
+	output[16] = (unsigned char)(ctx->ihv[4] >> 24);
+	output[17] = (unsigned char)(ctx->ihv[4] >> 16);
+	output[18] = (unsigned char)(ctx->ihv[4] >> 8);
+	output[19] = (unsigned char)(ctx->ihv[4]);
+	return ctx->found_collision;
+}
diff --git a/src/hash/sha1dc/sha1.h b/src/hash/sha1dc/sha1.h
new file mode 100644
index 0000000..8b522f9
--- /dev/null
+++ b/src/hash/sha1dc/sha1.h
@@ -0,0 +1,94 @@
+/***
+* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
+* Distributed under the MIT Software License.
+* See accompanying file LICENSE.txt or copy at
+* https://opensource.org/licenses/MIT
+***/
+
+#include <stdint.h>
+
+// uses SHA-1 message expansion to expand the first 16 words of W[] to 80 words
+void sha1_message_expansion(uint32_t W[80]);
+
+// sha-1 compression function; first version takes a message block pre-parsed as 16 32-bit integers, second version takes an already expanded message)
+void sha1_compression(uint32_t ihv[5], const uint32_t m[16]);
+void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]);
+
+// same as sha1_compression_W, but additionally store intermediate states
+// only stores states ii (the state between step ii-1 and step ii) when DOSTORESTATEii is defined in ubc_check.h
+void sha1_compression_states(uint32_t ihv[5], const uint32_t W[80], uint32_t states[80][5]);
+
+// function type for sha1_recompression_step_T (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5])
+// where 0 <= T < 80
+//       me2 is an expanded message (the expansion of an original message block XOR'ed with a disturbance vector's message block difference)
+//       state is the internal state (a,b,c,d,e) before step T of the SHA-1 compression function while processing the original message block
+// the function will return:
+//       ihvin: the reconstructed input chaining value
+//       ihvout: the reconstructed output chaining value
+typedef void(*sha1_recompression_type)(uint32_t*, uint32_t*, const uint32_t*, const uint32_t*);
+
+// table of sha1_recompression_step_0, ... , sha1_recompression_step_79
+extern sha1_recompression_type sha1_recompression_step[80];
+
+// a callback function type that can be set to be called when a collision block has been found:
+// void collision_block_callback(uint64_t byteoffset, const uint32_t ihvin1[5], const uint32_t ihvin2[5], const uint32_t m1[80], const uint32_t m2[80])
+typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*);
+
+// the SHA-1 context
+typedef struct {
+	uint64_t total;
+	uint32_t ihv[5];
+	unsigned char buffer[64];
+	int bigendian;
+	int found_collision;
+	int safe_hash;
+	int detect_coll;
+	int ubc_check;
+	int reduced_round_coll;
+	collision_block_callback callback;
+
+	uint32_t ihv1[5];
+	uint32_t ihv2[5];
+	uint32_t m1[80];
+	uint32_t m2[80];
+	uint32_t states[80][5];
+} SHA1_CTX;
+
+// initialize SHA-1 context
+void SHA1DCInit(SHA1_CTX*); 
+
+// function to enable safe SHA-1 hashing:
+// collision attacks are thwarted by hashing a detected near-collision block 3 times
+// think of it as extending SHA-1 from 80-steps to 240-steps for such blocks:
+//   the best collision attacks against SHA-1 have complexity about 2^60, 
+//   thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would 2^180
+//   an attacker would be better off using a generic birthday search of complexity 2^80
+//
+// enabling safe SHA-1 hashing will result in the correct SHA-1 hash for messages where no collision attack was detected
+// but it will result in a different SHA-1 hash for messages where a collision attack was detected 
+// this will automatically invalidate SHA-1 based digital signature forgeries
+// enabled by default
+void SHA1DCSetSafeHash(SHA1_CTX*, int);
+
+// function to disable or enable the use of Unavoidable Bitconditions (provides a significant speed up)
+// enabled by default
+void SHA1DCSetUseUBC(SHA1_CTX*, int);
+
+// function to disable or enable the use of Collision Detection
+// enabled by default
+void SHA1DCSetUseDetectColl(SHA1_CTX* ctx, int detect_coll);
+
+// function to disable or enable the detection of reduced-round SHA-1 collisions
+// disabled by default
+void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX*, int);
+
+// function to set a callback function, pass NULL to disable
+// by default no callback set
+void SHA1DCSetCallback(SHA1_CTX*, collision_block_callback);
+
+// update SHA-1 context with buffer contents
+void SHA1DCUpdate(SHA1_CTX*, const char*, unsigned);
+
+// obtain SHA-1 hash from SHA-1 context
+// returns: 0 = no collision detected, otherwise = collision found => warn user for active attack
+int  SHA1DCFinal(unsigned char[20], SHA1_CTX*); 
diff --git a/src/hash/sha1dc/ubc_check.c b/src/hash/sha1dc/ubc_check.c
new file mode 100644
index 0000000..556aaf3
--- /dev/null
+++ b/src/hash/sha1dc/ubc_check.c
@@ -0,0 +1,361 @@
+/***
+* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
+* Distributed under the MIT Software License.
+* See accompanying file LICENSE.txt or copy at
+* https://opensource.org/licenses/MIT
+***/
+
+// this file was generated by the 'parse_bitrel' program in the tools section
+// using the data files from directory 'tools/data/3565'
+//
+// sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check
+// dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper)
+// dm[80] is the expanded message block XOR-difference defined by the DV
+// testt is the step to do the recompression from for collision detection
+// maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check
+//
+// ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs
+// it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met
+// thus one needs to do the recompression check for each DV that has its bit set
+// 
+// ubc_check is programmatically generated and the unavoidable bitconditions have been hardcoded
+// a directly verifiable version named ubc_check_verify can be found in ubc_check_verify.c
+// ubc_check has been verified against ubc_check_verify using the 'ubc_check_test' program in the tools section
+
+#include <stdint.h>
+#include "ubc_check.h"
+
+static const uint32_t DV_I_43_0_bit 	= (uint32_t)(1) << 0;
+static const uint32_t DV_I_44_0_bit 	= (uint32_t)(1) << 1;
+static const uint32_t DV_I_45_0_bit 	= (uint32_t)(1) << 2;
+static const uint32_t DV_I_46_0_bit 	= (uint32_t)(1) << 3;
+static const uint32_t DV_I_46_2_bit 	= (uint32_t)(1) << 4;
+static const uint32_t DV_I_47_0_bit 	= (uint32_t)(1) << 5;
+static const uint32_t DV_I_47_2_bit 	= (uint32_t)(1) << 6;
+static const uint32_t DV_I_48_0_bit 	= (uint32_t)(1) << 7;
+static const uint32_t DV_I_48_2_bit 	= (uint32_t)(1) << 8;
+static const uint32_t DV_I_49_0_bit 	= (uint32_t)(1) << 9;
+static const uint32_t DV_I_49_2_bit 	= (uint32_t)(1) << 10;
+static const uint32_t DV_I_50_0_bit 	= (uint32_t)(1) << 11;
+static const uint32_t DV_I_50_2_bit 	= (uint32_t)(1) << 12;
+static const uint32_t DV_I_51_0_bit 	= (uint32_t)(1) << 13;
+static const uint32_t DV_I_51_2_bit 	= (uint32_t)(1) << 14;
+static const uint32_t DV_I_52_0_bit 	= (uint32_t)(1) << 15;
+static const uint32_t DV_II_45_0_bit 	= (uint32_t)(1) << 16;
+static const uint32_t DV_II_46_0_bit 	= (uint32_t)(1) << 17;
+static const uint32_t DV_II_46_2_bit 	= (uint32_t)(1) << 18;
+static const uint32_t DV_II_47_0_bit 	= (uint32_t)(1) << 19;
+static const uint32_t DV_II_48_0_bit 	= (uint32_t)(1) << 20;
+static const uint32_t DV_II_49_0_bit 	= (uint32_t)(1) << 21;
+static const uint32_t DV_II_49_2_bit 	= (uint32_t)(1) << 22;
+static const uint32_t DV_II_50_0_bit 	= (uint32_t)(1) << 23;
+static const uint32_t DV_II_50_2_bit 	= (uint32_t)(1) << 24;
+static const uint32_t DV_II_51_0_bit 	= (uint32_t)(1) << 25;
+static const uint32_t DV_II_51_2_bit 	= (uint32_t)(1) << 26;
+static const uint32_t DV_II_52_0_bit 	= (uint32_t)(1) << 27;
+static const uint32_t DV_II_53_0_bit 	= (uint32_t)(1) << 28;
+static const uint32_t DV_II_54_0_bit 	= (uint32_t)(1) << 29;
+static const uint32_t DV_II_55_0_bit 	= (uint32_t)(1) << 30;
+static const uint32_t DV_II_56_0_bit 	= (uint32_t)(1) << 31;
+
+dv_info_t sha1_dvs[] = 
+{
+  {1,43,0,58,0,0, { 0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161,0x80000599 } }
+, {1,44,0,58,0,1, { 0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161 } }
+, {1,45,0,58,0,2, { 0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803 } }
+, {1,46,0,58,0,3, { 0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c } }
+, {1,46,2,58,0,4, { 0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a,0x00000132 } }
+, {1,47,0,58,0,5, { 0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6 } }
+, {1,47,2,58,0,6, { 0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a } }
+, {1,48,0,58,0,7, { 0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408 } }
+, {1,48,2,58,0,8, { 0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020 } }
+, {1,49,0,58,0,9, { 0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164 } }
+, {1,49,2,58,0,10, { 0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590 } }
+, {1,50,0,65,0,11, { 0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018 } }
+, {1,50,2,65,0,12, { 0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060 } }
+, {1,51,0,65,0,13, { 0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202 } }
+, {1,51,2,65,0,14, { 0xa0000003,0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a } }
+, {1,52,0,65,0,15, { 0x04000010,0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012 } }
+, {2,45,0,58,0,16, { 0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054,0x00000967 } }
+, {2,46,0,58,0,17, { 0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054 } }
+, {2,46,2,58,0,18, { 0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6,0x0000106a,0x00000b90,0x00000152 } }
+, {2,47,0,58,0,19, { 0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4 } }
+, {2,48,0,58,0,20, { 0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a } }
+, {2,49,0,58,0,21, { 0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d } }
+, {2,49,2,58,0,22, { 0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6 } }
+, {2,50,0,65,0,23, { 0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b } }
+, {2,50,2,65,0,24, { 0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c } }
+, {2,51,0,65,0,25, { 0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b } }
+, {2,51,2,65,0,26, { 0x00000043,0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e } }
+, {2,52,0,65,0,27, { 0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014 } }
+, {2,53,0,65,0,28, { 0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089 } }
+, {2,54,0,65,0,29, { 0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107 } }
+, {2,55,0,65,0,30, { 0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b } }
+, {2,56,0,65,0,31, { 0x2600001a,0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046 } }
+, {0,0,0,0,0,0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
+};
+void ubc_check(const uint32_t W[80], uint32_t dvmask[1])
+{
+	uint32_t mask = ~((uint32_t)(0));
+	mask &= (((((W[44]^W[45])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_I_51_0_bit|DV_I_52_0_bit|DV_II_45_0_bit|DV_II_46_0_bit|DV_II_50_0_bit|DV_II_51_0_bit));
+	mask &= (((((W[49]^W[50])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_II_45_0_bit|DV_II_50_0_bit|DV_II_51_0_bit|DV_II_55_0_bit|DV_II_56_0_bit));
+	mask &= (((((W[48]^W[49])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_52_0_bit|DV_II_49_0_bit|DV_II_50_0_bit|DV_II_54_0_bit|DV_II_55_0_bit));
+	mask &= ((((W[47]^(W[50]>>25))&(1<<4))-(1<<4)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_51_0_bit|DV_II_56_0_bit));
+	mask &= (((((W[47]^W[48])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_51_0_bit|DV_II_48_0_bit|DV_II_49_0_bit|DV_II_53_0_bit|DV_II_54_0_bit));
+	mask &= (((((W[46]>>4)^(W[49]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_50_0_bit|DV_II_55_0_bit));
+	mask &= (((((W[46]^W[47])>>29)&1)-1) | ~(DV_I_43_0_bit|DV_I_50_0_bit|DV_II_47_0_bit|DV_II_48_0_bit|DV_II_52_0_bit|DV_II_53_0_bit));
+	mask &= (((((W[45]>>4)^(W[48]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_49_0_bit|DV_II_54_0_bit));
+	mask &= (((((W[45]^W[46])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_51_0_bit|DV_II_52_0_bit));
+	mask &= (((((W[44]>>4)^(W[47]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_53_0_bit));
+	mask &= (((((W[43]>>4)^(W[46]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_52_0_bit));
+	mask &= (((((W[43]^W[44])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_I_50_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_49_0_bit|DV_II_50_0_bit));
+	mask &= (((((W[42]>>4)^(W[45]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_51_0_bit));
+	mask &= (((((W[41]>>4)^(W[44]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_50_0_bit));
+	mask &= (((((W[40]^W[41])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_47_0_bit|DV_I_48_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_56_0_bit));
+	mask &= (((((W[54]^W[55])>>29)&1)-1) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_50_0_bit|DV_II_55_0_bit|DV_II_56_0_bit));
+	mask &= (((((W[53]^W[54])>>29)&1)-1) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_49_0_bit|DV_II_54_0_bit|DV_II_55_0_bit));
+	mask &= (((((W[52]^W[53])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit|DV_II_53_0_bit|DV_II_54_0_bit));
+	mask &= ((((W[50]^(W[53]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_48_0_bit|DV_II_54_0_bit));
+	mask &= (((((W[50]^W[51])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_II_46_0_bit|DV_II_51_0_bit|DV_II_52_0_bit|DV_II_56_0_bit));
+	mask &= ((((W[49]^(W[52]>>25))&(1<<4))-(1<<4)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_47_0_bit|DV_II_53_0_bit));
+	mask &= ((((W[48]^(W[51]>>25))&(1<<4))-(1<<4)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_52_0_bit));
+	mask &= (((((W[42]^W[43])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_49_0_bit));
+	mask &= (((((W[41]^W[42])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_48_0_bit));
+	mask &= (((((W[40]>>4)^(W[43]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_50_0_bit|DV_II_49_0_bit|DV_II_56_0_bit));
+	mask &= (((((W[39]>>4)^(W[42]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_49_0_bit|DV_II_48_0_bit|DV_II_55_0_bit));
+	if (mask & (DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit))
+		mask &= (((((W[38]>>4)^(W[41]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit));
+	mask &= (((((W[37]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_47_0_bit|DV_II_46_0_bit|DV_II_53_0_bit|DV_II_55_0_bit));
+	if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit))
+		mask &= (((((W[55]^W[56])>>29)&1)-1) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit));
+	if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit))
+		mask &= ((((W[52]^(W[55]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit));
+	if (mask & (DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit))
+		mask &= ((((W[51]^(W[54]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit));
+	if (mask & (DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit))
+		mask &= (((((W[51]^W[52])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit));
+	if (mask & (DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit))
+		mask &= (((((W[36]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit));
+	if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit))
+		mask &= ((0-(((W[53]^W[56])>>29)&1)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit));
+	if (mask & (DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit))
+		mask &= ((0-(((W[51]^W[54])>>29)&1)) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit));
+	if (mask & (DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit))
+		mask &= ((0-(((W[50]^W[52])>>29)&1)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit));
+	if (mask & (DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit))
+		mask &= ((0-(((W[49]^W[51])>>29)&1)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit));
+	if (mask & (DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit))
+		mask &= ((0-(((W[48]^W[50])>>29)&1)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit));
+	if (mask & (DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit))
+		mask &= ((0-(((W[47]^W[49])>>29)&1)) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit));
+	if (mask & (DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit))
+		mask &= ((0-(((W[46]^W[48])>>29)&1)) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit));
+	mask &= ((((W[45]^W[47])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit|DV_I_51_2_bit));
+	if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit))
+		mask &= ((0-(((W[45]^W[47])>>29)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit));
+	mask &= (((((W[44]^W[46])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit|DV_I_50_2_bit));
+	if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit))
+		mask &= ((0-(((W[44]^W[46])>>29)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit));
+	mask &= ((0-((W[41]^(W[42]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_II_46_2_bit|DV_II_51_2_bit));
+	mask &= ((0-((W[40]^(W[41]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_51_2_bit|DV_II_50_2_bit));
+	if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit))
+		mask &= ((0-(((W[40]^W[42])>>4)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit));
+	mask &= ((0-((W[39]^(W[40]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_50_2_bit|DV_II_49_2_bit));
+	if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit))
+		mask &= ((0-(((W[39]^W[41])>>4)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit));
+	if (mask & (DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit))
+		mask &= ((0-(((W[38]^W[40])>>4)&1)) | ~(DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit));
+	if (mask & (DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit))
+		mask &= ((0-(((W[37]^W[39])>>4)&1)) | ~(DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit));
+	mask &= ((0-((W[36]^(W[37]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_50_2_bit|DV_II_46_2_bit));
+	if (mask & (DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit))
+		mask &= (((((W[35]>>4)^(W[39]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit));
+	if (mask & (DV_I_48_0_bit|DV_II_48_0_bit))
+		mask &= ((0-((W[63]^(W[64]>>5))&(1<<0))) | ~(DV_I_48_0_bit|DV_II_48_0_bit));
+	if (mask & (DV_I_45_0_bit|DV_II_45_0_bit))
+		mask &= ((0-((W[63]^(W[64]>>5))&(1<<1))) | ~(DV_I_45_0_bit|DV_II_45_0_bit));
+	if (mask & (DV_I_47_0_bit|DV_II_47_0_bit))
+		mask &= ((0-((W[62]^(W[63]>>5))&(1<<0))) | ~(DV_I_47_0_bit|DV_II_47_0_bit));
+	if (mask & (DV_I_46_0_bit|DV_II_46_0_bit))
+		mask &= ((0-((W[61]^(W[62]>>5))&(1<<0))) | ~(DV_I_46_0_bit|DV_II_46_0_bit));
+	mask &= ((0-((W[61]^(W[62]>>5))&(1<<2))) | ~(DV_I_46_2_bit|DV_II_46_2_bit));
+	if (mask & (DV_I_45_0_bit|DV_II_45_0_bit))
+		mask &= ((0-((W[60]^(W[61]>>5))&(1<<0))) | ~(DV_I_45_0_bit|DV_II_45_0_bit));
+	if (mask & (DV_II_51_0_bit|DV_II_54_0_bit))
+		mask &= (((((W[58]^W[59])>>29)&1)-1) | ~(DV_II_51_0_bit|DV_II_54_0_bit));
+	if (mask & (DV_II_50_0_bit|DV_II_53_0_bit))
+		mask &= (((((W[57]^W[58])>>29)&1)-1) | ~(DV_II_50_0_bit|DV_II_53_0_bit));
+	if (mask & (DV_II_52_0_bit|DV_II_54_0_bit))
+		mask &= ((((W[56]^(W[59]>>25))&(1<<4))-(1<<4)) | ~(DV_II_52_0_bit|DV_II_54_0_bit));
+	if (mask & (DV_II_51_0_bit|DV_II_52_0_bit))
+		mask &= ((0-(((W[56]^W[59])>>29)&1)) | ~(DV_II_51_0_bit|DV_II_52_0_bit));
+	if (mask & (DV_II_49_0_bit|DV_II_52_0_bit))
+		mask &= (((((W[56]^W[57])>>29)&1)-1) | ~(DV_II_49_0_bit|DV_II_52_0_bit));
+	if (mask & (DV_II_51_0_bit|DV_II_53_0_bit))
+		mask &= ((((W[55]^(W[58]>>25))&(1<<4))-(1<<4)) | ~(DV_II_51_0_bit|DV_II_53_0_bit));
+	if (mask & (DV_II_50_0_bit|DV_II_52_0_bit))
+		mask &= ((((W[54]^(W[57]>>25))&(1<<4))-(1<<4)) | ~(DV_II_50_0_bit|DV_II_52_0_bit));
+	if (mask & (DV_II_49_0_bit|DV_II_51_0_bit))
+		mask &= ((((W[53]^(W[56]>>25))&(1<<4))-(1<<4)) | ~(DV_II_49_0_bit|DV_II_51_0_bit));
+	mask &= ((((W[51]^(W[50]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_46_2_bit));
+	mask &= ((((W[48]^W[50])&(1<<6))-(1<<6)) | ~(DV_I_50_2_bit|DV_II_46_2_bit));
+	if (mask & (DV_I_51_0_bit|DV_I_52_0_bit))
+		mask &= ((0-(((W[48]^W[55])>>29)&1)) | ~(DV_I_51_0_bit|DV_I_52_0_bit));
+	mask &= ((((W[47]^W[49])&(1<<6))-(1<<6)) | ~(DV_I_49_2_bit|DV_I_51_2_bit));
+	mask &= ((((W[48]^(W[47]>>5))&(1<<1))-(1<<1)) | ~(DV_I_47_2_bit|DV_II_51_2_bit));
+	mask &= ((((W[46]^W[48])&(1<<6))-(1<<6)) | ~(DV_I_48_2_bit|DV_I_50_2_bit));
+	mask &= ((((W[47]^(W[46]>>5))&(1<<1))-(1<<1)) | ~(DV_I_46_2_bit|DV_II_50_2_bit));
+	mask &= ((0-((W[44]^(W[45]>>5))&(1<<1))) | ~(DV_I_51_2_bit|DV_II_49_2_bit));
+	mask &= ((((W[43]^W[45])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit));
+	mask &= (((((W[42]^W[44])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit));
+	mask &= ((((W[43]^(W[42]>>5))&(1<<1))-(1<<1)) | ~(DV_II_46_2_bit|DV_II_51_2_bit));
+	mask &= ((((W[42]^(W[41]>>5))&(1<<1))-(1<<1)) | ~(DV_I_51_2_bit|DV_II_50_2_bit));
+	mask &= ((((W[41]^(W[40]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_49_2_bit));
+	if (mask & (DV_I_52_0_bit|DV_II_51_0_bit))
+		mask &= ((((W[39]^(W[43]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_51_0_bit));
+	if (mask & (DV_I_51_0_bit|DV_II_50_0_bit))
+		mask &= ((((W[38]^(W[42]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_50_0_bit));
+	if (mask & (DV_I_48_2_bit|DV_I_51_2_bit))
+		mask &= ((0-((W[37]^(W[38]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_I_51_2_bit));
+	if (mask & (DV_I_50_0_bit|DV_II_49_0_bit))
+		mask &= ((((W[37]^(W[41]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_II_49_0_bit));
+	if (mask & (DV_II_52_0_bit|DV_II_54_0_bit))
+		mask &= ((0-((W[36]^W[38])&(1<<4))) | ~(DV_II_52_0_bit|DV_II_54_0_bit));
+	mask &= ((0-((W[35]^(W[36]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_49_2_bit));
+	if (mask & (DV_I_51_0_bit|DV_II_47_0_bit))
+		mask &= ((((W[35]^(W[39]>>25))&(1<<3))-(1<<3)) | ~(DV_I_51_0_bit|DV_II_47_0_bit));
+if (mask) {
+
+	if (mask & DV_I_43_0_bit)
+		 if (
+			    !((W[61]^(W[62]>>5)) & (1<<1))
+			 || !(!((W[59]^(W[63]>>25)) & (1<<5)))
+			 || !((W[58]^(W[63]>>30)) & (1<<0))
+		 )  mask &= ~DV_I_43_0_bit;
+	if (mask & DV_I_44_0_bit)
+		 if (
+			    !((W[62]^(W[63]>>5)) & (1<<1))
+			 || !(!((W[60]^(W[64]>>25)) & (1<<5)))
+			 || !((W[59]^(W[64]>>30)) & (1<<0))
+		 )  mask &= ~DV_I_44_0_bit;
+	if (mask & DV_I_46_2_bit)
+		mask &= ((~((W[40]^W[42])>>2)) | ~DV_I_46_2_bit);
+	if (mask & DV_I_47_2_bit)
+		 if (
+			    !((W[62]^(W[63]>>5)) & (1<<2))
+			 || !(!((W[41]^W[43]) & (1<<6)))
+		 )  mask &= ~DV_I_47_2_bit;
+	if (mask & DV_I_48_2_bit)
+		 if (
+			    !((W[63]^(W[64]>>5)) & (1<<2))
+			 || !(!((W[48]^(W[49]<<5)) & (1<<6)))
+		 )  mask &= ~DV_I_48_2_bit;
+	if (mask & DV_I_49_2_bit)
+		 if (
+			    !(!((W[49]^(W[50]<<5)) & (1<<6)))
+			 || !((W[42]^W[50]) & (1<<1))
+			 || !(!((W[39]^(W[40]<<5)) & (1<<6)))
+			 || !((W[38]^W[40]) & (1<<1))
+		 )  mask &= ~DV_I_49_2_bit;
+	if (mask & DV_I_50_0_bit)
+		mask &= ((((W[36]^W[37])<<7)) | ~DV_I_50_0_bit);
+	if (mask & DV_I_50_2_bit)
+		mask &= ((((W[43]^W[51])<<11)) | ~DV_I_50_2_bit);
+	if (mask & DV_I_51_0_bit)
+		mask &= ((((W[37]^W[38])<<9)) | ~DV_I_51_0_bit);
+	if (mask & DV_I_51_2_bit)
+		 if (
+			    !(!((W[51]^(W[52]<<5)) & (1<<6)))
+			 || !(!((W[49]^W[51]) & (1<<6)))
+			 || !(!((W[37]^(W[37]>>5)) & (1<<1)))
+			 || !(!((W[35]^(W[39]>>25)) & (1<<5)))
+		 )  mask &= ~DV_I_51_2_bit;
+	if (mask & DV_I_52_0_bit)
+		mask &= ((((W[38]^W[39])<<11)) | ~DV_I_52_0_bit);
+	if (mask & DV_II_46_2_bit)
+		mask &= ((((W[47]^W[51])<<17)) | ~DV_II_46_2_bit);
+	if (mask & DV_II_48_0_bit)
+		 if (
+			    !(!((W[36]^(W[40]>>25)) & (1<<3)))
+			 || !((W[35]^(W[40]<<2)) & (1<<30))
+		 )  mask &= ~DV_II_48_0_bit;
+	if (mask & DV_II_49_0_bit)
+		 if (
+			    !(!((W[37]^(W[41]>>25)) & (1<<3)))
+			 || !((W[36]^(W[41]<<2)) & (1<<30))
+		 )  mask &= ~DV_II_49_0_bit;
+	if (mask & DV_II_49_2_bit)
+		 if (
+			    !(!((W[53]^(W[54]<<5)) & (1<<6)))
+			 || !(!((W[51]^W[53]) & (1<<6)))
+			 || !((W[50]^W[54]) & (1<<1))
+			 || !(!((W[45]^(W[46]<<5)) & (1<<6)))
+			 || !(!((W[37]^(W[41]>>25)) & (1<<5)))
+			 || !((W[36]^(W[41]>>30)) & (1<<0))
+		 )  mask &= ~DV_II_49_2_bit;
+	if (mask & DV_II_50_0_bit)
+		 if (
+			    !((W[55]^W[58]) & (1<<29))
+			 || !(!((W[38]^(W[42]>>25)) & (1<<3)))
+			 || !((W[37]^(W[42]<<2)) & (1<<30))
+		 )  mask &= ~DV_II_50_0_bit;
+	if (mask & DV_II_50_2_bit)
+		 if (
+			    !(!((W[54]^(W[55]<<5)) & (1<<6)))
+			 || !(!((W[52]^W[54]) & (1<<6)))
+			 || !((W[51]^W[55]) & (1<<1))
+			 || !((W[45]^W[47]) & (1<<1))
+			 || !(!((W[38]^(W[42]>>25)) & (1<<5)))
+			 || !((W[37]^(W[42]>>30)) & (1<<0))
+		 )  mask &= ~DV_II_50_2_bit;
+	if (mask & DV_II_51_0_bit)
+		 if (
+			    !(!((W[39]^(W[43]>>25)) & (1<<3)))
+			 || !((W[38]^(W[43]<<2)) & (1<<30))
+		 )  mask &= ~DV_II_51_0_bit;
+	if (mask & DV_II_51_2_bit)
+		 if (
+			    !(!((W[55]^(W[56]<<5)) & (1<<6)))
+			 || !(!((W[53]^W[55]) & (1<<6)))
+			 || !((W[52]^W[56]) & (1<<1))
+			 || !((W[46]^W[48]) & (1<<1))
+			 || !(!((W[39]^(W[43]>>25)) & (1<<5)))
+			 || !((W[38]^(W[43]>>30)) & (1<<0))
+		 )  mask &= ~DV_II_51_2_bit;
+	if (mask & DV_II_52_0_bit)
+		 if (
+			    !(!((W[59]^W[60]) & (1<<29)))
+			 || !(!((W[40]^(W[44]>>25)) & (1<<3)))
+			 || !(!((W[40]^(W[44]>>25)) & (1<<4)))
+			 || !((W[39]^(W[44]<<2)) & (1<<30))
+		 )  mask &= ~DV_II_52_0_bit;
+	if (mask & DV_II_53_0_bit)
+		 if (
+			    !((W[58]^W[61]) & (1<<29))
+			 || !(!((W[57]^(W[61]>>25)) & (1<<4)))
+			 || !(!((W[41]^(W[45]>>25)) & (1<<3)))
+			 || !(!((W[41]^(W[45]>>25)) & (1<<4)))
+		 )  mask &= ~DV_II_53_0_bit;
+	if (mask & DV_II_54_0_bit)
+		 if (
+			    !(!((W[58]^(W[62]>>25)) & (1<<4)))
+			 || !(!((W[42]^(W[46]>>25)) & (1<<3)))
+			 || !(!((W[42]^(W[46]>>25)) & (1<<4)))
+		 )  mask &= ~DV_II_54_0_bit;
+	if (mask & DV_II_55_0_bit)
+		 if (
+			    !(!((W[59]^(W[63]>>25)) & (1<<4)))
+			 || !(!((W[57]^(W[59]>>25)) & (1<<4)))
+			 || !(!((W[43]^(W[47]>>25)) & (1<<3)))
+			 || !(!((W[43]^(W[47]>>25)) & (1<<4)))
+		 )  mask &= ~DV_II_55_0_bit;
+	if (mask & DV_II_56_0_bit)
+		 if (
+			    !(!((W[60]^(W[64]>>25)) & (1<<4)))
+			 || !(!((W[44]^(W[48]>>25)) & (1<<3)))
+			 || !(!((W[44]^(W[48]>>25)) & (1<<4)))
+		 )  mask &= ~DV_II_56_0_bit;
+}
+
+	dvmask[0]=mask;
+}
diff --git a/src/hash/sha1dc/ubc_check.h b/src/hash/sha1dc/ubc_check.h
new file mode 100644
index 0000000..27285bd
--- /dev/null
+++ b/src/hash/sha1dc/ubc_check.h
@@ -0,0 +1,35 @@
+/***
+* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
+* Distributed under the MIT Software License.
+* See accompanying file LICENSE.txt or copy at
+* https://opensource.org/licenses/MIT
+***/
+
+// this file was generated by the 'parse_bitrel' program in the tools section
+// using the data files from directory 'tools/data/3565'
+//
+// sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check
+// dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper)
+// dm[80] is the expanded message block XOR-difference defined by the DV
+// testt is the step to do the recompression from for collision detection
+// maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check
+//
+// ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs
+// it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met
+// thus one needs to do the recompression check for each DV that has its bit set
+
+#ifndef UBC_CHECK_H
+#define UBC_CHECK_H
+
+#include <stdint.h>
+
+#define DVMASKSIZE 1
+typedef struct { int dvType; int dvK; int dvB; int testt; int maski; int maskb; uint32_t dm[80]; } dv_info_t;
+extern dv_info_t sha1_dvs[];
+void ubc_check(const uint32_t W[80], uint32_t dvmask[DVMASKSIZE]);
+
+#define DOSTORESTATE58
+#define DOSTORESTATE65
+
+
+#endif // UBC_CHECK_H
diff --git a/src/hashsig.c b/src/hashsig.c
index e99637d..bea5383 100644
--- a/src/hashsig.c
+++ b/src/hashsig.c
@@ -214,7 +214,7 @@
 	if (sig->mins.size < HASHSIG_HEAP_MIN_SIZE &&
 		!(sig->opt & GIT_HASHSIG_ALLOW_SMALL_FILES)) {
 		giterr_set(GITERR_INVALID,
-			"File too small for similarity signature calculation");
+			"file too small for similarity signature calculation");
 		return GIT_EBUFS;
 	}
 
@@ -286,7 +286,7 @@
 		if ((buflen = p_read(fd, buf, sizeof(buf))) <= 0) {
 			if ((error = (int)buflen) < 0)
 				giterr_set(GITERR_OS,
-					"Read error on '%s' calculating similarity hashes", path);
+					"read error on '%s' calculating similarity hashes", path);
 			break;
 		}
 
diff --git a/src/idxmap.c b/src/idxmap.c
new file mode 100644
index 0000000..45f0c22
--- /dev/null
+++ b/src/idxmap.c
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+#include "idxmap.h"
+
+/* This is __ac_X31_hash_string but with tolower and it takes the entry's stage into account */
+static kh_inline khint_t idxentry_hash(const git_index_entry *e)
+{
+	const char *s = e->path;
+	khint_t h = (khint_t)git__tolower(*s);
+	if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)git__tolower(*s);
+	return h + GIT_IDXENTRY_STAGE(e);
+}
+
+#define idxentry_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcmp(a->path, b->path) == 0)
+#define idxentry_icase_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcasecmp(a->path, b->path) == 0)
+
+__KHASH_IMPL(idx, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_equal)
+__KHASH_IMPL(idxicase, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_icase_equal)
+
+int git_idxmap_alloc(git_idxmap **map)
+{
+	if ((*map = kh_init(idx)) == NULL) {
+		giterr_set_oom();
+		return -1;
+	}
+
+	return 0;
+}
+
+int git_idxmap_icase_alloc(git_idxmap_icase **map)
+{
+	if ((*map = kh_init(idxicase)) == NULL) {
+		giterr_set_oom();
+		return -1;
+	}
+
+	return 0;
+}
+
+void git_idxmap_insert(git_idxmap *map, const git_index_entry *key, void *value, int *rval)
+{
+	khiter_t idx = kh_put(idx, map, key, rval);
+
+	if ((*rval) >= 0) {
+		if ((*rval) == 0)
+			kh_key(map, idx) = key;
+		kh_val(map, idx) = value;
+	}
+}
+
+void git_idxmap_icase_insert(git_idxmap_icase *map, const git_index_entry *key, void *value, int *rval)
+{
+	khiter_t idx = kh_put(idxicase, map, key, rval);
+
+	if ((*rval) >= 0) {
+		if ((*rval) == 0)
+			kh_key(map, idx) = key;
+		kh_val(map, idx) = value;
+	}
+}
+
+size_t git_idxmap_lookup_index(git_idxmap *map, const git_index_entry *key)
+{
+	return kh_get(idx, map, key);
+}
+
+size_t git_idxmap_icase_lookup_index(git_idxmap_icase *map, const git_index_entry *key)
+{
+	return kh_get(idxicase, map, key);
+}
+
+void *git_idxmap_value_at(git_idxmap *map, size_t idx)
+{
+	return kh_val(map, idx);
+}
+
+int git_idxmap_valid_index(git_idxmap *map, size_t idx)
+{
+	return idx != kh_end(map);
+}
+
+int git_idxmap_has_data(git_idxmap *map, size_t idx)
+{
+	return kh_exist(map, idx);
+}
+
+void git_idxmap_resize(git_idxmap *map, size_t size)
+{
+	kh_resize(idx, map, size);
+}
+
+void git_idxmap_icase_resize(git_idxmap_icase *map, size_t size)
+{
+	kh_resize(idxicase, map, size);
+}
+
+void git_idxmap_free(git_idxmap *map)
+{
+	kh_destroy(idx, map);
+}
+
+void git_idxmap_clear(git_idxmap *map)
+{
+	kh_clear(idx, map);
+}
+
+void git_idxmap_delete_at(git_idxmap *map, size_t idx)
+{
+	kh_del(idx, map, idx);
+}
+
+void git_idxmap_icase_delete_at(git_idxmap_icase *map, size_t idx)
+{
+	kh_del(idxicase, map, idx);
+}
+
+void git_idxmap_delete(git_idxmap *map, const git_index_entry *key)
+{
+	khiter_t idx = git_idxmap_lookup_index(map, key);
+	if (git_idxmap_valid_index(map, idx))
+		git_idxmap_delete_at(map, idx);
+}
+void git_idxmap_icase_delete(git_idxmap_icase *map, const git_index_entry *key)
+{
+	khiter_t idx = git_idxmap_icase_lookup_index(map, key);
+	if (git_idxmap_valid_index((git_idxmap *)map, idx))
+		git_idxmap_icase_delete_at(map, idx);
+}
diff --git a/src/idxmap.h b/src/idxmap.h
index 4122a89..dc702c3 100644
--- a/src/idxmap.h
+++ b/src/idxmap.h
@@ -26,66 +26,27 @@
 
 typedef khiter_t git_idxmap_iter;
 
-/* This is __ac_X31_hash_string but with tolower and it takes the entry's stage into account */
-static kh_inline khint_t idxentry_hash(const git_index_entry *e)
-{
-	const char *s = e->path;
-	khint_t h = (khint_t)git__tolower(*s);
-	if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)git__tolower(*s);
-	return h + GIT_IDXENTRY_STAGE(e);
-}
+int git_idxmap_alloc(git_idxmap **map);
+int git_idxmap_icase_alloc(git_idxmap_icase **map);
+void git_idxmap_insert(git_idxmap *map, const git_index_entry *key, void *value, int *rval);
+void git_idxmap_icase_insert(git_idxmap_icase *map, const git_index_entry *key, void *value, int *rval);
 
-#define idxentry_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcmp(a->path, b->path) == 0)
-#define idxentry_icase_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcasecmp(a->path, b->path) == 0)
+size_t git_idxmap_lookup_index(git_idxmap *map, const git_index_entry *key);
+size_t git_idxmap_icase_lookup_index(git_idxmap_icase *map, const git_index_entry *key);
+void *git_idxmap_value_at(git_idxmap *map, size_t idx);
+int git_idxmap_valid_index(git_idxmap *map, size_t idx);
+int git_idxmap_has_data(git_idxmap *map, size_t idx);
 
-#define GIT__USE_IDXMAP \
-	__KHASH_IMPL(idx, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_equal)
+void git_idxmap_resize(git_idxmap *map, size_t size);
+void git_idxmap_icase_resize(git_idxmap_icase *map, size_t size);
+void git_idxmap_free(git_idxmap *map);
+void git_idxmap_clear(git_idxmap *map);
 
-#define GIT__USE_IDXMAP_ICASE \
-	__KHASH_IMPL(idxicase, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_icase_equal)
+void git_idxmap_delete_at(git_idxmap *map, size_t idx);
+void git_idxmap_icase_delete_at(git_idxmap_icase *map, size_t idx);
 
-#define git_idxmap_alloc(hp) \
-	((*(hp) = kh_init(idx)) == NULL) ? giterr_set_oom(), -1 : 0
-
-#define git_idxmap_icase_alloc(hp) \
-	((*(hp) = kh_init(idxicase)) == NULL) ? giterr_set_oom(), -1 : 0
-
-#define git_idxmap_insert(h, key, val, rval) do { \
-	khiter_t __pos = kh_put(idx, h, key, &rval); \
-	if (rval >= 0) { \
-		if (rval == 0) kh_key(h, __pos) = key; \
-		kh_val(h, __pos) = val; \
-	} } while (0)
-
-#define git_idxmap_icase_insert(h, key, val, rval) do { \
-	khiter_t __pos = kh_put(idxicase, h, key, &rval); \
-	if (rval >= 0) { \
-		if (rval == 0) kh_key(h, __pos) = key; \
-		kh_val(h, __pos) = val; \
-	} } while (0)
-
-#define git_idxmap_lookup_index(h, k)  kh_get(idx, h, k)
-#define git_idxmap_icase_lookup_index(h, k)  kh_get(idxicase, h, k)
-#define git_idxmap_value_at(h, idx)        kh_val(h, idx)
-#define git_idxmap_valid_index(h, idx) (idx != kh_end(h))
-#define git_idxmap_has_data(h, idx) kh_exist(h, idx)
-
-#define git_idxmap_resize(h,s)  kh_resize(idx, h, s)
-#define git_idxmap_free(h)  kh_destroy(idx, h), h = NULL
-#define git_idxmap_clear(h) kh_clear(idx, h)
-
-#define git_idxmap_delete_at(h, id)       kh_del(idx, h, id)
-#define git_idxmap_icase_delete_at(h, id)       kh_del(idxicase, h, id)
-
-#define git_idxmap_delete(h, key) do { \
-	khiter_t __pos = git_idxmap_lookup_index(h, key); \
-	if (git_idxmap_valid_index(h, __pos)) \
-		git_idxmap_delete_at(h, __pos); } while (0)
-
-#define git_idxmap_icase_delete(h, key) do { \
-	khiter_t __pos = git_idxmap_icase_lookup_index(h, key); \
-	if (git_idxmap_valid_index(h, __pos)) \
-		git_idxmap_icase_delete_at(h, __pos); } while (0)
+void git_idxmap_delete(git_idxmap *map, const git_index_entry *key);
+void git_idxmap_icase_delete(git_idxmap_icase *map, const git_index_entry *key);
 
 #define git_idxmap_begin		kh_begin
 #define git_idxmap_end		kh_end
diff --git a/src/ignore.c b/src/ignore.c
index dcbd5c1..c324d4d 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -175,7 +175,7 @@
 		context = attrs->entry->path;
 
 	if (git_mutex_lock(&attrs->lock) < 0) {
-		giterr_set(GITERR_OS, "Failed to lock ignore file");
+		giterr_set(GITERR_OS, "failed to lock ignore file");
 		return -1;
 	}
 
@@ -277,8 +277,9 @@
 {
 	int error = 0;
 	const char *workdir = git_repository_workdir(repo);
+	git_buf infopath = GIT_BUF_INIT;
 
-	assert(ignores && path);
+	assert(repo && ignores && path);
 
 	memset(ignores, 0, sizeof(*ignores));
 	ignores->repo = repo;
@@ -322,10 +323,14 @@
 			goto cleanup;
 	}
 
+	if ((error = git_repository_item_path(&infopath,
+			repo, GIT_REPOSITORY_ITEM_INFO)) < 0)
+		goto cleanup;
+
 	/* load .git/info/exclude */
 	error = push_ignore_file(
 		ignores, &ignores->ign_global,
-		git_repository_path(repo), GIT_IGNORE_FILE_INREPO);
+		infopath.ptr, GIT_IGNORE_FILE_INREPO);
 	if (error < 0)
 		goto cleanup;
 
@@ -336,6 +341,7 @@
 			git_repository_attr_cache(repo)->cfg_excl_file);
 
 cleanup:
+	git_buf_free(&infopath);
 	if (error < 0)
 		git_ignore__free(ignores);
 
@@ -503,9 +509,9 @@
 	unsigned int i;
 	git_attr_file *file;
 
-	assert(ignored && pathname);
+	assert(repo && ignored && pathname);
 
-	workdir = repo ? git_repository_workdir(repo) : NULL;
+	workdir = git_repository_workdir(repo);
 
 	memset(&path, 0, sizeof(path));
 	memset(&ignores, 0, sizeof(ignores));
diff --git a/src/ignore.h b/src/ignore.h
index d40bd60..876c8e0 100644
--- a/src/ignore.h
+++ b/src/ignore.h
@@ -12,7 +12,7 @@
 #include "attr_file.h"
 
 #define GIT_IGNORE_FILE			".gitignore"
-#define GIT_IGNORE_FILE_INREPO	"info/exclude"
+#define GIT_IGNORE_FILE_INREPO	"exclude"
 #define GIT_IGNORE_FILE_XDG		"ignore"
 
 /* The git_ignores structure maintains three sets of ignores:
diff --git a/src/index.c b/src/index.c
index bc15959..932a530 100644
--- a/src/index.c
+++ b/src/index.c
@@ -27,9 +27,6 @@
 #include "git2/config.h"
 #include "git2/sys/index.h"
 
-GIT__USE_IDXMAP
-GIT__USE_IDXMAP_ICASE
-
 #define INSERT_IN_MAP_EX(idx, map, e, err) do {				\
 		if ((idx)->ignore_case)					\
 			git_idxmap_icase_insert((khash_t(idxicase) *) (map), (e), (e), (err)); \
@@ -552,7 +549,7 @@
 
 static int create_index_error(int error, const char *msg)
 {
-	giterr_set(GITERR_INDEX, msg);
+	giterr_set_str(GITERR_INDEX, msg);
 	return error;
 }
 
@@ -570,7 +567,7 @@
 
 		if (!repo)
 			return create_index_error(
-				-1, "Cannot access repository to set index caps");
+				-1, "cannot access repository to set index caps");
 
 		if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORECASE))
 			index->ignore_case = (val != 0);
@@ -639,7 +636,7 @@
 
 	if (!index->index_file_path)
 		return create_index_error(-1,
-			"Failed to read index: The index is in-memory only");
+			"failed to read index: The index is in-memory only");
 
 	index->on_disk = git_path_exists(index->index_file_path);
 
@@ -653,7 +650,7 @@
 	    ((updated = compare_checksum(index)) < 0)) {
 		giterr_set(
 			GITERR_INDEX,
-			"Failed to read index: '%s' no longer exists",
+			"failed to read index: '%s' no longer exists",
 			index->index_file_path);
 		return updated;
 	}
@@ -765,7 +762,7 @@
 
 	if (version < INDEX_VERSION_NUMBER_LB ||
 	    version > INDEX_VERSION_NUMBER_UB) {
-		giterr_set(GITERR_INDEX, "Invalid version number");
+		giterr_set(GITERR_INDEX, "invalid version number");
 		return -1;
 	}
 
@@ -805,7 +802,7 @@
 
 	if (repo == NULL)
 		return create_index_error(-1, "Failed to write tree. "
-		  "The index file is not backed up by an existing repository");
+		  "the index file is not backed up by an existing repository");
 
 	return git_tree__write_index(oid, index, repo);
 }
@@ -847,7 +844,7 @@
 	if (git_idxmap_valid_index(index->entries_map, pos))
 		return git_idxmap_value_at(index->entries_map, pos);
 
-	giterr_set(GITERR_INDEX, "Index does not contain %s", path);
+	giterr_set(GITERR_INDEX, "index does not contain '%s'", path);
 	return NULL;
 }
 
@@ -934,7 +931,7 @@
 
 	if (INDEX_OWNER(index) == NULL)
 		return create_index_error(-1,
-			"Could not initialize index entry. "
+			"could not initialize index entry. "
 			"Index is not backed up by an existing repository.");
 
 	if (index_entry_create(&entry, INDEX_OWNER(index), rel_path, true) < 0)
@@ -1365,7 +1362,7 @@
 		error = git_vector_insert_sorted(&index->entries, entry, index_no_dups);
 
 		if (error == 0) {
-			INSERT_IN_MAP(index, entry, error);
+			INSERT_IN_MAP(index, entry, &error);
 		}
 	}
 
@@ -1423,7 +1420,7 @@
 
 	if (INDEX_OWNER(index) == NULL)
 		return create_index_error(-1,
-			"Could not initialize index entry. "
+			"could not initialize index entry. "
 			"Index is not backed up by an existing repository.");
 
 	if (!valid_filemode(source_entry->mode)) {
@@ -1592,7 +1589,7 @@
 		if ((ret = git_vector_insert(&index->entries, entry)) < 0)
 			break;
 
-		INSERT_IN_MAP(index, entry, ret);
+		INSERT_IN_MAP(index, entry, &ret);
 		if (ret < 0)
 			break;
 	}
@@ -1637,7 +1634,7 @@
 
 	if (index_find(&position, index, path, 0, stage) < 0) {
 		giterr_set(
-			GITERR_INDEX, "Index does not contain %s at stage %d", path, stage);
+			GITERR_INDEX, "index does not contain %s at stage %d", path, stage);
 		error = GIT_ENOTFOUND;
 	} else {
 		error = index_remove_entry(index, position);
@@ -1709,7 +1706,7 @@
 
 	if (git_vector_bsearch2(
 			&pos, &index->entries, index->entries_search_path, path) < 0) {
-		giterr_set(GITERR_INDEX, "Index does not contain %s", path);
+		giterr_set(GITERR_INDEX, "index does not contain %s", path);
 		return GIT_ENOTFOUND;
 	}
 
@@ -2153,7 +2150,7 @@
 
 static int index_error_invalid(const char *message)
 {
-	giterr_set(GITERR_INDEX, "Invalid data in index - %s", message);
+	giterr_set(GITERR_INDEX, "invalid data in index - %s", message);
 	return -1;
 }
 
@@ -2479,9 +2476,9 @@
 	assert(!index->entries.length);
 
 	if (index->ignore_case)
-		kh_resize(idxicase, (khash_t(idxicase) *) index->entries_map, header.entry_count);
+		git_idxmap_icase_resize((khash_t(idxicase) *) index->entries_map, header.entry_count);
 	else
-		kh_resize(idx, index->entries_map, header.entry_count);
+		git_idxmap_resize(index->entries_map, header.entry_count);
 
 	/* Parse all the entries */
 	for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
@@ -2499,7 +2496,7 @@
 			goto done;
 		}
 
-		INSERT_IN_MAP(index, entry, error);
+		INSERT_IN_MAP(index, entry, &error);
 
 		if (error < 0) {
 			index_entry_free(entry);
@@ -2979,12 +2976,12 @@
 		goto cleanup;
 
 	if (index->ignore_case)
-		kh_resize(idxicase, (khash_t(idxicase) *) entries_map, entries.length);
+		git_idxmap_icase_resize((khash_t(idxicase) *) entries_map, entries.length);
 	else
-		kh_resize(idx, entries_map, entries.length);
+		git_idxmap_resize(entries_map, entries.length);
 
 	git_vector_foreach(&entries, i, e) {
-		INSERT_IN_MAP_EX(index, entries_map, e, error);
+		INSERT_IN_MAP_EX(index, entries_map, e, &error);
 
 		if (error < 0) {
 			giterr_set(GITERR_INDEX, "failed to insert entry into map");
@@ -3037,9 +3034,9 @@
 		goto done;
 
 	if (index->ignore_case && new_length_hint)
-		kh_resize(idxicase, (khash_t(idxicase) *) new_entries_map, new_length_hint);
+		git_idxmap_icase_resize((khash_t(idxicase) *) new_entries_map, new_length_hint);
 	else if (new_length_hint)
-		kh_resize(idx, new_entries_map, new_length_hint);
+		git_idxmap_resize(new_entries_map, new_length_hint);
 
 	opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE |
 		GIT_ITERATOR_INCLUDE_CONFLICTS;
@@ -3103,7 +3100,7 @@
 
 		if (add_entry) {
 			if ((error = git_vector_insert(&new_entries, add_entry)) == 0)
-				INSERT_IN_MAP_EX(index, new_entries_map, add_entry, error);
+				INSERT_IN_MAP_EX(index, new_entries_map, add_entry, &error);
 		}
 
 		if (remove_entry && error >= 0)
@@ -3390,7 +3387,7 @@
 				i--; /* back up foreach if we removed this */
 			break;
 		default:
-			giterr_set(GITERR_INVALID, "Unknown index action %d", action);
+			giterr_set(GITERR_INVALID, "unknown index action %d", action);
 			error = -1;
 			break;
 		}
@@ -3475,13 +3472,13 @@
 
 	if (!index->index_file_path)
 		return create_index_error(-1,
-			"Failed to write index: The index is in-memory only");
+			"failed to write index: The index is in-memory only");
 
 	if ((error = git_filebuf_open(
 		&writer->file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_INDEX_FILE_MODE)) < 0) {
 
 		if (error == GIT_ELOCKED)
-			giterr_set(GITERR_INDEX, "The index is locked. This might be due to a concurrent or crashed process");
+			giterr_set(GITERR_INDEX, "the index is locked; this might be due to a concurrent or crashed process");
 
 		return error;
 	}
@@ -3530,7 +3527,7 @@
 
 	if ((error = git_futils_filestamp_check(
 		&writer->index->stamp, writer->index->index_file_path)) < 0) {
-		giterr_set(GITERR_OS, "Could not read index timestamp");
+		giterr_set(GITERR_OS, "could not read index timestamp");
 		return -1;
 	}
 
diff --git a/src/indexer.c b/src/indexer.c
index a3a8669..ce67240 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -17,8 +17,7 @@
 #include "oid.h"
 #include "oidmap.h"
 #include "zstream.h"
-
-GIT__USE_OIDMAP
+#include "object.h"
 
 extern git_mutex git__mwindow_mutex;
 
@@ -33,9 +32,10 @@
 
 struct git_indexer {
 	unsigned int parsed_header :1,
-		opened_pack :1,
+		pack_committed :1,
 		have_stream :1,
-		have_delta :1;
+		have_delta :1,
+		do_fsync :1;
 	struct git_pack_header hdr;
 	struct git_pack_file *pack;
 	unsigned int mode;
@@ -83,12 +83,12 @@
 
 	/* Verify we recognize this pack file format. */
 	if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) {
-		giterr_set(GITERR_INDEXER, "Wrong pack signature");
+		giterr_set(GITERR_INDEXER, "wrong pack signature");
 		return -1;
 	}
 
 	if (!pack_version_ok(hdr->hdr_version)) {
-		giterr_set(GITERR_INDEXER, "Wrong pack version");
+		giterr_set(GITERR_INDEXER, "wrong pack version");
 		return -1;
 	}
 
@@ -125,6 +125,9 @@
 	git_hash_ctx_init(&idx->hash_ctx);
 	git_hash_ctx_init(&idx->trailer);
 
+	if (git_object__synchronous_writing)
+		idx->do_fsync = 1;
+
 	error = git_buf_joinpath(&path, prefix, suff);
 	if (error < 0)
 		goto cleanup;
@@ -151,12 +154,23 @@
 	if (fd != -1)
 		p_close(fd);
 
+	if (git_buf_len(&tmp_path) > 0)
+		p_unlink(git_buf_cstr(&tmp_path));
+
+	if (idx->pack != NULL)
+		p_unlink(idx->pack->pack_name);
+
 	git_buf_free(&path);
 	git_buf_free(&tmp_path);
 	git__free(idx);
 	return -1;
 }
 
+void git_indexer__set_fsync(git_indexer *idx, int do_fsync)
+{
+	idx->do_fsync = !!do_fsync;
+}
+
 /* Try to store the delta so we can try to resolve it later */
 static int store_delta(git_indexer *idx)
 {
@@ -288,7 +302,7 @@
 	git_oid_cpy(&pentry->sha1, &oid);
 	pentry->offset = entry_start;
 
-	k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
+	k = git_oidmap_put(idx->pack->idx_cache, &pentry->sha1, &error);
 	if (error == -1) {
 		git__free(pentry);
 		giterr_set_oom();
@@ -302,7 +316,7 @@
 	}
 
 
-	kh_value(idx->pack->idx_cache, k) = pentry;
+	git_oidmap_set_value_at(idx->pack->idx_cache, k, pentry);
 
 	git_oid_cpy(&entry->oid, &oid);
 
@@ -327,9 +341,7 @@
 
 GIT_INLINE(bool) has_entry(git_indexer *idx, git_oid *id)
 {
-	khiter_t k;
-	k = kh_get(oid, idx->pack->idx_cache, id);
-	return (k != kh_end(idx->pack->idx_cache));
+	return git_oidmap_exists(idx->pack->idx_cache, id);
 }
 
 static int save_entry(git_indexer *idx, struct entry *entry, struct git_pack_entry *pentry, git_off_t entry_start)
@@ -345,14 +357,14 @@
 	}
 
 	pentry->offset = entry_start;
-	k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
+	k = git_oidmap_put(idx->pack->idx_cache, &pentry->sha1, &error);
 
 	if (error <= 0) {
 		giterr_set(GITERR_INDEXER, "cannot insert object into pack");
 		return -1;
 	}
 
-	kh_value(idx->pack->idx_cache, k) = pentry;
+	git_oidmap_set_value_at(idx->pack->idx_cache, k, pentry);
 
 	/* Add the object to the list */
 	if (git_vector_insert(&idx->objects, entry) < 0)
@@ -376,7 +388,7 @@
 	GITERR_CHECK_ALLOC(entry);
 
 	if (git_odb__hashobj(&oid, obj) < 0) {
-		giterr_set(GITERR_INDEXER, "Failed to hash object");
+		giterr_set(GITERR_INDEXER, "failed to hash object");
 		goto on_error;
 	}
 
@@ -477,13 +489,29 @@
 
 static int append_to_pack(git_indexer *idx, const void *data, size_t size)
 {
+	git_off_t new_size;
+	size_t mmap_alignment;
+	size_t page_offset;
+	git_off_t page_start;
 	git_off_t current_size = idx->pack->mwf.size;
 	int fd = idx->pack->mwf.fd;
+	int error;
 
 	if (!size)
 		return 0;
 
-	if (p_lseek(fd, current_size + size - 1, SEEK_SET) < 0 ||
+	if ((error = git__mmap_alignment(&mmap_alignment)) < 0)
+		return error;
+
+	/* Write a single byte to force the file system to allocate space now or
+	 * report an error, since we can't report errors when writing using mmap.
+	 * Round the size up to the nearest page so that we only need to perform file
+	 * I/O when we add a page, instead of whenever we write even a single byte. */
+	new_size = current_size + size;
+	page_offset = new_size % mmap_alignment;
+	page_start = new_size - page_offset;
+
+	if (p_lseek(fd, page_start + mmap_alignment - 1, SEEK_SET) < 0 ||
 	    p_write(idx->pack->mwf.fd, data, 1) < 0) {
 		giterr_set(GITERR_OS, "cannot extend packfile '%s'", idx->pack->pack_name);
 		return -1;
@@ -971,7 +999,9 @@
 		return -1;
 
 	if (git_filebuf_open(&index_file, filename.ptr,
-		GIT_FILEBUF_HASH_CONTENTS, idx->mode) < 0)
+		GIT_FILEBUF_HASH_CONTENTS |
+		(idx->do_fsync ? GIT_FILEBUF_FSYNC : 0),
+		idx->mode) < 0)
 		goto on_error;
 
 	/* Write out the header */
@@ -1041,6 +1071,18 @@
 		goto on_error;
 
 	git_mwindow_free_all(&idx->pack->mwf);
+
+	/* Truncate file to undo rounding up to next page_size in append_to_pack */
+	if (p_ftruncate(idx->pack->mwf.fd, idx->pack->mwf.size) < 0) {
+		giterr_set(GITERR_OS, "failed to truncate pack file '%s'", idx->pack->pack_name);
+		return -1;
+	}
+
+	if (idx->do_fsync && p_fsync(idx->pack->mwf.fd) < 0) {
+		giterr_set(GITERR_OS, "failed to fsync packfile");
+		goto on_error;
+	}
+
 	/* We need to close the descriptor here so Windows doesn't choke on commit_at */
 	if (p_close(idx->pack->mwf.fd) < 0) {
 		giterr_set(GITERR_OS, "failed to close packfile");
@@ -1053,7 +1095,15 @@
 		goto on_error;
 
 	/* And don't forget to rename the packfile to its new place. */
-	p_rename(idx->pack->pack_name, git_buf_cstr(&filename));
+	if (p_rename(idx->pack->pack_name, git_buf_cstr(&filename)) < 0)
+		goto on_error;
+
+	/* And fsync the parent directory if we're asked to. */
+	if (idx->do_fsync &&
+		git_futils_fsync_parent(git_buf_cstr(&filename)) < 0)
+		goto on_error;
+
+	idx->pack_committed = 1;
 
 	git_buf_free(&filename);
 	git_hash_ctx_cleanup(&ctx);
@@ -1074,10 +1124,11 @@
 
 	git_vector_free_deep(&idx->objects);
 
-	if (idx->pack && idx->pack->idx_cache) {
+	if (idx->pack->idx_cache) {
 		struct git_pack_entry *pentry;
-		kh_foreach_value(
-			idx->pack->idx_cache, pentry, { git__free(pentry); });
+		git_oidmap_foreach_value(idx->pack->idx_cache, pentry, {
+			git__free(pentry);
+		});
 
 		git_oidmap_free(idx->pack->idx_cache);
 	}
@@ -1085,6 +1136,9 @@
 	git_vector_free_deep(&idx->deltas);
 
 	if (!git_mutex_lock(&git__mwindow_mutex)) {
+		if (!idx->pack_committed)
+			git_packfile_close(idx->pack, true);
+
 		git_packfile_free(idx->pack);
 		git_mutex_unlock(&git__mwindow_mutex);
 	}
diff --git a/src/indexer.h b/src/indexer.h
new file mode 100644
index 0000000..702694b
--- /dev/null
+++ b/src/indexer.h
@@ -0,0 +1,12 @@
+/*
+ * 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_indexer_h__
+#define INCLUDE_indexer_h__
+
+extern int git_indexer__set_fsync(git_indexer *idx, int do_fsync);
+
+#endif
diff --git a/src/integer.h b/src/integer.h
index b08094c..61712ce 100644
--- a/src/integer.h
+++ b/src/integer.h
@@ -55,16 +55,16 @@
 }
 
 /* Use clang/gcc compiler intrinsics whenever possible */
-#if (SIZE_MAX == UINT_MAX) && __has_builtin(__builtin_uadd_overflow)
-# define git__add_sizet_overflow(out, one, two) \
-	__builtin_uadd_overflow(one, two, out)
-# define git__multiply_sizet_overflow(out, one, two) \
-	__builtin_umul_overflow(one, two, out)
-#elif (SIZE_MAX == ULONG_MAX) && __has_builtin(__builtin_uaddl_overflow)
+#if (SIZE_MAX == ULONG_MAX) && __has_builtin(__builtin_uaddl_overflow)
 # define git__add_sizet_overflow(out, one, two) \
 	__builtin_uaddl_overflow(one, two, out)
 # define git__multiply_sizet_overflow(out, one, two) \
 	__builtin_umull_overflow(one, two, out)
+#elif (SIZE_MAX == UINT_MAX) && __has_builtin(__builtin_uadd_overflow)
+# define git__add_sizet_overflow(out, one, two) \
+	__builtin_uadd_overflow(one, two, out)
+# define git__multiply_sizet_overflow(out, one, two) \
+	__builtin_umul_overflow(one, two, out)
 #else
 
 /**
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 968b558..6e00b5a 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -78,7 +78,7 @@
 	unsigned int i;
 
 	if (length < 2) {
-		giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length);
+		giterr_set(GITERR_INVALID, "at least two commits are required to find an ancestor");
 		return -1;
 	}
 
@@ -104,7 +104,7 @@
 		goto on_error;
 
 	if (!result) {
-		giterr_set(GITERR_MERGE, "No merge base found");
+		giterr_set(GITERR_MERGE, "no merge base found");
 		error = GIT_ENOTFOUND;
 		goto on_error;
 	}
@@ -184,7 +184,7 @@
 	assert(out && repo && input_array);
 
 	if (length < 2) {
-		giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length);
+		giterr_set(GITERR_INVALID, "at least two commits are required to find an ancestor");
 		return -1;
 	}
 
@@ -230,7 +230,7 @@
 
 	if (!result) {
 		git_revwalk_free(walk);
-		giterr_set(GITERR_MERGE, "No merge base found");
+		giterr_set(GITERR_MERGE, "no merge base found");
 		return GIT_ENOTFOUND;
 	}
 
@@ -562,7 +562,7 @@
 
 	assert(repo && cb);
 
-	if ((error = git_buf_joinpath(&merge_head_path, repo->path_repository,
+	if ((error = git_buf_joinpath(&merge_head_path, repo->gitdir,
 		GIT_MERGE_HEAD_FILE)) < 0)
 		return error;
 
@@ -574,7 +574,7 @@
 
 	while ((line = git__strsep(&buffer, "\n")) != NULL) {
 		if (strlen(line) != GIT_OID_HEXSZ) {
-			giterr_set(GITERR_INVALID, "Unable to parse OID - invalid length");
+			giterr_set(GITERR_INVALID, "unable to parse OID - invalid length");
 			error = -1;
 			goto cleanup;
 		}
@@ -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;
 	}
@@ -1075,7 +1075,7 @@
 	int score = 0;
 	int error = 0;
 
-	if (GIT_MODE_TYPE(a->mode) != GIT_MODE_TYPE(b->mode))
+	if (!GIT_MODE_ISBLOB(a->mode) || !GIT_MODE_ISBLOB(b->mode))
 		return 0;
 
 	/* update signature cache if needed */
@@ -1713,16 +1713,16 @@
 	if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
 		return error;
 
-	if (given != NULL)
+	if (given != NULL) {
 		memcpy(opts, given, sizeof(git_merge_options));
-	else {
+	} else {
 		git_merge_options init = GIT_MERGE_OPTIONS_INIT;
 		memcpy(opts, &init, sizeof(init));
-
-		opts->flags = GIT_MERGE_FIND_RENAMES;
-		opts->rename_threshold = GIT_MERGE_DEFAULT_RENAME_THRESHOLD;
 	}
 
+	if ((opts->flags & GIT_MERGE_FIND_RENAMES) && !opts->rename_threshold)
+		opts->rename_threshold = GIT_MERGE_DEFAULT_RENAME_THRESHOLD;
+
 	if (given && given->default_driver) {
 		opts->default_driver = git__strdup(given->default_driver);
 		GITERR_CHECK_ALLOC(opts->default_driver);
@@ -2297,7 +2297,7 @@
 
 	assert(repo && heads);
 
-	if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_HEAD_FILE)) < 0 ||
+	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_HEAD_FILE)) < 0 ||
 		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0)
 		goto cleanup;
 
@@ -2325,7 +2325,7 @@
 
 	assert(repo);
 
-	if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MODE_FILE)) < 0 ||
+	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MODE_FILE)) < 0 ||
 		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0)
 		goto cleanup;
 
@@ -2556,7 +2556,7 @@
 	for (i = 0; i < heads_len; i++)
 		entries[i].merge_head = heads[i];
 
-	if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
+	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 ||
 		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0 ||
 		(error = git_filebuf_write(&file, "Merge ", 6)) < 0)
 		goto cleanup;
@@ -2934,7 +2934,7 @@
 	if (!git_index_has_conflicts(index))
 		return 0;
 
-	if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
+	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 ||
 		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_APPEND, GIT_MERGE_FILE_MODE)) < 0)
 		goto cleanup;
 
@@ -3063,7 +3063,7 @@
 	assert(analysis_out && preference_out && repo && their_heads);
 
 	if (their_heads_len != 1) {
-		giterr_set(GITERR_MERGE, "Can only merge a single branch");
+		giterr_set(GITERR_MERGE, "can only merge a single branch");
 		error = -1;
 		goto done;
 	}
@@ -3119,7 +3119,7 @@
 	assert(repo && their_heads);
 
 	if (their_heads_len != 1) {
-		giterr_set(GITERR_MERGE, "Can only merge a single branch");
+		giterr_set(GITERR_MERGE, "can only merge a single branch");
 		return -1;
 	}
 
diff --git a/src/merge_driver.c b/src/merge_driver.c
index 88a53ec..0f35d23 100644
--- a/src/merge_driver.c
+++ b/src/merge_driver.c
@@ -32,6 +32,35 @@
 
 static void git_merge_driver_global_shutdown(void);
 
+const git_repository* git_merge_driver_source_repo(const git_merge_driver_source *src)
+{
+	assert(src);
+	return src->repo;
+}
+
+const git_index_entry* git_merge_driver_source_ancestor(const git_merge_driver_source *src)
+{
+	assert(src);
+	return src->ancestor;
+}
+
+const git_index_entry* git_merge_driver_source_ours(const git_merge_driver_source *src)
+{
+	assert(src);
+	return src->ours;
+}
+
+const git_index_entry* git_merge_driver_source_theirs(const git_merge_driver_source *src)
+{
+	assert(src);
+	return src->theirs;
+}
+
+const git_merge_file_options* git_merge_driver_source_file_options(const git_merge_driver_source *src)
+{
+	assert(src);
+	return src->file_opts;
+}
 
 int git_merge_driver__builtin_apply(
 	git_merge_driver *self,
diff --git a/src/merge_file.c b/src/merge_file.c
index 3f14a4f..5ecd8f4 100644
--- a/src/merge_file.c
+++ b/src/merge_file.c
@@ -127,7 +127,7 @@
 
 	if ((xdl_result = xdl_merge(&ancestor_mmfile, &our_mmfile,
 		&their_mmfile, &xmparam, &mmbuffer)) < 0) {
-		giterr_set(GITERR_MERGE, "Failed to merge files.");
+		giterr_set(GITERR_MERGE, "failed to merge files");
 		error = -1;
 		goto done;
 	}
diff --git a/src/mwindow.c b/src/mwindow.c
index 8a5b5ca..7bb9dbb 100644
--- a/src/mwindow.c
+++ b/src/mwindow.c
@@ -14,8 +14,6 @@
 #include "strmap.h"
 #include "pack.h"
 
-GIT__USE_STRMAP
-
 #define DEFAULT_WINDOW_SIZE \
 	(sizeof(void*) >= 8 \
 		? 1 * 1024 * 1024 * 1024 \
@@ -84,7 +82,7 @@
 
 	git_atomic_inc(&pack->refcount);
 
-	git_strmap_insert(git__pack_cache, pack->pack_name, pack, error);
+	git_strmap_insert(git__pack_cache, pack->pack_name, pack, &error);
 	git_mutex_unlock(&git__mwindow_mutex);
 
 	if (error < 0) {
@@ -231,7 +229,7 @@
 	}
 
 	if (!lru_w) {
-		giterr_set(GITERR_OS, "Failed to close memory window. Couldn't find LRU");
+		giterr_set(GITERR_OS, "failed to close memory window; couldn't find LRU");
 		return -1;
 	}
 
diff --git a/src/netops.c b/src/netops.c
index 90326ea..4b73baa 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -144,7 +144,7 @@
 		default_port = "80";
 
 		if (data->use_ssl) {
-			giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed");
+			giterr_set(GITERR_NET, "redirect from HTTPS to HTTP is not allowed");
 			goto cleanup;
 		}
 	} else if (!git__prefixcmp(url, prefix_https)) {
@@ -155,7 +155,7 @@
 		default_port = data->use_ssl ? "443" : "80";
 
 	if (!default_port) {
-		giterr_set(GITERR_NET, "Unrecognized URL prefix");
+		giterr_set(GITERR_NET, "unrecognized URL prefix");
 		goto cleanup;
 	}
 
@@ -187,7 +187,7 @@
 
 		/* Check for errors in the resulting data */
 		if (original_host && url[0] != '/' && strcmp(original_host, data->host)) {
-			giterr_set(GITERR_NET, "Cross host redirect not allowed");
+			giterr_set(GITERR_NET, "cross host redirect not allowed");
 			error = -1;
 		}
 	}
@@ -237,7 +237,7 @@
 	const char *_host, *_port, *_path, *_userinfo;
 
 	if (http_parser_parse_url(url, strlen(url), false, &u)) {
-		giterr_set(GITERR_NET, "Malformed URL '%s'", url);
+		giterr_set(GITERR_NET, "malformed URL '%s'", url);
 		return GIT_EINVALIDSPEC;
 	}
 
diff --git a/src/notes.c b/src/notes.c
index fe8d216..75108b9 100644
--- a/src/notes.c
+++ b/src/notes.c
@@ -15,7 +15,7 @@
 
 static int note_error_notfound(void)
 {
-	giterr_set(GITERR_INVALID, "Note could not be found");
+	giterr_set(GITERR_INVALID, "note could not be found");
 	return GIT_ENOTFOUND;
 }
 
@@ -226,7 +226,7 @@
 	GIT_UNUSED(note_oid);
 	GIT_UNUSED(fanout);
 
-	giterr_set(GITERR_REPOSITORY, "Object '%s' has no note", annotated_object_sha);
+	giterr_set(GITERR_REPOSITORY, "object '%s' has no note", annotated_object_sha);
 	return current_error;
 }
 
@@ -244,7 +244,7 @@
 	GIT_UNUSED(note_oid);
 	GIT_UNUSED(fanout);
 
-	giterr_set(GITERR_REPOSITORY, "Note for '%s' exists already", annotated_object_sha);
+	giterr_set(GITERR_REPOSITORY, "note for '%s' exists already", annotated_object_sha);
 	return current_error;
 }
 
diff --git a/src/object.c b/src/object.c
index 1d45f9f..bd87c93 100644
--- a/src/object.c
+++ b/src/object.c
@@ -16,6 +16,7 @@
 #include "tag.h"
 
 bool git_object__strict_input_validation = true;
+bool git_object__synchronous_writing = false;
 
 typedef struct {
 	const char	*str;	/* type name string */
@@ -66,12 +67,12 @@
 	/* Validate type match */
 	if (type != GIT_OBJ_ANY && type != odb_obj->cached.type) {
 		giterr_set(GITERR_INVALID,
-			"The requested type does not match the type in the ODB");
+			"the requested type does not match the type in the ODB");
 		return GIT_ENOTFOUND;
 	}
 
 	if ((object_size = git_object__size(odb_obj->cached.type)) == 0) {
-		giterr_set(GITERR_INVALID, "The requested type is invalid");
+		giterr_set(GITERR_INVALID, "the requested type is invalid");
 		return GIT_ENOTFOUND;
 	}
 
@@ -122,7 +123,7 @@
 	assert(repo && object_out && id);
 
 	if (len < GIT_OID_MINPREFIXLEN) {
-		giterr_set(GITERR_OBJECT, "Ambiguous lookup - OID prefix is too short");
+		giterr_set(GITERR_OBJECT, "ambiguous lookup - OID prefix is too short");
 		return GIT_EAMBIGUOUS;
 	}
 
@@ -147,7 +148,7 @@
 				if (type != GIT_OBJ_ANY && type != object->cached.type) {
 					git_object_free(object);
 					giterr_set(GITERR_INVALID,
-						"The requested type does not match the type in ODB");
+						"the requested type does not match the type in ODB");
 					return GIT_ENOTFOUND;
 				}
 
@@ -292,7 +293,7 @@
 	git_oid_fmt(hex_oid, oid);
 	hex_oid[GIT_OID_HEXSZ] = '\0';
 
-	giterr_set(GITERR_OBJECT, "The git_object of id '%s' can not be "
+	giterr_set(GITERR_OBJECT, "the git_object of id '%s' can not be "
 		"successfully peeled into a %s (git_otype=%i).", hex_oid, type_name, type);
 
 	return error;
diff --git a/src/object.h b/src/object.h
index dd227d1..13117e4 100644
--- a/src/object.h
+++ b/src/object.h
@@ -10,6 +10,7 @@
 #include "repository.h"
 
 extern bool git_object__strict_input_validation;
+extern bool git_object__synchronous_writing;
 
 /** Base git object for inheritance */
 struct git_object {
diff --git a/src/odb.c b/src/odb.c
index acf4dea..cf321f5 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -176,7 +176,7 @@
 	int error = 0;
 
 	if (!git_object_typeisloose(type)) {
-		giterr_set(GITERR_INVALID, "Invalid object type for hash");
+		giterr_set(GITERR_INVALID, "invalid object type for hash");
 		return -1;
 	}
 
@@ -199,7 +199,7 @@
 	 * If size is not zero, the file was truncated after we originally
 	 * stat'd it, so we consider this a read failure too */
 	if (read_len < 0 || size > 0) {
-		giterr_set(GITERR_OS, "Error reading file for hashing");
+		giterr_set(GITERR_OS, "error reading file for hashing");
 		error = -1;
 
 		goto done;
@@ -251,7 +251,7 @@
 		return -1;
 
 	if (!git__is_int(st.st_size) || (int)st.st_size < 0) {
-		giterr_set(GITERR_FILESYSTEM, "File size overflow for 32-bit systems");
+		giterr_set(GITERR_FILESYSTEM, "file size overflow for 32-bit systems");
 		return -1;
 	}
 
@@ -269,7 +269,7 @@
 		read_len = p_readlink(path, link_data, size);
 		link_data[size] = '\0';
 		if (read_len != size) {
-			giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);
+			giterr_set(GITERR_OS, "failed to read symlink data for '%s'", path);
 			git__free(link_data);
 			return -1;
 		}
@@ -295,7 +295,7 @@
 		return fd;
 
 	if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) {
-		giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
+		giterr_set(GITERR_OS, "file size overflow for 32-bit systems");
 		p_close(fd);
 		return -1;
 	}
@@ -475,7 +475,7 @@
 static int git_odb__error_unsupported_in_backend(const char *action)
 {
 	giterr_set(GITERR_ODB,
-		"Cannot %s - unsupported in the loaded odb backends", action);
+		"cannot %s - unsupported in the loaded odb backends", action);
 	return -1;
 }
 
@@ -492,11 +492,11 @@
 		return 0;
 	}
 
-	giterr_set(GITERR_ODB, "No ODB backend loaded at index %" PRIuZ, pos);
+	giterr_set(GITERR_ODB, "no ODB backend loaded at index %" PRIuZ, pos);
 	return GIT_ENOTFOUND;
 }
 
-static int add_default_backends(
+int git_odb__add_default_backends(
 	git_odb *db, const char *objects_dir,
 	bool as_alternates, int alternate_depth)
 {
@@ -517,7 +517,7 @@
 		if (as_alternates)
 			return 0;
 
-		giterr_set(GITERR_ODB, "Failed to load object database in '%s'", objects_dir);
+		giterr_set(GITERR_ODB, "failed to load object database in '%s'", objects_dir);
 		return -1;
 	}
 
@@ -531,7 +531,7 @@
 #endif
 
 	/* add the loose object backend */
-	if (git_odb_backend_loose(&loose, objects_dir, -1, 0, 0, 0) < 0 ||
+	if (git_odb_backend_loose(&loose, objects_dir, -1, db->do_fsync, 0, 0) < 0 ||
 		add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0)
 		return -1;
 
@@ -586,7 +586,7 @@
 			alternate = git_buf_cstr(&alternates_path);
 		}
 
-		if ((result = add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0)
+		if ((result = git_odb__add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0)
 			break;
 	}
 
@@ -598,7 +598,7 @@
 
 int git_odb_add_disk_alternate(git_odb *odb, const char *path)
 {
-	return add_default_backends(odb, path, true, 0);
+	return git_odb__add_default_backends(odb, path, true, 0);
 }
 
 int git_odb_open(git_odb **out, const char *objects_dir)
@@ -612,7 +612,7 @@
 	if (git_odb_new(&db) < 0)
 		return -1;
 
-	if (add_default_backends(db, objects_dir, 0, 0) < 0) {
+	if (git_odb__add_default_backends(db, objects_dir, 0, 0) < 0) {
 		git_odb_free(db);
 		return -1;
 	}
@@ -621,6 +621,24 @@
 	return 0;
 }
 
+int git_odb__set_caps(git_odb *odb, int caps)
+{
+	if (caps == GIT_ODB_CAP_FROM_OWNER) {
+		git_repository *repo = odb->rc.owner;
+		int val;
+
+		if (!repo) {
+			giterr_set(GITERR_ODB, "cannot access repository to set odb caps");
+			return -1;
+		}
+
+		if (!git_repository__cvar(&val, repo, GIT_CVAR_FSYNCOBJECTFILES))
+			odb->do_fsync = !!val;
+	}
+
+	return 0;
+}
+
 static void odb_free(git_odb *db)
 {
 	size_t i;
@@ -695,7 +713,7 @@
 	return (int)found;
 }
 
-static int odb_freshen(git_odb *db, const git_oid *id)
+int git_odb__freshen(git_odb *db, const git_oid *id)
 {
 	assert(db && id);
 
@@ -1167,7 +1185,7 @@
 	assert(oid && db);
 
 	git_odb_hash(oid, data, len, type);
-	if (odb_freshen(db, oid))
+	if (git_odb__freshen(db, oid))
 		return 0;
 
 	for (i = 0; i < db->backends.length && error < 0; ++i) {
@@ -1264,7 +1282,7 @@
 	const char *action)
 {
 	giterr_set(GITERR_ODB,
-		"Cannot %s - "
+		"cannot %s - "
 		"Invalid length. %"PRIuZ" was expected. The "
 		"total size of the received chunks amounts to %"PRIuZ".",
 		action, stream->declared_size, stream->received_bytes);		
@@ -1293,7 +1311,7 @@
 
 	git_hash_final(out, stream->hash_ctx);
 
-	if (odb_freshen(stream->backend->odb, out))
+	if (git_odb__freshen(stream->backend->odb, out))
 		return 0;
 
 	return stream->finalize_write(stream, out);
@@ -1399,17 +1417,17 @@
 	if (oid != NULL) {
 		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);
+		giterr_set(GITERR_ODB, "object not found - %s (%.*s)",
+			message, (int) oid_len, oid_str);
 	} else
-		giterr_set(GITERR_ODB, "Object not found - %s", message);
+		giterr_set(GITERR_ODB, "object not found - %s", message);
 
 	return GIT_ENOTFOUND;
 }
 
 int git_odb__error_ambiguous(const char *message)
 {
-	giterr_set(GITERR_ODB, "Ambiguous SHA1 prefix - %s", message);
+	giterr_set(GITERR_ODB, "ambiguous SHA1 prefix - %s", message);
 	return GIT_EAMBIGUOUS;
 }
 
diff --git a/src/odb.h b/src/odb.h
index 31a9fd1..4f548bb 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -38,8 +38,25 @@
 	git_refcount rc;
 	git_vector backends;
 	git_cache own_cache;
+	unsigned int do_fsync :1;
 };
 
+typedef enum {
+	GIT_ODB_CAP_FROM_OWNER = -1,
+} git_odb_cap_t;
+
+/*
+ * Set the capabilities for the object database.
+ */
+int git_odb__set_caps(git_odb *odb, int caps);
+
+/*
+ * Add the default loose and packed backends for a database.
+ */
+int git_odb__add_default_backends(
+	git_odb *db, const char *objects_dir,
+	bool as_alternates, int alternate_depth);
+
 /*
  * Hash a git_rawobj internally.
  * The `git_rawobj` is supposed to be previously initialized
@@ -98,6 +115,9 @@
 	git_odb_object **out, size_t *len_p, git_otype *type_p,
 	git_odb *db, const git_oid *id);
 
+/* freshen an entry in the object database */
+int git_odb__freshen(git_odb *db, const git_oid *id);
+
 /* fully free the object; internal method, DO NOT EXPORT */
 void git_odb_object__free(void *object);
 
diff --git a/src/odb_loose.c b/src/odb_loose.c
index f312b9c..e14af4f 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -14,6 +14,7 @@
 #include "odb.h"
 #include "delta.h"
 #include "filebuf.h"
+#include "object.h"
 
 #include "git2/odb_backend.h"
 #include "git2/types.h"
@@ -214,7 +215,7 @@
 	inflateEnd(s);
 
 	if ((status != Z_STREAM_END) || (s->avail_in != 0)) {
-		giterr_set(GITERR_ZLIB, "Failed to finish ZLib inflation. Stream aborted prematurely");
+		giterr_set(GITERR_ZLIB, "failed to finish zlib inflation; stream aborted prematurely");
 		return -1;
 	}
 
@@ -243,7 +244,7 @@
 	zs.avail_in = (uInt)inlen;
 
 	if (inflateInit(&zs) < Z_OK) {
-		giterr_set(GITERR_ZLIB, "Failed to inflate buffer");
+		giterr_set(GITERR_ZLIB, "failed to inflate buffer");
 		return -1;
 	}
 
@@ -255,7 +256,7 @@
 	if (status != Z_STREAM_END /* || zs.avail_in != 0 */ ||
 		zs.total_out != outlen)
 	{
-		giterr_set(GITERR_ZLIB, "Failed to inflate buffer. Stream aborted prematurely");
+		giterr_set(GITERR_ZLIB, "failed to inflate buffer; stream aborted prematurely");
 		return -1;
 	}
 
@@ -319,7 +320,7 @@
 	 */
 	if ((used = get_binary_object_header(&hdr, obj)) == 0 ||
 		!git_object_typeisloose(hdr.type)) {
-		giterr_set(GITERR_ODB, "Failed to inflate loose object.");
+		giterr_set(GITERR_ODB, "failed to inflate loose object");
 		return -1;
 	}
 
@@ -366,7 +367,7 @@
 		(used = get_object_header(&hdr, head)) == 0 ||
 		!git_object_typeisloose(hdr.type))
 	{
-		giterr_set(GITERR_ODB, "Failed to inflate disk object.");
+		giterr_set(GITERR_ODB, "failed to inflate disk object");
 		return -1;
 	}
 
@@ -455,7 +456,7 @@
 		|| get_object_header(&header_obj, inflated_buffer) == 0
 		|| git_object_typeisloose(header_obj.type) == 0)
 	{
-		giterr_set(GITERR_ZLIB, "Failed to read loose object header");
+		giterr_set(GITERR_ZLIB, "failed to read loose object header");
 		error = -1;
 	} else {
 		out->len = header_obj.size;
@@ -838,6 +839,17 @@
 	git__free(stream);
 }
 
+static int filebuf_flags(loose_backend *backend)
+{
+	int flags = GIT_FILEBUF_TEMPORARY |
+		(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT);
+
+	if (backend->fsync_object_files || git_object__synchronous_writing)
+		flags |= GIT_FILEBUF_FSYNC;
+
+	return flags;
+}
+
 static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, git_off_t length, git_otype type)
 {
 	loose_backend *backend;
@@ -864,9 +876,7 @@
 	stream->stream.mode = GIT_STREAM_WRONLY;
 
 	if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
-		git_filebuf_open(&stream->fbuf, tmp_path.ptr,
-			GIT_FILEBUF_TEMPORARY |
-			(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT),
+		git_filebuf_open(&stream->fbuf, tmp_path.ptr, filebuf_flags(backend),
 			backend->object_file_mode) < 0 ||
 		stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
 	{
@@ -894,9 +904,7 @@
 	header_len = git_odb__format_object_header(header, sizeof(header), len, type);
 
 	if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
-		git_filebuf_open(&fbuf, final_path.ptr,
-			GIT_FILEBUF_TEMPORARY |
-			(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT),
+		git_filebuf_open(&fbuf, final_path.ptr, filebuf_flags(backend),
 			backend->object_file_mode) < 0)
 	{
 		error = -1;
diff --git a/src/odb_mempack.c b/src/odb_mempack.c
index 594a278..d6f2fb4 100644
--- a/src/odb_mempack.c
+++ b/src/odb_mempack.c
@@ -18,13 +18,11 @@
 #include "git2/types.h"
 #include "git2/pack.h"
 
-GIT__USE_OIDMAP
-
 struct memobject {
 	git_oid oid;
 	size_t len;
 	git_otype type;
-	char data[];
+	char data[GIT_FLEX_ARRAY];
 };
 
 struct memory_packer_db {
@@ -41,7 +39,7 @@
 	size_t alloc_len;
 	int rval;
 
-	pos = kh_put(oid, db->objects, oid, &rval);
+	pos = git_oidmap_put(db->objects, oid, &rval);
 	if (rval < 0)
 		return -1;
 
@@ -57,8 +55,8 @@
 	obj->len = len;
 	obj->type = type;
 
-	kh_key(db->objects, pos) = &obj->oid;
-	kh_val(db->objects, pos) = obj;
+	git_oidmap_set_key_at(db->objects, pos, &obj->oid);
+	git_oidmap_set_value_at(db->objects, pos, obj);
 
 	if (type == GIT_OBJ_COMMIT) {
 		struct memobject **store = git_array_alloc(db->commits);
@@ -72,13 +70,8 @@
 static int impl__exists(git_odb_backend *backend, const git_oid *oid)
 {
 	struct memory_packer_db *db = (struct memory_packer_db *)backend;
-	khiter_t pos;
 
-	pos = kh_get(oid, db->objects, oid);
-	if (pos != kh_end(db->objects))
-		return 1;
-
-	return 0;
+	return git_oidmap_exists(db->objects, oid);
 }
 
 static int impl__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
@@ -87,11 +80,11 @@
 	struct memobject *obj = NULL;
 	khiter_t pos;
 
-	pos = kh_get(oid, db->objects, oid);
-	if (pos == kh_end(db->objects))
+	pos = git_oidmap_lookup_index(db->objects, oid);
+	if (!git_oidmap_valid_index(db->objects, pos))
 		return GIT_ENOTFOUND;
 
-	obj = kh_val(db->objects, pos);
+	obj = git_oidmap_value_at(db->objects, pos);
 
 	*len_p = obj->len;
 	*type_p = obj->type;
@@ -108,11 +101,11 @@
 	struct memobject *obj = NULL;
 	khiter_t pos;
 
-	pos = kh_get(oid, db->objects, oid);
-	if (pos == kh_end(db->objects))
+	pos = git_oidmap_lookup_index(db->objects, oid);
+	if (!git_oidmap_valid_index(db->objects, pos))
 		return GIT_ENOTFOUND;
 
-	obj = kh_val(db->objects, pos);
+	obj = git_oidmap_value_at(db->objects, pos);
 
 	*len_p = obj->len;
 	*type_p = obj->type;
@@ -149,7 +142,7 @@
 	struct memory_packer_db *db = (struct memory_packer_db *)_backend;
 	struct memobject *object = NULL;
 
-	kh_foreach_value(db->objects, object, {
+	git_oidmap_foreach_value(db->objects, object, {
 		git__free(object);
 	});
 
@@ -177,6 +170,7 @@
 
 	db->objects = git_oidmap_alloc();
 
+	db->parent.version = GIT_ODB_BACKEND_VERSION;
 	db->parent.read = &impl__read;
 	db->parent.write = &impl__write;
 	db->parent.read_header = &impl__read_header;
diff --git a/src/odb_pack.c b/src/odb_pack.c
index b80d033..51770a8 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -428,7 +428,7 @@
 			git_oid_cpy(out_oid, short_oid);
 	} else {
 		struct git_pack_entry e;
-		git_rawobj raw;
+		git_rawobj raw = {NULL};
 
 		if ((error = pack_entry_find_prefix(
 				&e, (struct pack_backend *)backend, short_oid, len)) == 0 &&
diff --git a/src/offmap.c b/src/offmap.c
new file mode 100644
index 0000000..ab66496
--- /dev/null
+++ b/src/offmap.c
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#include "offmap.h"
+
+__KHASH_IMPL(off, static kh_inline, git_off_t, void *, 1, kh_int64_hash_func, kh_int64_hash_equal)
+
+git_offmap *git_offmap_alloc(void)
+{
+	return kh_init(off);
+}
+
+void git_offmap_free(git_offmap *map)
+{
+	kh_destroy(off, map);
+}
+
+void git_offmap_clear(git_offmap *map)
+{
+	kh_clear(off, map);
+}
+
+size_t git_offmap_num_entries(git_offmap *map)
+{
+	return kh_size(map);
+}
+
+size_t git_offmap_lookup_index(git_offmap *map, const git_off_t key)
+{
+	return kh_get(off, map, key);
+}
+
+int git_offmap_valid_index(git_offmap *map, size_t idx)
+{
+	return idx != kh_end(map);
+}
+
+int git_offmap_exists(git_offmap *map, const git_off_t key)
+{
+	return kh_get(off, map, key) != kh_end(map);
+}
+
+void *git_offmap_value_at(git_offmap *map, size_t idx)
+{
+	return kh_val(map, idx);
+}
+
+void git_offmap_set_value_at(git_offmap *map, size_t idx, void *value)
+{
+	kh_val(map, idx) = value;
+}
+
+void git_offmap_delete_at(git_offmap *map, size_t idx)
+{
+	kh_del(off, map, idx);
+}
+
+int git_offmap_put(git_offmap *map, const git_off_t key, int *err)
+{
+	return kh_put(off, map, key, err);
+}
+
+void git_offmap_insert(git_offmap *map, const git_off_t key, void *value, int *rval)
+{
+	khiter_t idx = kh_put(off, map, key, rval);
+
+	if ((*rval) >= 0) {
+		if ((*rval) == 0)
+			kh_key(map, idx) = key;
+		kh_val(map, idx) = value;
+	}
+}
+
+void git_offmap_delete(git_offmap *map, const git_off_t key)
+{
+	khiter_t idx = git_offmap_lookup_index(map, key);
+	if (git_offmap_valid_index(map, idx))
+		git_offmap_delete_at(map, idx);
+}
diff --git a/src/offmap.h b/src/offmap.h
index 0d0e512..f9d2483 100644
--- a/src/offmap.h
+++ b/src/offmap.h
@@ -20,45 +20,24 @@
 __KHASH_TYPE(off, git_off_t, void *)
 typedef khash_t(off) git_offmap;
 
-#define GIT__USE_OFFMAP \
-	__KHASH_IMPL(off, static kh_inline, git_off_t, void *, 1, kh_int64_hash_func, kh_int64_hash_equal)
+git_offmap *git_offmap_alloc(void);
+void git_offmap_free(git_offmap *map);
+void git_offmap_clear(git_offmap *map);
 
-#define git_offmap_alloc()  kh_init(off)
-#define git_offmap_free(h)  kh_destroy(off, h), h = NULL
-#define git_offmap_clear(h) kh_clear(off, h)
+size_t git_offmap_num_entries(git_offmap *map);
 
-#define git_offmap_num_entries(h) kh_size(h)
+size_t git_offmap_lookup_index(git_offmap *map, const git_off_t key);
+int git_offmap_valid_index(git_offmap *map, size_t idx);
 
-#define git_offmap_lookup_index(h, k)  kh_get(off, h, k)
-#define git_offmap_valid_index(h, idx) (idx != kh_end(h))
+int git_offmap_exists(git_offmap *map, const git_off_t key);
 
-#define git_offmap_exists(h, k) (kh_get(off, h, k) != kh_end(h))
+void *git_offmap_value_at(git_offmap *map, size_t idx);
+void git_offmap_set_value_at(git_offmap *map, size_t idx, void *value);
+void git_offmap_delete_at(git_offmap *map, size_t idx);
 
-#define git_offmap_value_at(h, idx)        kh_val(h, idx)
-#define git_offmap_set_value_at(h, idx, v) kh_val(h, idx) = v
-#define git_offmap_delete_at(h, idx)       kh_del(off, h, idx)
-
-#define git_offmap_insert(h, key, val, rval) do { \
-	khiter_t __pos = kh_put(off, h, key, &rval); \
-	if (rval >= 0) { \
-		if (rval == 0) kh_key(h, __pos) = key; \
-		kh_val(h, __pos) = val; \
-	} } while (0)
-
-#define git_offmap_insert2(h, key, val, oldv, rval) do { \
-	khiter_t __pos = kh_put(off, h, key, &rval); \
-	if (rval >= 0) { \
-		if (rval == 0) { \
-			oldv = kh_val(h, __pos); \
-			kh_key(h, __pos) = key; \
-		} else { oldv = NULL; } \
-		kh_val(h, __pos) = val; \
-	} } while (0)
-
-#define git_offmap_delete(h, key) do { \
-	khiter_t __pos = git_offmap_lookup_index(h, key); \
-	if (git_offmap_valid_index(h, __pos)) \
-		git_offmap_delete_at(h, __pos); } while (0)
+int git_offmap_put(git_offmap *map, const git_off_t key, int *err);
+void git_offmap_insert(git_offmap *map, const git_off_t key, void *value, int *rval);
+void git_offmap_delete(git_offmap *map, const git_off_t key);
 
 #define git_offmap_foreach		kh_foreach
 #define git_offmap_foreach_value	kh_foreach_value
diff --git a/src/oid.c b/src/oid.c
index 9fe2ebb..9dc7191 100644
--- a/src/oid.c
+++ b/src/oid.c
@@ -16,7 +16,7 @@
 
 static int oid_error_invalid(const char *msg)
 {
-	giterr_set(GITERR_INVALID, "Unable to parse OID - %s", msg);
+	giterr_set(GITERR_INVALID, "unable to parse OID - %s", msg);
 	return -1;
 }
 
@@ -380,7 +380,7 @@
 	node_index idx;
 
 	if (os->full) {
-		giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full");
+		giterr_set(GITERR_INVALID, "unable to shorten OID - OID set full");
 		return -1;
 	}
 
@@ -395,7 +395,7 @@
 		trie_node *node;
 
 		if (c == -1) {
-			giterr_set(GITERR_INVALID, "Unable to shorten OID - invalid hex value");
+			giterr_set(GITERR_INVALID, "unable to shorten OID - invalid hex value");
 			return -1;
 		}
 
@@ -410,7 +410,7 @@
 			node = push_leaf(os, idx, git__fromhex(tail[0]), &tail[1]);
 			if (node == NULL) {
 				if (os->full)
-					giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full");
+					giterr_set(GITERR_INVALID, "unable to shorten OID - OID set full");
 				return -1;
 			}
 		}
@@ -418,7 +418,7 @@
 		if (node->children[c] == 0) {
 			if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL) {
 				if (os->full)
-					giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full");
+					giterr_set(GITERR_INVALID, "unable to shorten OID - OID set full");
 				return -1;
 			}
 			break;
diff --git a/src/oidmap.c b/src/oidmap.c
new file mode 100644
index 0000000..5f156a1
--- /dev/null
+++ b/src/oidmap.c
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#include "oidmap.h"
+
+GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid)
+{
+	khint_t h;
+	memcpy(&h, oid, sizeof(khint_t));
+	return h;
+}
+
+__KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, git_oidmap_hash, git_oid_equal)
+
+git_oidmap *git_oidmap_alloc()
+{
+	return kh_init(oid);
+}
+
+void git_oidmap_free(git_oidmap *map)
+{
+	kh_destroy(oid, map);
+}
+
+void git_oidmap_clear(git_oidmap *map)
+{
+	kh_clear(oid, map);
+}
+
+size_t git_oidmap_size(git_oidmap *map)
+{
+	return kh_size(map);
+}
+
+size_t git_oidmap_lookup_index(git_oidmap *map, const git_oid *key)
+{
+	return kh_get(oid, map, key);
+}
+
+int git_oidmap_valid_index(git_oidmap *map, size_t idx)
+{
+	return idx != kh_end(map);
+}
+
+int git_oidmap_exists(git_oidmap *map, const git_oid *key)
+{
+	return kh_get(oid, map, key) != kh_end(map);
+}
+
+int git_oidmap_has_data(git_oidmap *map, size_t idx)
+{
+	return kh_exist(map, idx);
+}
+
+const git_oid *git_oidmap_key(git_oidmap *map, size_t idx)
+{
+	return kh_key(map, idx);
+}
+
+void git_oidmap_set_key_at(git_oidmap *map, size_t idx, git_oid *key)
+{
+	kh_key(map, idx) = key;
+}
+
+void *git_oidmap_value_at(git_oidmap *map, size_t idx)
+{
+	return kh_val(map, idx);
+}
+
+void git_oidmap_set_value_at(git_oidmap *map, size_t idx, void *value)
+{
+	kh_val(map, idx) = value;
+}
+
+void git_oidmap_delete_at(git_oidmap *map, size_t idx)
+{
+	kh_del(oid, map, idx);
+}
+
+int git_oidmap_put(git_oidmap *map, const git_oid *key, int *err)
+{
+	return kh_put(oid, map, key, err);
+}
+
+void git_oidmap_insert(git_oidmap *map, const git_oid *key, void *value, int *rval)
+{
+	khiter_t idx = kh_put(oid, map, key, rval);
+
+	if ((*rval) >= 0) {
+		if ((*rval) == 0)
+			kh_key(map, idx) = key;
+		kh_val(map, idx) = value;
+	}
+}
+
+void git_oidmap_delete(git_oidmap *map, const git_oid *key)
+{
+	khiter_t idx = git_oidmap_lookup_index(map, key);
+	if (git_oidmap_valid_index(map, idx))
+		git_oidmap_delete_at(map, idx);
+}
diff --git a/src/oidmap.h b/src/oidmap.h
index 2cf208f..5632224 100644
--- a/src/oidmap.h
+++ b/src/oidmap.h
@@ -20,35 +20,31 @@
 __KHASH_TYPE(oid, const git_oid *, void *)
 typedef khash_t(oid) git_oidmap;
 
-GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid)
-{
-	khint_t h;
-	memcpy(&h, oid, sizeof(khint_t));
-	return h;
-}
+git_oidmap *git_oidmap_alloc(void);
+void git_oidmap_free(git_oidmap *map);
+void git_oidmap_clear(git_oidmap *map);
 
-#define GIT__USE_OIDMAP \
-	__KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, git_oidmap_hash, git_oid_equal)
+size_t git_oidmap_size(git_oidmap *map);
 
-#define git_oidmap_alloc() kh_init(oid)
-#define git_oidmap_free(h) kh_destroy(oid,h), h = NULL
+size_t git_oidmap_lookup_index(git_oidmap *map, const git_oid *key);
+int git_oidmap_valid_index(git_oidmap *map, size_t idx);
 
-#define git_oidmap_lookup_index(h, k) kh_get(oid, h, k)
-#define git_oidmap_valid_index(h, idx) (idx != kh_end(h))
+int git_oidmap_exists(git_oidmap *map, const git_oid *key);
+int git_oidmap_has_data(git_oidmap *map, size_t idx);
 
-#define git_oidmap_value_at(h, idx) kh_val(h, idx)
+const git_oid *git_oidmap_key(git_oidmap *map, size_t idx);
+void git_oidmap_set_key_at(git_oidmap *map, size_t idx, git_oid *key);
+void *git_oidmap_value_at(git_oidmap *map, size_t idx);
+void git_oidmap_set_value_at(git_oidmap *map, size_t idx, void *value);
+void git_oidmap_delete_at(git_oidmap *map, size_t idx);
 
-#define git_oidmap_insert(h, key, val, rval) do { \
-	khiter_t __pos = kh_put(oid, h, key, &rval); \
-	if (rval >= 0) { \
-		if (rval == 0) kh_key(h, __pos) = key; \
-		kh_val(h, __pos) = val; \
-	} } while (0)
+int git_oidmap_put(git_oidmap *map, const git_oid *key, int *err);
+void git_oidmap_insert(git_oidmap *map, const git_oid *key, void *value, int *rval);
+void git_oidmap_delete(git_oidmap *map, const git_oid *key);
 
 #define git_oidmap_foreach_value kh_foreach_value
 
-#define git_oidmap_size(h) kh_size(h)
-
-#define git_oidmap_clear(h) kh_clear(oid, h)
+#define git_oidmap_begin	kh_begin
+#define git_oidmap_end		kh_end
 
 #endif
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
index b8ab21f..c0a9c3c 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"
@@ -65,18 +66,26 @@
 	CRYPTO_set_locking_callback(NULL);
 
 	for (i = 0; i < num_locks; ++i)
-		git_mutex_free(openssl_locks);
+		git_mutex_free(&openssl_locks[i]);
 	git__free(openssl_locks);
 }
 
 #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);
@@ -148,7 +164,7 @@
 	git__on_shutdown(shutdown_ssl_locking);
 	return 0;
 #else
-	giterr_set(GITERR_THREAD, "libgit2 as not built with threads");
+	giterr_set(GITERR_THREAD, "libgit2 was not built with threads");
 	return -1;
 #endif
 }
@@ -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)
 {
@@ -239,10 +257,10 @@
 	switch (err) {
 	case SSL_ERROR_WANT_CONNECT:
 	case SSL_ERROR_WANT_ACCEPT:
-		giterr_set(GITERR_NET, "SSL error: connection failure\n");
+		giterr_set(GITERR_NET, "SSL error: connection failure");
 		break;
 	case SSL_ERROR_WANT_X509_LOOKUP:
-		giterr_set(GITERR_NET, "SSL error: x509 error\n");
+		giterr_set(GITERR_NET, "SSL error: x509 error");
 		break;
 	case SSL_ERROR_SYSCALL:
 		e = ERR_get_error();
@@ -309,7 +327,7 @@
 	int i = -1,j;
 
 	if (SSL_get_verify_result(ssl) != X509_V_OK) {
-		giterr_set(GITERR_SSL, "The SSL certificate is invalid");
+		giterr_set(GITERR_SSL, "the SSL certificate is invalid");
 		return GIT_ECERTIFICATE;
 	}
 
@@ -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..f5e59da 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 || defined(LIBRESSL_VERSION_NUMBER)
+
+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-objects.c b/src/pack-objects.c
index 9f62322..ef272e8 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -41,8 +41,6 @@
 	git_transfer_progress *stats;
 };
 
-GIT__USE_OIDMAP
-
 #ifdef GIT_THREADS
 
 #define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) do { \
@@ -162,7 +160,7 @@
 		git_mutex_init(&pb->progress_mutex) ||
 		git_cond_init(&pb->progress_cond))
 	{
-		giterr_set(GITERR_OS, "Failed to initialize packbuilder mutex");
+		giterr_set(GITERR_OS, "failed to initialize packbuilder mutex");
 		goto on_error;
 	}
 
@@ -197,10 +195,10 @@
 	size_t i;
 	int ret;
 
-	kh_clear(oid, pb->object_ix);
+	git_oidmap_clear(pb->object_ix);
 	for (i = 0, po = pb->object_list; i < pb->nr_objects; i++, po++) {
-		pos = kh_put(oid, pb->object_ix, &po->id, &ret);
-		kh_value(pb->object_ix, pos) = po;
+		pos = git_oidmap_put(pb->object_ix, &po->id, &ret);
+		git_oidmap_set_value_at(pb->object_ix, pos, po);
 	}
 }
 
@@ -216,8 +214,7 @@
 
 	/* If the object already exists in the hash table, then we don't
 	 * have any work to do */
-	pos = kh_get(oid, pb->object_ix, oid);
-	if (pos != kh_end(pb->object_ix))
+	if (git_oidmap_exists(pb->object_ix, oid))
 		return 0;
 
 	if (pb->nr_objects >= pb->nr_alloc) {
@@ -225,7 +222,7 @@
 		GITERR_CHECK_ALLOC_MULTIPLY(&newsize, newsize, 3 / 2);
 
 		if (!git__is_uint32(newsize)) {
-			giterr_set(GITERR_NOMEMORY, "Packfile too large to fit in memory.");
+			giterr_set(GITERR_NOMEMORY, "packfile too large to fit in memory.");
 			return -1;
 		}
 
@@ -247,13 +244,13 @@
 	git_oid_cpy(&po->id, oid);
 	po->hash = name_hash(name);
 
-	pos = kh_put(oid, pb->object_ix, &po->id, &ret);
+	pos = git_oidmap_put(pb->object_ix, &po->id, &ret);
 	if (ret < 0) {
 		giterr_set_oom();
 		return ret;
 	}
 	assert(ret != 0);
-	kh_value(pb->object_ix, pos) = po;
+	git_oidmap_set_value_at(pb->object_ix, pos, po);
 
 	pb->done = false;
 
@@ -298,7 +295,7 @@
 		goto on_error;
 
 	if (error == GIT_EBUFS || delta_size != po->delta_size) {
-		giterr_set(GITERR_INVALID, "Delta size changed");
+		giterr_set(GITERR_INVALID, "delta size changed");
 		goto on_error;
 	}
 
@@ -517,11 +514,11 @@
 
 	GIT_UNUSED(name);
 
-	pos = kh_get(oid, pb->object_ix, oid);
-	if (pos == kh_end(pb->object_ix))
+	pos = git_oidmap_lookup_index(pb->object_ix, oid);
+	if (!git_oidmap_valid_index(pb->object_ix, pos))
 		return 0;
 
-	po = kh_value(pb->object_ix, pos);
+	po = git_oidmap_value_at(pb->object_ix, pos);
 	po->tagged = 1;
 
 	/* TODO: peel objects */
@@ -808,7 +805,7 @@
 
 		if (sz != trg_size) {
 			giterr_set(GITERR_INVALID,
-				   "Inconsistent target object length");
+				   "inconsistent target object length");
 			return -1;
 		}
 
@@ -830,7 +827,7 @@
 
 		if (sz != src_size) {
 			giterr_set(GITERR_INVALID,
-				   "Inconsistent source object length");
+				   "inconsistent source object length");
 			return -1;
 		}
 
@@ -1388,6 +1385,7 @@
 	git_indexer *indexer;
 	git_transfer_progress stats;
 	struct pack_write_context ctx;
+	int t;
 
 	PREPARE_PACK;
 
@@ -1395,6 +1393,9 @@
 		&indexer, path, mode, pb->odb, progress_cb, progress_cb_payload) < 0)
 		return -1;
 
+	if (!git_repository__cvar(&t, pb->repo, GIT_CVAR_FSYNCOBJECTFILES) && t)
+		git_indexer__set_fsync(indexer, 1);
+
 	ctx.indexer = indexer;
 	ctx.stats = &stats;
 
@@ -1541,7 +1542,7 @@
 		if ((error = lookup_walk_object(&obj, pb, id)) < 0)
 			return error;
 
-		git_oidmap_insert(pb->walk_objects, &obj->id, obj, error);
+		git_oidmap_insert(pb->walk_objects, &obj->id, obj, &error);
 	}
 
 	*out = obj;
@@ -1738,7 +1739,7 @@
 	if (error == GIT_ITEROVER)
 		error = 0;
 
-	return 0;
+	return error;
 }
 
 int git_packbuilder_set_callbacks(git_packbuilder *pb, git_packbuilder_progress progress_cb, void *progress_cb_payload)
diff --git a/src/pack-objects.h b/src/pack-objects.h
index 5a84f41..e1e0ee3 100644
--- a/src/pack-objects.h
+++ b/src/pack-objects.h
@@ -16,6 +16,7 @@
 #include "netops.h"
 #include "zstream.h"
 #include "pool.h"
+#include "indexer.h"
 
 #include "git2/oid.h"
 #include "git2/pack.h"
diff --git a/src/pack.c b/src/pack.c
index 310f00f..60b757e 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -16,9 +16,6 @@
 
 #include <zlib.h>
 
-GIT__USE_OFFMAP
-GIT__USE_OIDMAP
-
 static int packfile_open(struct git_pack_file *p);
 static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n);
 static int packfile_unpack_compressed(
@@ -45,7 +42,7 @@
 
 static int packfile_error(const char *message)
 {
-	giterr_set(GITERR_ODB, "Invalid pack file - %s", message);
+	giterr_set(GITERR_ODB, "invalid pack file - %s", message);
 	return -1;
 }
 
@@ -78,13 +75,12 @@
 
 static void cache_free(git_pack_cache *cache)
 {
-	khiter_t k;
+	git_pack_cache_entry *entry;
 
 	if (cache->entries) {
-		for (k = kh_begin(cache->entries); k != kh_end(cache->entries); k++) {
-			if (kh_exist(cache->entries, k))
-				free_cache_object(kh_value(cache->entries, k));
-		}
+		git_offmap_foreach_value(cache->entries, entry, {
+			free_cache_object(entry);
+		});
 
 		git_offmap_free(cache->entries);
 		cache->entries = NULL;
@@ -99,7 +95,7 @@
 	cache->memory_limit = GIT_PACK_CACHE_MEMORY_LIMIT;
 
 	if (git_mutex_init(&cache->lock)) {
-		giterr_set(GITERR_OS, "Failed to initialize pack cache mutex");
+		giterr_set(GITERR_OS, "failed to initialize pack cache mutex");
 
 		git__free(cache->entries);
 		cache->entries = NULL;
@@ -118,9 +114,9 @@
 	if (git_mutex_lock(&cache->lock) < 0)
 		return NULL;
 
-	k = kh_get(off, cache->entries, offset);
-	if (k != kh_end(cache->entries)) { /* found it */
-		entry = kh_value(cache->entries, k);
+	k = git_offmap_lookup_index(cache->entries, offset);
+	if (git_offmap_valid_index(cache->entries, k)) { /* found it */
+		entry = git_offmap_value_at(cache->entries, k);
 		git_atomic_inc(&entry->refcount);
 		entry->last_usage = cache->use_ctr++;
 	}
@@ -132,21 +128,16 @@
 /* Run with the cache lock held */
 static void free_lowest_entry(git_pack_cache *cache)
 {
+	git_off_t offset;
 	git_pack_cache_entry *entry;
-	khiter_t k;
 
-	for (k = kh_begin(cache->entries); k != kh_end(cache->entries); k++) {
-		if (!kh_exist(cache->entries, k))
-			continue;
-
-		entry = kh_value(cache->entries, k);
-
+	git_offmap_foreach(cache->entries, offset, entry, {
 		if (entry && entry->refcount.val == 0) {
 			cache->memory_used -= entry->raw.len;
-			kh_del(off, cache->entries, k);
+			git_offmap_delete(cache->entries, offset);
 			free_cache_object(entry);
 		}
-	}
+	});
 }
 
 static int cache_add(
@@ -170,14 +161,14 @@
 			return -1;
 		}
 		/* Add it to the cache if nobody else has */
-		exists = kh_get(off, cache->entries, offset) != kh_end(cache->entries);
+		exists = git_offmap_exists(cache->entries, offset);
 		if (!exists) {
 			while (cache->memory_used + base->len > cache->memory_limit)
 				free_lowest_entry(cache);
 
-			k = kh_put(off, cache->entries, offset, &error);
+			k = git_offmap_put(cache->entries, offset, &error);
 			assert(error != 0);
-			kh_value(cache->entries, k) = entry;
+			git_offmap_set_value_at(cache->entries, k, entry);
 			cache->memory_used += entry->raw.len;
 
 			*cached_out = entry;
@@ -226,7 +217,7 @@
 
 	if (p_fstat(fd, &st) < 0) {
 		p_close(fd);
-		giterr_set(GITERR_OS, "Unable to stat pack index '%s'", path);
+		giterr_set(GITERR_OS, "unable to stat pack index '%s'", path);
 		return -1;
 	}
 
@@ -235,7 +226,7 @@
 		(idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20)
 	{
 		p_close(fd);
-		giterr_set(GITERR_ODB, "Invalid pack index '%s'", path);
+		giterr_set(GITERR_ODB, "invalid pack index '%s'", path);
 		return -1;
 	}
 
@@ -509,8 +500,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 +750,11 @@
 	}
 
 cleanup:
-	if (error < 0)
+	if (error < 0) {
 		git__free(obj->data);
+		if (cached)
+			git_atomic_dec(&cached->refcount);
+	}
 
 	if (elem)
 		*obj_offset = curpos;
@@ -957,10 +953,10 @@
 			git_oid oid;
 
 			git_oid_fromraw(&oid, base_info);
-			k = kh_get(oid, p->idx_cache, &oid);
-			if (k != kh_end(p->idx_cache)) {
+			k = git_oidmap_lookup_index(p->idx_cache, &oid);
+			if (git_oidmap_valid_index(p->idx_cache, k)) {
 				*curpos += 20;
-				return ((struct git_pack_entry *)kh_value(p->idx_cache, k))->offset;
+				return ((struct git_pack_entry *)git_oidmap_value_at(p->idx_cache, k))->offset;
 			} else {
 				/* If we're building an index, don't try to find the pack
 				 * entry; we just haven't seen it yet.  We'll make
@@ -986,6 +982,18 @@
  *
  ***********************************************************/
 
+void git_packfile_close(struct git_pack_file *p, bool unlink_packfile)
+{
+	if (p->mwf.fd >= 0) {
+		git_mwindow_free_all_locked(&p->mwf);
+		p_close(p->mwf.fd);
+		p->mwf.fd = -1;
+	}
+
+	if (unlink_packfile)
+		p_unlink(p->pack_name);
+}
+
 void git_packfile_free(struct git_pack_file *p)
 {
 	if (!p)
@@ -993,10 +1001,7 @@
 
 	cache_free(&p->bases);
 
-	if (p->mwf.fd >= 0) {
-		git_mwindow_free_all_locked(&p->mwf);
-		p_close(p->mwf.fd);
-	}
+	git_packfile_close(p, false);
 
 	pack_index_free(p);
 
@@ -1077,7 +1082,7 @@
 	return 0;
 
 cleanup:
-	giterr_set(GITERR_OS, "Invalid packfile '%s'", p->pack_name);
+	giterr_set(GITERR_OS, "invalid packfile '%s'", p->pack_name);
 
 	if (p->mwf.fd >= 0)
 		p_close(p->mwf.fd);
@@ -1153,7 +1158,7 @@
 	p->index_version = -1;
 
 	if (git_mutex_init(&p->lock)) {
-		giterr_set(GITERR_OS, "Failed to initialize packfile mutex");
+		giterr_set(GITERR_OS, "failed to initialize packfile mutex");
 		git__free(p);
 		return -1;
 	}
@@ -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/pack.h b/src/pack.h
index 5302db5..e2bf165 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -149,6 +149,7 @@
 		git_off_t *curpos, git_otype type,
 		git_off_t delta_obj_offset);
 
+void git_packfile_close(struct git_pack_file *p, bool unlink_packfile);
 void git_packfile_free(struct git_pack_file *p);
 int git_packfile_alloc(struct git_pack_file **pack_out, const char *path);
 
diff --git a/src/patch_generate.c b/src/patch_generate.c
index a13f2ff..804fc0e 100644
--- a/src/patch_generate.c
+++ b/src/patch_generate.c
@@ -206,35 +206,14 @@
 		 ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 ||
 		  (patch->nfile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0));
 
-	/* always try to load workdir content first because filtering may
-	 * need 2x data size and this minimizes peak memory footprint
-	 */
-	if (patch->ofile.src == GIT_ITERATOR_TYPE_WORKDIR) {
-		if ((error = git_diff_file_content__load(
-				&patch->ofile, &patch->base.diff_opts)) < 0 ||
-			should_skip_binary(patch, patch->ofile.file))
-			goto cleanup;
-	}
-	if (patch->nfile.src == GIT_ITERATOR_TYPE_WORKDIR) {
-		if ((error = git_diff_file_content__load(
-				&patch->nfile, &patch->base.diff_opts)) < 0 ||
-			should_skip_binary(patch, patch->nfile.file))
-			goto cleanup;
-	}
-
-	/* once workdir has been tried, load other data as needed */
-	if (patch->ofile.src != GIT_ITERATOR_TYPE_WORKDIR) {
-		if ((error = git_diff_file_content__load(
-				&patch->ofile, &patch->base.diff_opts)) < 0 ||
-			should_skip_binary(patch, patch->ofile.file))
-			goto cleanup;
-	}
-	if (patch->nfile.src != GIT_ITERATOR_TYPE_WORKDIR) {
-		if ((error = git_diff_file_content__load(
-				&patch->nfile, &patch->base.diff_opts)) < 0 ||
-			should_skip_binary(patch, patch->nfile.file))
-			goto cleanup;
-	}
+	if ((error = git_diff_file_content__load(
+			&patch->ofile, &patch->base.diff_opts)) < 0 ||
+		should_skip_binary(patch, patch->ofile.file))
+		goto cleanup;
+	if ((error = git_diff_file_content__load(
+			&patch->nfile, &patch->base.diff_opts)) < 0 ||
+		should_skip_binary(patch, patch->nfile.file))
+		goto cleanup;
 
 	/* if previously missing an oid, and now that we have it the two sides
 	 * are the same (and not submodules), update MODIFIED -> UNMODIFIED
@@ -284,7 +263,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 */
@@ -417,58 +396,10 @@
 {
 	if (diff)
 		return 0;
-	giterr_set(GITERR_INVALID, "Must provide valid diff to %s", action);
+	giterr_set(GITERR_INVALID, "must provide valid diff to %s", action);
 	return -1;
 }
 
-int git_diff_foreach(
-	git_diff *diff,
-	git_diff_file_cb file_cb,
-	git_diff_binary_cb binary_cb,
-	git_diff_hunk_cb hunk_cb,
-	git_diff_line_cb data_cb,
-	void *payload)
-{
-	int error = 0;
-	git_xdiff_output xo;
-	size_t idx;
-	git_patch_generated patch;
-
-	if ((error = diff_required(diff, "git_diff_foreach")) < 0)
-		return error;
-
-	memset(&xo, 0, sizeof(xo));
-	memset(&patch, 0, sizeof(patch));
-	diff_output_init(
-		&xo.output, &diff->opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
-	git_xdiff_init(&xo, &diff->opts);
-
-	git_vector_foreach(&diff->deltas, idx, patch.base.delta) {
-
-		/* check flags against patch status */
-		if (git_diff_delta__should_skip(&diff->opts, patch.base.delta))
-			continue;
-
-		if (binary_cb || hunk_cb || data_cb) {
-			if ((error = patch_generated_init(&patch, diff, idx)) != 0 ||
-				(error = patch_generated_load(&patch, &xo.output)) != 0)
-				return error;
-		}
-
-		if ((error = patch_generated_invoke_file_callback(&patch, &xo.output)) == 0) {
-			if (binary_cb || hunk_cb || data_cb)
-					error = patch_generated_create(&patch, &xo.output);
-		}
-
-		git_patch_free(&patch.base);
-
-		if (error)
-			break;
-	}
-
-	return error;
-}
-
 typedef struct {
 	git_patch_generated patch;
 	git_diff_delta delta;
@@ -776,7 +707,7 @@
 
 	delta = git_vector_get(&diff->deltas, idx);
 	if (!delta) {
-		giterr_set(GITERR_INVALID, "Index out of range for delta in diff");
+		giterr_set(GITERR_INVALID, "index out of range for delta in diff");
 		return GIT_ENOTFOUND;
 	}
 
diff --git a/src/patch_parse.c b/src/patch_parse.c
index 5ee09ee..0a9edcd 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 */
@@ -444,10 +444,10 @@
 				goto done;
 
 			parse_advance_ws(ctx);
-			parse_advance_expected_str(ctx, "\n");
 
-			if (ctx->line_len > 0) {
-				error = parse_err("trailing data at line %d", ctx->line_num);
+			if (parse_advance_expected_str(ctx, "\n") < 0 ||
+			    ctx->line_len > 0) {
+				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;
 }
@@ -562,15 +562,16 @@
 	int newlines = hunk->hunk.new_lines;
 
 	for (;
-		ctx->remain_len > 4 && (oldlines || newlines) &&
-		memcmp(ctx->line, "@@ -", 4) != 0;
+		ctx->remain_len > 1 &&
+		(oldlines || newlines) &&
+		(ctx->remain_len <= 4 || memcmp(ctx->line, "@@ -", 4) != 0);
 		parse_advance_line(ctx)) {
 
 		int origin;
 		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 +597,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 +673,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 +716,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 +737,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 +746,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 +755,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 +786,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 +794,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 +803,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 +821,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 +913,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 +1015,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..c3d3eb1 100644
--- a/src/path.c
+++ b/src/path.c
@@ -111,13 +111,41 @@
 }
 
 /*
+ * Determine if the path is a Windows prefix and, if so, returns
+ * its actual lentgh. If it is not a prefix, returns -1.
+ */
+static int win32_prefix_length(const char *path, int len)
+{
+#ifndef GIT_WIN32
+	GIT_UNUSED(path);
+	GIT_UNUSED(len);
+#else
+	/*
+	 * Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return
+	 * 'C:/' here
+	 */
+	if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path))
+		return 2;
+
+	/*
+	 * Similarly checks if we're dealing with a network computer name
+	 * '//computername/.git' will return '//computername/'
+	 */
+	if (looks_like_network_computer_name(path, len))
+		return len;
+#endif
+
+	return -1;
+}
+
+/*
  * Based on the Android implementation, BSD licensed.
  * Check http://android.git.kernel.org/
  */
 int git_path_dirname_r(git_buf *buffer, const char *path)
 {
 	const char *endp;
-	int result, len;
+	int is_prefix = 0, len;
 
 	/* Empty or NULL string gets treated as "." */
 	if (path == NULL || *path == '\0') {
@@ -131,6 +159,11 @@
 	while (endp > path && *endp == '/')
 		endp--;
 
+	if ((len = win32_prefix_length(path, endp - path + 1)) > 0) {
+		is_prefix = 1;
+		goto Exit;
+	}
+
 	/* Find the start of the dir */
 	while (endp > path && *endp != '/')
 		endp--;
@@ -146,35 +179,23 @@
 		endp--;
 	} while (endp > path && *endp == '/');
 
+	if ((len = win32_prefix_length(path, endp - path + 1)) > 0) {
+		is_prefix = 1;
+		goto Exit;
+	}
+
 	/* Cast is safe because max path < max int */
 	len = (int)(endp - path + 1);
 
-#ifdef GIT_WIN32
-	/* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return
-		'C:/' here */
-
-	if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) {
-		len = 3;
-		goto Exit;
-	}
-
-	/* Similarly checks if we're dealing with a network computer name
-		'//computername/.git' will return '//computername/' */
-
-	if (looks_like_network_computer_name(path, len)) {
-		len++;
-		goto Exit;
-	}
-
-#endif
-
 Exit:
-	result = len;
+	if (buffer) {
+		if (git_buf_set(buffer, path, len) < 0)
+			return -1;
+		if (is_prefix && git_buf_putc(buffer, '/') < 0)
+			return -1;
+	}
 
-	if (buffer != NULL && git_buf_set(buffer, path, len) < 0)
-		return -1;
-
-	return result;
+	return len;
 }
 
 
@@ -341,7 +362,7 @@
 	if (p_realpath(path, buf) == NULL) {
 		/* giterr_set resets the errno when dealing with a GITERR_OS kind of error */
 		int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1;
-		giterr_set(GITERR_OS, "Failed to resolve path '%s'", path);
+		giterr_set(GITERR_OS, "failed to resolve path '%s'", path);
 
 		git_buf_clear(path_out);
 
@@ -632,20 +653,24 @@
 	switch (errno_value) {
 	case ENOENT:
 	case ENOTDIR:
-		giterr_set(GITERR_OS, "Could not find '%s' to %s", path, action);
+		giterr_set(GITERR_OS, "could not find '%s' to %s", path, action);
 		return GIT_ENOTFOUND;
 
 	case EINVAL:
 	case ENAMETOOLONG:
-		giterr_set(GITERR_OS, "Invalid path for filesystem '%s'", path);
+		giterr_set(GITERR_OS, "invalid path for filesystem '%s'", path);
 		return GIT_EINVALIDSPEC;
 
 	case EEXIST:
-		giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path);
+		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);
+		giterr_set(GITERR_OS, "could not %s '%s'", action, path);
 		return -1;
 	}
 }
@@ -754,7 +779,7 @@
 			/* error out if trying to up one from a hard base */
 			if (to == base && ceiling != 0) {
 				giterr_set(GITERR_INVALID,
-					"Cannot strip root component off url");
+					"cannot strip root component off url");
 				return -1;
 			}
 
@@ -983,7 +1008,7 @@
 	return 0;
 
 fail:
-	giterr_set(GITERR_OS, "Unable to convert unicode path data");
+	giterr_set(GITERR_OS, "unable to convert unicode path data");
 	return -1;
 }
 
@@ -1076,7 +1101,7 @@
 	wd_len = git_buf_len(path);
 
 	if ((dir = opendir(path->ptr)) == NULL) {
-		giterr_set(GITERR_OS, "Failed to open directory '%s'", path->ptr);
+		giterr_set(GITERR_OS, "failed to open directory '%s'", path->ptr);
 		if (errno == ENOENT)
 			return GIT_ENOTFOUND;
 
@@ -1141,7 +1166,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)
@@ -1158,13 +1182,13 @@
 	git_path_trim_slashes(&diriter->path_utf8);
 
 	if (diriter->path_utf8.size == 0) {
-		giterr_set(GITERR_FILESYSTEM, "Could not open directory '%s'", path);
+		giterr_set(GITERR_FILESYSTEM, "could not open directory '%s'", path);
 		return -1;
 	}
 
 	if ((diriter->parent_len = git_win32_path_from_utf8(diriter->path, diriter->path_utf8.ptr)) < 0 ||
 			!git_win32__findfirstfile_filter(path_filter, diriter->path_utf8.ptr)) {
-		giterr_set(GITERR_OS, "Could not parse the directory path '%s'", path);
+		giterr_set(GITERR_OS, "could not parse the directory path '%s'", path);
 		return -1;
 	}
 
@@ -1177,7 +1201,7 @@
 		is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0);
 
 	if (diriter->handle == INVALID_HANDLE_VALUE) {
-		giterr_set(GITERR_OS, "Could not open directory '%s'", path);
+		giterr_set(GITERR_OS, "could not open directory '%s'", path);
 		return -1;
 	}
 
@@ -1307,14 +1331,14 @@
 	git_path_trim_slashes(&diriter->path);
 
 	if (diriter->path.size == 0) {
-		giterr_set(GITERR_FILESYSTEM, "Could not open directory '%s'", path);
+		giterr_set(GITERR_FILESYSTEM, "could not open directory '%s'", path);
 		return -1;
 	}
 
 	if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) {
 		git_buf_free(&diriter->path);
 
-		giterr_set(GITERR_OS, "Failed to open directory '%s'", path);
+		giterr_set(GITERR_OS, "failed to open directory '%s'", path);
 		return -1;
 	}
 
@@ -1347,7 +1371,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/pathspec.c b/src/pathspec.c
index 361b398..00dba4f 100644
--- a/src/pathspec.c
+++ b/src/pathspec.c
@@ -487,7 +487,7 @@
 
 	/* if every pattern failed to match, then we have failed */
 	if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_files) {
-		giterr_set(GITERR_INVALID, "No matching files were found");
+		giterr_set(GITERR_INVALID, "no matching files were found");
 		error = GIT_ENOTFOUND;
 	}
 
@@ -658,7 +658,7 @@
 
 	/* if every pattern failed to match, then we have failed */
 	if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_deltas) {
-		giterr_set(GITERR_INVALID, "No matching deltas were found");
+		giterr_set(GITERR_INVALID, "no matching deltas were found");
 		error = GIT_ENOTFOUND;
 	}
 
diff --git a/src/posix.c b/src/posix.c
index b3f1a1c..94deb6a 100644
--- a/src/posix.c
+++ b/src/posix.c
@@ -10,6 +10,8 @@
 #include <stdio.h>
 #include <ctype.h>
 
+size_t p_fsync__cnt = 0;
+
 #ifndef GIT_WIN32
 
 #ifdef NO_ADDRINFO
@@ -240,7 +242,7 @@
 	out->len = 0;
 
 	if ((prot & GIT_PROT_WRITE) && ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)) {
-		giterr_set(GITERR_OS, "Trying to map shared-writeable");
+		giterr_set(GITERR_OS, "trying to map shared-writeable");
 		return -1;
 	}
 
diff --git a/src/posix.h b/src/posix.h
index f204751..bd5a98e 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -111,6 +111,12 @@
 extern int git__page_size(size_t *page_size);
 extern int git__mmap_alignment(size_t *page_size);
 
+/* The number of times `p_fsync` has been called.  Note that this is for
+ * test code only; it it not necessarily thread-safe and should not be
+ * relied upon in production.
+ */
+extern size_t p_fsync__cnt;
+
 /**
  * Platform-dependent methods
  */
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/push.c b/src/push.c
index b490138..09c2340 100644
--- a/src/push.c
+++ b/src/push.c
@@ -90,7 +90,7 @@
 static int check_rref(char *ref)
 {
 	if (git__prefixcmp(ref, "refs/")) {
-		giterr_set(GITERR_INVALID, "Not a valid reference '%s'", ref);
+		giterr_set(GITERR_INVALID, "not a valid reference '%s'", ref);
 		return -1;
 	}
 
@@ -111,7 +111,7 @@
 		giterr_set(GITERR_REFERENCE,
 			"src refspec '%s' does not match any existing object", ref);
 	else
-		giterr_set(GITERR_INVALID, "Not a valid reference '%s'", ref);
+		giterr_set(GITERR_INVALID, "not a valid reference '%s'", ref);
 	return -1;
 }
 
@@ -321,7 +321,7 @@
 
 			if (!git_odb_exists(push->repo->_odb, &spec->roid)) {
 				giterr_set(GITERR_REFERENCE, 
-					"Cannot push because a reference that you are trying to update on the remote contains commits that are not present locally.");
+					"cannot push because a reference that you are trying to update on the remote contains commits that are not present locally.");
 				error = GIT_ENONFASTFORWARD;
 				goto on_error;
 			}
@@ -332,7 +332,7 @@
 			if (error == GIT_ENOTFOUND ||
 				(!error && !git_oid_equal(&base, &spec->roid))) {
 				giterr_set(GITERR_REFERENCE,
-					"Cannot push non-fastforwardable reference");
+					"cannot push non-fastforwardable reference");
 				error = GIT_ENONFASTFORWARD;
 				goto on_error;
 			}
@@ -553,7 +553,7 @@
 			/* This is a create or update.  Local ref must exist. */
 			if (git_reference_name_to_id(
 					&spec->loid, push->repo, spec->refspec.src) < 0) {
-				giterr_set(GITERR_REFERENCE, "No such reference '%s'", spec->refspec.src);
+				giterr_set(GITERR_REFERENCE, "no such reference '%s'", spec->refspec.src);
 				return -1;
 			}
 		}
@@ -579,7 +579,7 @@
 	git_transport *transport = push->remote->transport;
 
 	if (!transport->push) {
-		giterr_set(GITERR_NET, "Remote transport doesn't support push");
+		giterr_set(GITERR_NET, "remote transport doesn't support push");
 		error = -1;
 		goto on_error;
 	}
diff --git a/src/rebase.c b/src/rebase.c
index e86312e..f528031 100644
--- a/src/rebase.c
+++ b/src/rebase.c
@@ -92,7 +92,7 @@
 	git_buf path = GIT_BUF_INIT;
 	git_rebase_type_t type = GIT_REBASE_TYPE_NONE;
 
-	if (git_buf_joinpath(&path, repo->path_repository, REBASE_APPLY_DIR) < 0)
+	if (git_buf_joinpath(&path, repo->gitdir, REBASE_APPLY_DIR) < 0)
 		return -1;
 
 	if (git_path_isdir(git_buf_cstr(&path))) {
@@ -101,7 +101,7 @@
 	}
 
 	git_buf_clear(&path);
-	if (git_buf_joinpath(&path, repo->path_repository, REBASE_MERGE_DIR) < 0)
+	if (git_buf_joinpath(&path, repo->gitdir, REBASE_MERGE_DIR) < 0)
 		return -1;
 
 	if (git_path_isdir(git_buf_cstr(&path))) {
@@ -152,7 +152,7 @@
 		return error;
 
 	if (git__strtol32(&num, asc_out->ptr, &eol, 10) < 0 || num < 0 || *eol) {
-		giterr_set(GITERR_REBASE, "The file '%s' contains an invalid numeric value", filename);
+		giterr_set(GITERR_REBASE, "the file '%s' contains an invalid numeric value", filename);
 		return -1;
 	}
 
@@ -170,7 +170,7 @@
 		return error;
 
 	if (str_out->size != GIT_OID_HEXSZ || git_oid_fromstr(out, str_out->ptr) < 0) {
-		giterr_set(GITERR_REBASE, "The file '%s' contains an invalid object ID", filename);
+		giterr_set(GITERR_REBASE, "the file '%s' contains an invalid object ID", filename);
 		return -1;
 	}
 
@@ -316,7 +316,7 @@
 		goto done;
 
 	if (rebase->type == GIT_REBASE_TYPE_NONE) {
-		giterr_set(GITERR_REBASE, "There is no rebase in progress");
+		giterr_set(GITERR_REBASE, "there is no rebase in progress");
 		error = GIT_ENOTFOUND;
 		goto done;
 	}
@@ -372,14 +372,14 @@
 
 	switch (rebase->type) {
 	case GIT_REBASE_TYPE_INTERACTIVE:
-		giterr_set(GITERR_REBASE, "Interactive rebase is not supported");
+		giterr_set(GITERR_REBASE, "interactive rebase is not supported");
 		error = -1;
 		break;
 	case GIT_REBASE_TYPE_MERGE:
 		error = rebase_open_merge(rebase);
 		break;
 	case GIT_REBASE_TYPE_APPLY:
-		giterr_set(GITERR_REBASE, "Patch application rebase is not supported");
+		giterr_set(GITERR_REBASE, "patch application rebase is not supported");
 		error = -1;
 		break;
 	default:
@@ -447,8 +447,8 @@
 	size_t i;
 	int error = 0;
 
-	if ((error = rebase_setupfile(rebase, END_FILE, -1, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 ||
-		(error = rebase_setupfile(rebase, ONTO_NAME_FILE, -1, "%s\n", rebase->onto_name)) < 0)
+	if ((error = rebase_setupfile(rebase, END_FILE, 0, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 ||
+		(error = rebase_setupfile(rebase, ONTO_NAME_FILE, 0, "%s\n", rebase->onto_name)) < 0)
 		goto done;
 
 	for (i = 0; i < git_array_size(rebase->operations); i++) {
@@ -459,7 +459,7 @@
 
 		git_oid_fmt(id_str, &operation->id);
 
-		if ((error = rebase_setupfile(rebase, commit_filename.ptr, -1,
+		if ((error = rebase_setupfile(rebase, commit_filename.ptr, 0,
 				"%.*s\n", GIT_OID_HEXSZ, id_str)) < 0)
 			goto done;
 	}
@@ -478,7 +478,7 @@
 	git_oid_fmt(orig_head, &rebase->orig_head_id);
 
 	if (p_mkdir(rebase->state_path, REBASE_DIR_MODE) < 0) {
-		giterr_set(GITERR_OS, "Failed to create rebase directory '%s'", rebase->state_path);
+		giterr_set(GITERR_OS, "failed to create rebase directory '%s'", rebase->state_path);
 		return -1;
 	}
 
@@ -486,10 +486,10 @@
 		rebase->orig_head_name;
 
 	if (git_repository__set_orig_head(rebase->repo, &rebase->orig_head_id) < 0 ||
-		rebase_setupfile(rebase, HEAD_NAME_FILE, -1, "%s\n", orig_head_name) < 0 ||
-		rebase_setupfile(rebase, ONTO_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, onto) < 0 ||
-		rebase_setupfile(rebase, ORIG_HEAD_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, orig_head) < 0 ||
-		rebase_setupfile(rebase, QUIET_FILE, -1, rebase->quiet ? "t\n" : "\n") < 0)
+		rebase_setupfile(rebase, HEAD_NAME_FILE, 0, "%s\n", orig_head_name) < 0 ||
+		rebase_setupfile(rebase, ONTO_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, onto) < 0 ||
+		rebase_setupfile(rebase, ORIG_HEAD_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, orig_head) < 0 ||
+		rebase_setupfile(rebase, QUIET_FILE, 0, rebase->quiet ? "t\n" : "\n") < 0)
 		return -1;
 
 	return rebase_setupfiles_merge(rebase);
@@ -511,7 +511,7 @@
 		return error;
 
 	if (type != GIT_REBASE_TYPE_NONE) {
-		giterr_set(GITERR_REBASE, "There is an existing rebase in progress");
+		giterr_set(GITERR_REBASE, "there is an existing rebase in progress");
 		return -1;
 	}
 
@@ -536,7 +536,7 @@
 			goto done;
 
 		if (git_diff_num_deltas(diff) > 0) {
-			giterr_set(GITERR_REBASE, "Uncommitted changes exist in index");
+			giterr_set(GITERR_REBASE, "uncommitted changes exist in index");
 			error = fail_with;
 			goto done;
 		}
@@ -546,11 +546,13 @@
 	}
 
 	if (check_workdir) {
-		if ((error = git_diff_index_to_workdir(&diff, repo, index, NULL)) < 0)
+		git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+		diff_opts.ignore_submodules = GIT_SUBMODULE_IGNORE_UNTRACKED;
+		if ((error = git_diff_index_to_workdir(&diff, repo, index, &diff_opts)) < 0)
 			goto done;
 
 		if (git_diff_num_deltas(diff) > 0) {
-			giterr_set(GITERR_REBASE, "Unstaged changes exist in workdir");
+			giterr_set(GITERR_REBASE, "unstaged changes exist in workdir");
 			error = fail_with;
 			goto done;
 		}
@@ -624,13 +626,13 @@
 
 	GIT_UNUSED(upstream);
 
-	if ((error = git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR)) < 0)
+	if ((error = git_buf_joinpath(&state_path, repo->gitdir, REBASE_MERGE_DIR)) < 0)
 		goto done;
 
 	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 {
@@ -731,7 +733,7 @@
 	if (inmemory)
 		error = rebase_init_inmemory(rebase, repo, branch, upstream, onto);
 	else
-		rebase_init_merge(rebase, repo, branch ,upstream, onto);
+		error = rebase_init_merge(rebase, repo, branch ,upstream, onto);
 
 	if (error == 0)
 		*out = rebase;
@@ -807,7 +809,7 @@
 		goto done;
 
 	if ((parent_count = git_commit_parentcount(current_commit)) > 1) {
-		giterr_set(GITERR_REBASE, "Cannot rebase a merge commit");
+		giterr_set(GITERR_REBASE, "cannot rebase a merge commit");
 		error = -1;
 		goto done;
 	} else if (parent_count) {
@@ -821,8 +823,8 @@
 	normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit);
 
 	if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 ||
-		(error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%" PRIuZ "\n", rebase->current+1)) < 0 ||
-		(error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 ||
+		(error = rebase_setupfile(rebase, MSGNUM_FILE, 0, "%" PRIuZ "\n", rebase->current+1)) < 0 ||
+		(error = rebase_setupfile(rebase, CURRENT_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 ||
 		(error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0 ||
 		(error = git_merge__check_result(rebase->repo, index)) < 0 ||
 		(error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0 ||
@@ -864,7 +866,7 @@
 		goto done;
 
 	if ((parent_count = git_commit_parentcount(current_commit)) > 1) {
-		giterr_set(GITERR_REBASE, "Cannot rebase a merge commit");
+		giterr_set(GITERR_REBASE, "cannot rebase a merge commit");
 		error = -1;
 		goto done;
 	} else if (parent_count) {
@@ -1259,7 +1261,7 @@
 	goto done;
 
 on_error:
-	giterr_set(GITERR_REBASE, "Invalid rewritten file at line %d", linenum);
+	giterr_set(GITERR_REBASE, "invalid rewritten file at line %d", linenum);
 	error = -1;
 
 done:
diff --git a/src/refdb.c b/src/refdb.c
index debba12..1ee0efb 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");
+		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..b325d27 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -26,8 +26,6 @@
 #include <git2/sys/refs.h>
 #include <git2/sys/reflog.h>
 
-GIT__USE_STRMAP
-
 #define DEFAULT_NESTING_LEVEL	5
 #define MAX_NESTING_LEVEL		10
 
@@ -55,12 +53,16 @@
 	git_refdb_backend parent;
 
 	git_repository *repo;
-	char *path;
+	/* path to git directory */
+	char *gitpath;
+	/* path to common objects' directory */
+	char *commonpath;
 
 	git_sortedcache *refcache;
 	int peeling_mode;
 	git_iterator_flag_t iterator_flags;
 	uint32_t direach_flags;
+	int fsync;
 } refdb_fs_backend;
 
 static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name);
@@ -77,7 +79,7 @@
 	git_buf packedrefs = GIT_BUF_INIT;
 	char *scan, *eof, *eol;
 
-	if (!backend->path)
+	if (!backend->gitpath)
 		return 0;
 
 	error = git_sortedcache_lockandload(backend->refcache, &packedrefs);
@@ -185,7 +187,7 @@
 	return 0;
 
 parse_failed:
-	giterr_set(GITERR_REFERENCE, "Corrupted packed references file");
+	giterr_set(GITERR_REFERENCE, "corrupted packed references file");
 
 	git_sortedcache_clear(backend->refcache, false);
 	git_sortedcache_wunlock(backend->refcache);
@@ -212,7 +214,7 @@
 		return 0;
 
 corrupted:
-	giterr_set(GITERR_REFERENCE, "Corrupted loose reference file: %s", filename);
+	giterr_set(GITERR_REFERENCE, "corrupted loose reference file: %s", filename);
 	return -1;
 }
 
@@ -238,7 +240,7 @@
 	/* if we fail to load the loose reference, assume someone changed
 	 * the filesystem under us and skip it...
 	 */
-	if (loose_readbuffer(&ref_file, backend->path, name) < 0) {
+	if (loose_readbuffer(&ref_file, backend->gitpath, name) < 0) {
 		giterr_clear();
 		goto done;
 	}
@@ -287,7 +289,7 @@
 		return error;
 	}
 
-	file_path = full_path->ptr + strlen(backend->path);
+	file_path = full_path->ptr + strlen(backend->gitpath);
 
 	return loose_lookup_to_packfile(backend, file_path);
 }
@@ -303,7 +305,7 @@
 	int error;
 	git_buf refs_path = GIT_BUF_INIT;
 
-	if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0)
+	if (git_buf_joinpath(&refs_path, backend->gitpath, GIT_REFS_DIR) < 0)
 		return -1;
 
 	/*
@@ -326,12 +328,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->gitpath, ref_name)) < 0)
+		return error;
 
 	*exists = git_path_isfile(ref_path.ptr) ||
 		(git_sortedcache_lookup(backend->refcache, ref_name) != NULL);
@@ -348,7 +351,7 @@
 	refname_start = (const char *)file_content->ptr;
 
 	if (git_buf_len(file_content) < header_len + 1) {
-		giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
+		giterr_set(GITERR_REFERENCE, "corrupted loose reference file");
 		return NULL;
 	}
 
@@ -361,6 +364,19 @@
 	return refname_start;
 }
 
+/*
+ * Returns whether a reference is stored per worktree or not.
+ * Per-worktree references are:
+ *
+ * - all pseudorefs, e.g. HEAD and MERGE_HEAD
+ * - all references stored inside of "refs/bisect/"
+ */
+static bool is_per_worktree_ref(const char *ref_name)
+{
+	return git__prefixcmp(ref_name, "refs/") != 0 ||
+	    git__prefixcmp(ref_name, "refs/bisect/") == 0;
+}
+
 static int loose_lookup(
 	git_reference **out,
 	refdb_fs_backend *backend,
@@ -368,11 +384,17 @@
 {
 	git_buf ref_file = GIT_BUF_INIT;
 	int error = 0;
+	const char *ref_dir;
 
 	if (out)
 		*out = NULL;
 
-	if ((error = loose_readbuffer(&ref_file, backend->path, ref_name)) < 0)
+	if (is_per_worktree_ref(ref_name))
+		ref_dir = backend->gitpath;
+	else
+		ref_dir = backend->commonpath;
+
+	if ((error = loose_readbuffer(&ref_file, ref_dir, ref_name)) < 0)
 		/* cannot read loose ref file - gah */;
 	else if (git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF) == 0) {
 		const char *target;
@@ -397,7 +419,7 @@
 
 static int ref_error_notfound(const char *name)
 {
-	giterr_set(GITERR_REFERENCE, "Reference '%s' not found", name);
+	giterr_set(GITERR_REFERENCE, "reference '%s' not found", name);
 	return GIT_ENOTFOUND;
 }
 
@@ -409,8 +431,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;
@@ -483,12 +505,12 @@
 	git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT;
 	const git_index_entry *entry = NULL;
 
-	if (!backend->path) /* do nothing if no path for loose refs */
+	if (!backend->commonpath) /* do nothing if no commonpath for loose refs */
 		return 0;
 
 	fsit_opts.flags = backend->iterator_flags;
 
-	if ((error = git_buf_printf(&path, "%s/refs", backend->path)) < 0 ||
+	if ((error = git_buf_printf(&path, "%s/refs", backend->commonpath)) < 0 ||
 		(error = git_iterator_for_filesystem(&fsit, path.ptr, &fsit_opts)) < 0) {
 		git_buf_free(&path);
 		return error;
@@ -615,13 +637,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,20 +697,22 @@
 	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,
-				"Failed to write reference '%s': a reference with "
+				"failed to write reference '%s': a reference with "
 				"that name already exists.", new_ref);
 			return GIT_EEXISTS;
 		}
@@ -701,7 +726,7 @@
 		if (ref && !ref_is_available(old_ref, new_ref, ref->name)) {
 			git_sortedcache_runlock(backend->refcache);
 			giterr_set(GITERR_REFERENCE,
-				"Path to reference '%s' collides with existing one", new_ref);
+				"path to reference '%s' collides with existing one", new_ref);
 			return -1;
 		}
 	}
@@ -712,26 +737,36 @@
 
 static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name)
 {
-	int error;
+	int error, filebuf_flags;
 	git_buf ref_path = GIT_BUF_INIT;
+	const char *basedir;
 
 	assert(file && backend && name);
 
 	if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
-		giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", name);
+		giterr_set(GITERR_INVALID, "invalid reference name '%s'", name);
 		return GIT_EINVALIDSPEC;
 	}
 
+	if (is_per_worktree_ref(name))
+		basedir = backend->gitpath;
+	else
+		basedir = backend->commonpath;
+
 	/* 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)
+	if ((error = git_futils_rmdir_r(name, basedir, GIT_RMDIR_SKIP_NONEMPTY)) < 0)
+		return error;
+
+	if (git_buf_joinpath(&ref_path, basedir, name) < 0)
 		return -1;
 
-	if (git_buf_joinpath(&ref_path, backend->path, name) < 0)
-		return -1;
+	filebuf_flags = GIT_FILEBUF_FORCE;
+	if (backend->fsync)
+		filebuf_flags |= GIT_FILEBUF_FSYNC;
 
-	error = git_filebuf_open(file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE);
+	error = git_filebuf_open(file, ref_path.ptr, filebuf_flags, GIT_REFS_FILE_MODE);
 
 	if (error == GIT_EDIRECTORY)
 		giterr_set(GITERR_REFERENCE, "cannot lock ref '%s', there are refs beneath that folder", name);
@@ -901,40 +936,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 +1001,45 @@
 {
 	git_sortedcache *refcache = backend->refcache;
 	git_filebuf pack_file = GIT_FILEBUF_INIT;
+	int error, open_flags = 0;
 	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;
+
+	if (backend->fsync)
+		open_flags = GIT_FILEBUF_FSYNC;
 
 	/* 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), open_flags, 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 +1052,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 +1204,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 */
@@ -1257,18 +1317,17 @@
 	}
 
 	/* If a loose reference exists, remove it from the filesystem */
-	if (git_buf_joinpath(&loose_path, backend->path, ref_name) < 0)
+	if (git_buf_joinpath(&loose_path, backend->gitpath, 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 +1350,7 @@
 	error = packed_write(backend);
 
 cleanup:
+	git_buf_free(&loose_path);
 	git_filebuf_cleanup(file);
 
 	return error;
@@ -1362,14 +1422,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;
 }
@@ -1381,28 +1442,30 @@
 	assert(backend);
 
 	git_sortedcache_free(backend->refcache);
-	git__free(backend->path);
+	git__free(backend->gitpath);
+	git__free(backend->commonpath);
 	git__free(backend);
 }
 
-static int setup_namespace(git_buf *path, git_repository *repo)
+static char *setup_namespace(git_repository *repo, const char *in)
 {
-	char *parts, *start, *end;
+	git_buf path = GIT_BUF_INIT;
+	char *parts, *start, *end, *out = NULL;
 
-	/* Not all repositories have a path */
-	if (repo->path_repository == NULL)
-		return 0;
+	if (!in)
+		goto done;
 
-	/* Load the path to the repo first */
-	git_buf_puts(path, repo->path_repository);
+	git_buf_puts(&path, in);
 
 	/* if the repo is not namespaced, nothing else to do */
-	if (repo->namespace == NULL)
-		return 0;
+	if (repo->namespace == NULL) {
+		out = git_buf_detach(&path);
+		goto done;
+	}
 
 	parts = end = git__strdup(repo->namespace);
 	if (parts == NULL)
-		return -1;
+		goto done;
 
 	/*
 	 * From `man gitnamespaces`:
@@ -1410,21 +1473,24 @@
 	 *  of namespaces; for example, GIT_NAMESPACE=foo/bar will store
 	 *  refs under refs/namespaces/foo/refs/namespaces/bar/
 	 */
-	while ((start = git__strsep(&end, "/")) != NULL) {
-		git_buf_printf(path, "refs/namespaces/%s/", start);
-	}
+	while ((start = git__strsep(&end, "/")) != NULL)
+		git_buf_printf(&path, "refs/namespaces/%s/", start);
 
-	git_buf_printf(path, "refs/namespaces/%s/refs", end);
+	git_buf_printf(&path, "refs/namespaces/%s/refs", end);
 	git__free(parts);
 
 	/* Make sure that the folder with the namespace exists */
-	if (git_futils_mkdir_relative(git_buf_cstr(path), repo->path_repository,
-			0777, GIT_MKDIR_PATH, NULL) < 0)
-		return -1;
+	if (git_futils_mkdir_relative(git_buf_cstr(&path), in, 0777,
+			GIT_MKDIR_PATH, NULL) < 0)
+		goto done;
 
-	/* Return root of the namespaced path, i.e. without the trailing '/refs' */
-	git_buf_rtruncate_at_char(path, '/');
-	return 0;
+	/* Return root of the namespaced gitpath, i.e. without the trailing '/refs' */
+	git_buf_rtruncate_at_char(&path, '/');
+	out = git_buf_detach(&path);
+
+done:
+	git_buf_free(&path);
+	return out;
 }
 
 static int reflog_alloc(git_reflog **reflog, const char *name)
@@ -1457,7 +1523,7 @@
 
 #define seek_forward(_increase) do { \
 	if (_increase >= buf_size) { \
-		giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \
+		giterr_set(GITERR_INVALID, "ran out of data while parsing reflog"); \
 		goto fail; \
 	} \
 	buf += _increase; \
@@ -1535,7 +1601,7 @@
 
 GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const char *name)
 {
-	return git_buf_join3(path, '/', repo->path_repository, GIT_REFLOG_DIR, name);
+	return git_buf_join3(path, '/', repo->commondir, GIT_REFLOG_DIR, name);
 }
 
 static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name)
@@ -1673,7 +1739,7 @@
 	repo = backend->repo;
 
 	if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
-		giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", refname);
+		giterr_set(GITERR_INVALID, "invalid reference name '%s'", refname);
 		return GIT_EINVALIDSPEC;
 	}
 
@@ -1682,7 +1748,7 @@
 
 	if (!git_path_isfile(git_buf_cstr(&log_path))) {
 		giterr_set(GITERR_INVALID,
-			"Log file for reference '%s' doesn't exist.", refname);
+			"log file for reference '%s' doesn't exist", refname);
 		error = -1;
 		goto cleanup;
 	}
@@ -1734,7 +1800,7 @@
 /* Append to the reflog, must be called under reference lock */
 static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *who, const char *message)
 {
-	int error, is_symbolic;
+	int error, is_symbolic, open_flags;
 	git_oid old_id = {{0}}, new_id = {{0}};
 	git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
 	git_repository *repo = backend->repo;
@@ -1789,9 +1855,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;
@@ -1801,7 +1868,12 @@
 			goto cleanup;
 	}
 
-	error = git_futils_writebuffer(&buf, git_buf_cstr(&path), O_WRONLY|O_CREAT|O_APPEND, GIT_REFLOG_FILE_MODE);
+	open_flags = O_WRONLY | O_CREAT | O_APPEND;
+
+	if (backend->fsync)
+		open_flags |= O_FSYNC;
+
+	error = git_futils_writebuffer(&buf, git_buf_cstr(&path), open_flags, GIT_REFLOG_FILE_MODE);
 
 cleanup:
 	git_buf_free(&buf);
@@ -1829,7 +1901,7 @@
 		&normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0)
 			return error;
 
-	if (git_buf_joinpath(&temp_path, repo->path_repository, GIT_REFLOG_DIR) < 0)
+	if (git_buf_joinpath(&temp_path, repo->gitdir, GIT_REFLOG_DIR) < 0)
 		return -1;
 
 	if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), old_name) < 0)
@@ -1861,7 +1933,7 @@
 	p_close(fd);
 
 	if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
-		giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
+		giterr_set(GITERR_OS, "failed to rename reflog for %s", new_name);
 		error = -1;
 		goto cleanup;
 	}
@@ -1878,7 +1950,7 @@
 	}
 
 	if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
-		giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
+		giterr_set(GITERR_OS, "failed to rename reflog for %s", new_name);
 		error = -1;
 	}
 
@@ -1920,7 +1992,7 @@
 	git_repository *repository)
 {
 	int t = 0;
-	git_buf path = GIT_BUF_INIT;
+	git_buf gitpath = GIT_BUF_INIT;
 	refdb_fs_backend *backend;
 
 	backend = git__calloc(1, sizeof(refdb_fs_backend));
@@ -1928,18 +2000,27 @@
 
 	backend->repo = repository;
 
-	if (setup_namespace(&path, repository) < 0)
-		goto fail;
+	if (repository->gitdir) {
+		backend->gitpath = setup_namespace(repository, repository->gitdir);
 
-	backend->path = git_buf_detach(&path);
+		if (backend->gitpath == NULL)
+			goto fail;
+	}
 
-	if (git_buf_joinpath(&path, backend->path, GIT_PACKEDREFS_FILE) < 0 ||
+	if (repository->commondir) {
+		backend->commonpath = setup_namespace(repository, repository->commondir);
+
+		if (backend->commonpath == NULL)
+			goto fail;
+	}
+
+	if (git_buf_joinpath(&gitpath, backend->commonpath, GIT_PACKEDREFS_FILE) < 0 ||
 		git_sortedcache_new(
 			&backend->refcache, offsetof(struct packref, name),
-			NULL, NULL, packref_cmp, git_buf_cstr(&path)) < 0)
+			NULL, NULL, packref_cmp, git_buf_cstr(&gitpath)) < 0)
 		goto fail;
 
-	git_buf_free(&path);
+	git_buf_free(&gitpath);
 
 	if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_IGNORECASE) && t) {
 		backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE;
@@ -1949,6 +2030,9 @@
 		backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
 		backend->direach_flags  |= GIT_PATH_DIR_PRECOMPOSE_UNICODE;
 	}
+	if ((!git_repository__cvar(&t, backend->repo, GIT_CVAR_FSYNCOBJECTFILES) && t) ||
+		git_object__synchronous_writing)
+		backend->fsync = 1;
 
 	backend->parent.exists = &refdb_fs_backend__exists;
 	backend->parent.lookup = &refdb_fs_backend__lookup;
@@ -1971,8 +2055,9 @@
 	return 0;
 
 fail:
-	git_buf_free(&path);
-	git__free(backend->path);
+	git_buf_free(&gitpath);
+	git__free(backend->gitpath);
+	git__free(backend->commonpath);
 	git__free(backend);
 	return -1;
 }
diff --git a/src/reflog.c b/src/reflog.c
index 9ce9aee..98ef1b6 100644
--- a/src/reflog.c
+++ b/src/reflog.c
@@ -93,7 +93,7 @@
 
 		if (newline) {
 			if (newline[1] != '\0') {
-				giterr_set(GITERR_INVALID, "Reflog message cannot contain newline");
+				giterr_set(GITERR_INVALID, "reflog message cannot contain newline");
 				goto cleanup;
 			}
 
@@ -193,7 +193,7 @@
 	entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
 
 	if (entry == NULL) {
-		giterr_set(GITERR_REFERENCE, "No reflog entry at index %"PRIuZ, idx);
+		giterr_set(GITERR_REFERENCE, "no reflog entry at index %"PRIuZ, idx);
 		return GIT_ENOTFOUND;
 	}
 
diff --git a/src/refs.c b/src/refs.c
index bff443a..0837dc4 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -24,7 +24,7 @@
 #include <git2/signature.h>
 #include <git2/commit.h>
 
-GIT__USE_STRMAP
+bool git_reference__enable_symbolic_ref_target_validation = true;
 
 #define DEFAULT_NESTING_LEVEL	5
 #define MAX_NESTING_LEVEL		10
@@ -178,7 +178,8 @@
 static int reference_normalize_for_repo(
 	git_refname_t out,
 	git_repository *repo,
-	const char *name)
+	const char *name,
+	bool validate)
 {
 	int precompose;
 	unsigned int flags = GIT_REF_FORMAT_ALLOW_ONELEVEL;
@@ -187,6 +188,9 @@
 		precompose)
 		flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;
 
+	if (!validate)
+		flags |= GIT_REF_FORMAT__VALIDATION_DISABLE;
+
 	return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags);
 }
 
@@ -213,7 +217,7 @@
 
 	scan_type = GIT_REF_SYMBOLIC;
 
-	if ((error = reference_normalize_for_repo(scan_name, repo, name)) < 0)
+	if ((error = reference_normalize_for_repo(scan_name, repo, name, true)) < 0)
 		return error;
 
 	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
@@ -236,7 +240,7 @@
 
 	if (scan_type != GIT_REF_OID && max_nesting != 0) {
 		giterr_set(GITERR_REFERENCE,
-			"Cannot resolve reference (>%u levels deep)", max_nesting);
+			"cannot resolve reference (>%u levels deep)", max_nesting);
 		git_reference_free(ref);
 		return -1;
 	}
@@ -298,7 +302,7 @@
 	if (error && !foundvalid) {
 		/* never found a valid reference name */
 		giterr_set(GITERR_REFERENCE,
-			"Could not use '%s' as valid reference name", git_buf_cstr(&name));
+			"could not use '%s' as valid reference name", git_buf_cstr(&name));
 	}
 
 	if (error == GIT_ENOTFOUND)
@@ -383,7 +387,7 @@
 	if (ref_out)
 		*ref_out = NULL;
 
-	error = reference_normalize_for_repo(normalized, repo, name);
+	error = reference_normalize_for_repo(normalized, repo, name, true);
 	if (error < 0)
 		return error;
 
@@ -396,7 +400,7 @@
 
 		if (!git_object__is_valid(repo, oid, GIT_OBJ_ANY)) {
 			giterr_set(GITERR_REFERENCE,
-				"Target OID for the reference doesn't exist on the repository");
+				"target OID for the reference doesn't exist on the repository");
 			return -1;
 		}
 
@@ -404,7 +408,10 @@
 	} else {
 		git_refname_t normalized_target;
 
-		if ((error = reference_normalize_for_repo(normalized_target, repo, symbolic)) < 0)
+		error = reference_normalize_for_repo(normalized_target, repo,
+			symbolic, git_reference__enable_symbolic_ref_target_validation);
+
+		if (error < 0)
 			return error;
 
 		ref = git_reference__alloc_symbolic(normalized, normalized_target);
@@ -524,7 +531,7 @@
 	if (ref->type == GIT_REF_OID)
 		return 0;
 
-	giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");
+	giterr_set(GITERR_REFERENCE, "cannot set OID on symbolic reference");
 	return -1;
 }
 
@@ -552,7 +559,7 @@
 	if (ref->type == GIT_REF_SYMBOLIC)
 		return 0;
 
-	giterr_set(GITERR_REFERENCE, "Cannot set symbolic target on a direct reference");
+	giterr_set(GITERR_REFERENCE, "cannot set symbolic target on a direct reference");
 	return -1;
 }
 
@@ -583,7 +590,7 @@
 	assert(ref && new_name && signature);
 
 	if ((error = reference_normalize_for_repo(
-			normalized, git_reference_owner(ref), new_name)) < 0)
+		normalized, git_reference_owner(ref), new_name, true)) < 0)
 		return error;
 
 
@@ -599,7 +606,7 @@
 	/* Update HEAD it was pointing to the reference being renamed */
 	if (should_head_be_updated &&
 		(error = git_repository_set_head(ref->db->repo, normalized)) < 0) {
-		giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference");
+		giterr_set(GITERR_REFERENCE, "failed to update HEAD after renaming reference");
 		return error;
 	}
 
@@ -636,7 +643,7 @@
 		return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1);
 
 	default:
-		giterr_set(GITERR_REFERENCE, "Invalid reference");
+		giterr_set(GITERR_REFERENCE, "invalid reference");
 		return -1;
 	}
 }
@@ -876,6 +883,7 @@
 	int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
 	unsigned int process_flags;
 	bool normalize = (buf != NULL);
+	bool validate = (flags & GIT_REF_FORMAT__VALIDATION_DISABLE) == 0;
 
 #ifdef GIT_USE_ICONV
 	git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
@@ -886,7 +894,7 @@
 	process_flags = flags;
 	current = (char *)name;
 
-	if (*current == '/')
+	if (validate && *current == '/')
 		goto cleanup;
 
 	if (normalize)
@@ -902,6 +910,13 @@
 	}
 #endif
 
+	if (!validate) {
+		git_buf_sets(buf, current);
+
+		error = git_buf_oom(buf) ? -1 : 0;
+		goto cleanup;
+	}
+
 	while (true) {
 		segment_len = ensure_segment_validity(current);
 		if (segment_len < 0) {
@@ -973,7 +988,7 @@
 	if (error == GIT_EINVALIDSPEC)
 		giterr_set(
 			GITERR_REFERENCE,
-			"The given reference name '%s' is not valid", name);
+			"the given reference name '%s' is not valid", name);
 
 	if (error && normalize)
 		git_buf_free(buf);
@@ -1000,7 +1015,7 @@
 	if (git_buf_len(&buf) > buffer_size - 1) {
 		giterr_set(
 		GITERR_REFERENCE,
-		"The provided buffer is too short to hold the normalization of '%s'", name);
+		"the provided buffer is too short to hold the normalization of '%s'", name);
 		error = GIT_EBUFS;
 		goto cleanup;
 	}
@@ -1046,7 +1061,7 @@
 	int error = 0;
 
 	if (nesting > MAX_NESTING_LEVEL) {
-		giterr_set(GITERR_REFERENCE, "Reference chain too deep (%d)", nesting);
+		giterr_set(GITERR_REFERENCE, "reference chain too deep (%d)", nesting);
 		return GIT_ENOTFOUND;
 	}
 
@@ -1115,6 +1130,18 @@
 	return error;
 }
 
+static const char *commit_type(const git_commit *commit)
+{
+	unsigned int count = git_commit_parentcount(commit);
+
+	if (count >= 2)
+		return " (merge)";
+	else if (count == 0)
+		return " (initial)";
+	else
+		return "";
+}
+
 int git_reference__update_for_commit(
 	git_repository *repo,
 	git_reference *ref,
@@ -1131,7 +1158,7 @@
 	if ((error = git_commit_lookup(&commit, repo, id)) < 0 ||
 		(error = git_buf_printf(&reflog_msg, "%s%s: %s",
 			operation ? operation : "commit",
-			git_commit_parentcount(commit) == 0 ? " (initial)" : "",
+			commit_type(commit),
 			git_commit_summary(commit))) < 0)
 		goto done;
 
@@ -1229,7 +1256,7 @@
 {
 	giterr_set(
 		GITERR_INVALID,
-		"The reference '%s' cannot be peeled - %s", git_reference_name(ref), msg);
+		"the reference '%s' cannot be peeled - %s", git_reference_name(ref), msg);
 	return error;
 }
 
diff --git a/src/refs.h b/src/refs.h
index fda9532..80e655a 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -15,6 +15,8 @@
 #include "buffer.h"
 #include "oid.h"
 
+extern bool git_reference__enable_symbolic_ref_target_validation;
+
 #define GIT_REFS_DIR "refs/"
 #define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
 #define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"
@@ -53,6 +55,7 @@
 #define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
 
 #define GIT_REF_FORMAT__PRECOMPOSE_UNICODE	(1u << 16)
+#define GIT_REF_FORMAT__VALIDATION_DISABLE	(1u << 15)
 
 #define GIT_REFNAME_MAX 1024
 
diff --git a/src/remote.c b/src/remote.c
index c1d7d59..d3132f7 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -283,7 +283,7 @@
 
 	giterr_set(
 		GITERR_CONFIG,
-		"Remote '%s' already exists.", name);
+		"remote '%s' already exists", name);
 
 	return GIT_EEXISTS;
 }
@@ -476,7 +476,7 @@
 
 	if (!optional_setting_found) {
 		error = GIT_ENOTFOUND;
-		giterr_set(GITERR_CONFIG, "Remote '%s' does not exist.", name);
+		giterr_set(GITERR_CONFIG, "remote '%s' does not exist", name);
 		goto cleanup;
 	}
 
@@ -770,8 +770,12 @@
 		goto found;
 	}
 
-	/* HTTP_PROXY / HTTPS_PROXY environment variables */
-	error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
+	/* http_proxy / https_proxy environment variables */
+	error = git__getenv(&val, use_ssl ? "https_proxy" : "http_proxy");
+
+	/* try uppercase environment variables */
+	if (error == GIT_ENOTFOUND)
+		error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
 
 	if (error < 0) {
 		if (error == GIT_ENOTFOUND) {
@@ -1718,7 +1722,7 @@
 			error = 0;
 		break;
 	default:
-		giterr_set(GITERR_INVALID, "Invalid value for the tagopt setting");
+		giterr_set(GITERR_INVALID, "invalid value for the tagopt setting");
 		error = -1;
 	}
 
diff --git a/src/repository.c b/src/repository.c
index cf3d18a..425ef79 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -27,13 +27,41 @@
 #include "merge.h"
 #include "diff_driver.h"
 #include "annotated_commit.h"
+#include "submodule.h"
+#include "worktree.h"
+
+#include "strmap.h"
 
 #ifdef GIT_WIN32
 # include "win32/w32_util.h"
 #endif
 
+static const struct {
+    git_repository_item_t parent;
+    const char *name;
+    bool directory;
+} items[] = {
+	{ GIT_REPOSITORY_ITEM_GITDIR, NULL, true },
+	{ GIT_REPOSITORY_ITEM_WORKDIR, NULL, true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, NULL, true },
+	{ GIT_REPOSITORY_ITEM_GITDIR, "index", false },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "objects", true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "refs", true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "packed-refs", false },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "remotes", true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "config", false },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "info", true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "hooks", true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "logs", true },
+	{ GIT_REPOSITORY_ITEM_GITDIR, "modules", true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "worktrees", true }
+};
+
 static int check_repositoryformatversion(git_config *config);
 
+#define GIT_COMMONDIR_FILE "commondir"
+#define GIT_GITDIR_FILE "gitdir"
+
 #define GIT_FILE_CONTENT_PREFIX "gitdir:"
 
 #define GIT_BRANCH_MASTER "master"
@@ -109,6 +137,7 @@
 {
 	assert(repo);
 
+	git_repository_submodule_cache_clear(repo);
 	git_cache_clear(&repo->objects);
 	git_attr_cache_flush(repo);
 
@@ -136,8 +165,9 @@
 		git_buf_free(git_array_get(repo->reserved_names, i));
 	git_array_clear(repo->reserved_names);
 
-	git__free(repo->path_gitlink);
-	git__free(repo->path_repository);
+	git__free(repo->gitlink);
+	git__free(repo->gitdir);
+	git__free(repo->commondir);
 	git__free(repo->workdir);
 	git__free(repo->namespace);
 	git__free(repo->ident_name);
@@ -152,17 +182,41 @@
  *
  * Open a repository object from its path
  */
-static bool valid_repository_path(git_buf *repository_path)
+static bool valid_repository_path(git_buf *repository_path, git_buf *common_path)
 {
-	/* Check OBJECTS_DIR first, since it will generate the longest path name */
-	if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false)
-		return false;
+	/* Check if we have a separate commondir (e.g. we have a
+	 * worktree) */
+	if (git_path_contains_file(repository_path, GIT_COMMONDIR_FILE)) {
+		git_buf common_link  = GIT_BUF_INIT;
+		git_buf_joinpath(&common_link, repository_path->ptr, GIT_COMMONDIR_FILE);
+
+		git_futils_readbuffer(&common_link, common_link.ptr);
+		git_buf_rtrim(&common_link);
+
+		if (git_path_is_relative(common_link.ptr)) {
+			git_buf_joinpath(common_path, repository_path->ptr, common_link.ptr);
+		} else {
+			git_buf_swap(common_path, &common_link);
+		}
+
+		git_buf_free(&common_link);
+	}
+	else {
+		git_buf_set(common_path, repository_path->ptr, repository_path->size);
+	}
+
+	/* Make sure the commondir path always has a trailing * slash */
+	if (git_buf_rfind(common_path, '/') != (ssize_t)common_path->size - 1)
+		git_buf_putc(common_path, '/');
 
 	/* Ensure HEAD file exists */
 	if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
 		return false;
 
-	if (git_path_contains_dir(repository_path, GIT_REFS_DIR)  == false)
+	/* Check files in common dir */
+	if (git_path_contains_dir(common_path, GIT_OBJECTS_DIR) == false)
+		return false;
+	if (git_path_contains_dir(common_path, GIT_REFS_DIR) == false)
 		return false;
 
 	return true;
@@ -201,6 +255,7 @@
 	GITERR_CHECK_ALLOC(repo);
 
 	repo->is_bare = 1;
+	repo->is_worktree = 0;
 
 	return 0;
 }
@@ -220,9 +275,10 @@
 
 static int load_workdir(git_repository *repo, git_config *config, git_buf *parent_path)
 {
-	int         error;
+	int error;
 	git_config_entry *ce;
-	git_buf     worktree = GIT_BUF_INIT;
+	git_buf worktree = GIT_BUF_INIT;
+	git_buf path = GIT_BUF_INIT;
 
 	if (repo->is_bare)
 		return 0;
@@ -231,9 +287,26 @@
 			&ce, config, "core.worktree", false)) < 0)
 		return error;
 
-	if (ce && ce->value) {
+	if (repo->is_worktree) {
+		char *gitlink = git_worktree__read_link(repo->gitdir, GIT_GITDIR_FILE);
+		if (!gitlink) {
+			error = -1;
+			goto cleanup;
+		}
+
+		git_buf_attach(&worktree, gitlink, 0);
+
+		if ((git_path_dirname_r(&worktree, worktree.ptr)) < 0 ||
+		    git_path_to_dir(&worktree) < 0) {
+			error = -1;
+			goto cleanup;
+		}
+
+		repo->workdir = git_buf_detach(&worktree);
+	}
+	else if (ce && ce->value) {
 		if ((error = git_path_prettify_dir(
-				&worktree, ce->value, repo->path_repository)) < 0)
+				&worktree, ce->value, repo->gitdir)) < 0)
 			goto cleanup;
 
 		repo->workdir = git_buf_detach(&worktree);
@@ -241,7 +314,7 @@
 	else if (parent_path && git_path_isdir(parent_path->ptr))
 		repo->workdir = git_buf_detach(parent_path);
 	else {
-		if (git_path_dirname_r(&worktree, repo->path_repository) < 0 ||
+		if (git_path_dirname_r(&worktree, repo->gitdir) < 0 ||
 		    git_path_to_dir(&worktree) < 0) {
 			error = -1;
 			goto cleanup;
@@ -252,6 +325,7 @@
 
 	GITERR_CHECK_ALLOC(repo->workdir);
 cleanup:
+	git_buf_free(&path);
 	git_config_entry_free(ce);
 	return error;
 }
@@ -332,7 +406,7 @@
 		memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
 	{
 		giterr_set(GITERR_REPOSITORY,
-			"The `.git` file at '%s' is malformed", file_path);
+			"the `.git` file at '%s' is malformed", file_path);
 		error = -1;
 	}
 	else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) {
@@ -351,6 +425,7 @@
 	git_buf *repo_path,
 	git_buf *parent_path,
 	git_buf *link_path,
+	git_buf *common_path,
 	const char *start_path,
 	uint32_t flags,
 	const char *ceiling_dirs)
@@ -358,6 +433,7 @@
 	int error;
 	git_buf path = GIT_BUF_INIT;
 	git_buf repo_link = GIT_BUF_INIT;
+	git_buf common_link = GIT_BUF_INIT;
 	struct stat st;
 	dev_t initial_device = 0;
 	int min_iterations;
@@ -404,21 +480,30 @@
 				break;
 
 			if (S_ISDIR(st.st_mode)) {
-				if (valid_repository_path(&path)) {
+				if (valid_repository_path(&path, &common_link)) {
 					git_path_to_dir(&path);
 					git_buf_set(repo_path, path.ptr, path.size);
+
+					if (link_path)
+						git_buf_attach(link_path,
+							git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0);
+					if (common_path)
+						git_buf_swap(&common_link, common_path);
+
 					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;
-				if (valid_repository_path(&repo_link)) {
+				if (valid_repository_path(&repo_link, &common_link)) {
 					git_buf_swap(repo_path, &repo_link);
 
 					if (link_path)
 						error = git_buf_put(link_path, path.ptr, path.size);
+					if (common_path)
+						git_buf_swap(&common_link, common_path);
 				}
 				break;
 			}
@@ -459,12 +544,13 @@
 	 * to report, report that. */
 	if (!git_buf_len(repo_path) && !error) {
 		giterr_set(GITERR_REPOSITORY,
-			"Could not find repository from '%s'", start_path);
+			"could not find repository from '%s'", start_path);
 		error = GIT_ENOTFOUND;
 	}
 
 	git_buf_free(&path);
 	git_buf_free(&repo_link);
+	git_buf_free(&common_link);
 	return error;
 }
 
@@ -473,26 +559,30 @@
 	const char *bare_path)
 {
 	int error;
-	git_buf path = GIT_BUF_INIT;
+	git_buf path = GIT_BUF_INIT, common_path = GIT_BUF_INIT;
 	git_repository *repo = NULL;
 
 	if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0)
 		return error;
 
-	if (!valid_repository_path(&path)) {
+	if (!valid_repository_path(&path, &common_path)) {
 		git_buf_free(&path);
-		giterr_set(GITERR_REPOSITORY, "Path is not a repository: %s", bare_path);
+		git_buf_free(&common_path);
+		giterr_set(GITERR_REPOSITORY, "path is not a repository: %s", bare_path);
 		return GIT_ENOTFOUND;
 	}
 
 	repo = repository_alloc();
 	GITERR_CHECK_ALLOC(repo);
 
-	repo->path_repository = git_buf_detach(&path);
-	GITERR_CHECK_ALLOC(repo->path_repository);
+	repo->gitdir = git_buf_detach(&path);
+	GITERR_CHECK_ALLOC(repo->gitdir);
+	repo->commondir = git_buf_detach(&common_path);
+	GITERR_CHECK_ALLOC(repo->commondir);
 
 	/* of course we're bare! */
 	repo->is_bare = 1;
+	repo->is_worktree = 0;
 	repo->workdir = NULL;
 
 	*repo_ptr = repo;
@@ -613,9 +703,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 +729,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);
 
@@ -673,7 +766,7 @@
 {
 	int error;
 	git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT,
-		link_path = GIT_BUF_INIT;
+		link_path = GIT_BUF_INIT, common_path = GIT_BUF_INIT;
 	git_repository *repo;
 	git_config *config = NULL;
 
@@ -684,7 +777,7 @@
 		*repo_ptr = NULL;
 
 	error = find_repo(
-		&path, &parent, &link_path, start_path, flags, ceiling_dirs);
+		&path, &parent, &link_path, &common_path, start_path, flags, ceiling_dirs);
 
 	if (error < 0 || !repo_ptr)
 		return error;
@@ -692,13 +785,24 @@
 	repo = repository_alloc();
 	GITERR_CHECK_ALLOC(repo);
 
-	repo->path_repository = git_buf_detach(&path);
-	GITERR_CHECK_ALLOC(repo->path_repository);
+	repo->gitdir = git_buf_detach(&path);
+	GITERR_CHECK_ALLOC(repo->gitdir);
 
 	if (link_path.size) {
-		repo->path_gitlink = git_buf_detach(&link_path);
-		GITERR_CHECK_ALLOC(repo->path_gitlink);
+		repo->gitlink = git_buf_detach(&link_path);
+		GITERR_CHECK_ALLOC(repo->gitlink);
 	}
+	if (common_path.size) {
+		repo->commondir = git_buf_detach(&common_path);
+		GITERR_CHECK_ALLOC(repo->commondir);
+	}
+
+	if ((error = git_buf_joinpath(&path, repo->gitdir, "gitdir")) < 0)
+		goto cleanup;
+	/* A 'gitdir' file inside a git directory is currently
+	 * only used when the repository is a working tree. */
+	if (git_path_exists(path.ptr))
+		repo->is_worktree = 1;
 
 	/*
 	 * We'd like to have the config, but git doesn't particularly
@@ -723,6 +827,7 @@
 	}
 
 cleanup:
+	git_buf_free(&path);
 	git_buf_free(&parent);
 	git_config_free(config);
 
@@ -740,6 +845,36 @@
 		repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL);
 }
 
+int git_repository_open_from_worktree(git_repository **repo_out, git_worktree *wt)
+{
+	git_buf path = GIT_BUF_INIT;
+	git_repository *repo = NULL;
+	int len, err;
+
+	assert(repo_out && wt);
+
+	*repo_out = NULL;
+	len = strlen(wt->gitlink_path);
+
+	if (len <= 4 || strcasecmp(wt->gitlink_path + len - 4, ".git")) {
+		err = -1;
+		goto out;
+	}
+
+	if ((err = git_buf_set(&path, wt->gitlink_path, len - 4)) < 0)
+		goto out;
+
+	if ((err = git_repository_open(&repo, path.ptr)) < 0)
+		goto out;
+
+	*repo_out = repo;
+
+out:
+	git_buf_free(&path);
+
+	return err;
+}
+
 int git_repository_wrap_odb(git_repository **repo_out, git_odb *odb)
 {
 	git_repository *repo;
@@ -765,7 +900,7 @@
 
 	git_buf_sanitize(out);
 
-	return find_repo(out, NULL, NULL, start_path, flags, ceiling_dirs);
+	return find_repo(out, NULL, NULL, NULL, start_path, flags, ceiling_dirs);
 }
 
 static int load_config(
@@ -785,8 +920,7 @@
 	if ((error = git_config_new(&cfg)) < 0)
 		return error;
 
-	error = git_buf_joinpath(
-		&config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO);
+	error = git_repository_item_path(&config_path, repo, GIT_REPOSITORY_ITEM_CONFIG);
 	if (error < 0)
 		goto on_error;
 
@@ -920,18 +1054,23 @@
 		git_buf odb_path = GIT_BUF_INIT;
 		git_odb *odb;
 
-		if ((error = git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR)) < 0)
+		if ((error = git_repository_item_path(&odb_path, repo,
+				GIT_REPOSITORY_ITEM_OBJECTS)) < 0 ||
+			(error = git_odb_new(&odb)) < 0)
 			return error;
 
-		error = git_odb_open(&odb, odb_path.ptr);
-		if (!error) {
-			GIT_REFCOUNT_OWN(odb, repo);
+		GIT_REFCOUNT_OWN(odb, repo);
 
-			odb = git__compare_and_swap(&repo->_odb, NULL, odb);
-			if (odb != NULL) {
-				GIT_REFCOUNT_OWN(odb, NULL);
-				git_odb_free(odb);
-			}
+		if ((error = git_odb__set_caps(odb, GIT_ODB_CAP_FROM_OWNER)) < 0 ||
+			(error = git_odb__add_default_backends(odb, odb_path.ptr, 0, 0)) < 0) {
+			git_odb_free(odb);
+			return error;
+		}
+
+		odb = git__compare_and_swap(&repo->_odb, NULL, odb);
+		if (odb != NULL) {
+			GIT_REFCOUNT_OWN(odb, NULL);
+			git_odb_free(odb);
 		}
 
 		git_buf_free(&odb_path);
@@ -1006,7 +1145,7 @@
 		git_buf index_path = GIT_BUF_INIT;
 		git_index *index;
 
-		if ((error = git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE)) < 0)
+		if ((error = git_buf_joinpath(&index_path, repo->gitdir, GIT_INDEX_FILE)) < 0)
 			return error;
 
 		error = git_index_open(&index, index_path.ptr);
@@ -1122,13 +1261,13 @@
 			prefixcmp = (error || ignorecase) ? git__prefixcmp_icase :
 				git__prefixcmp;
 
-			if (repo->path_gitlink &&
-				reserved_names_add8dot3(repo, repo->path_gitlink) < 0)
+			if (repo->gitlink &&
+				reserved_names_add8dot3(repo, repo->gitlink) < 0)
 				goto on_error;
 
-			if (repo->path_repository &&
-				prefixcmp(repo->path_repository, repo->workdir) == 0 &&
-				reserved_names_add8dot3(repo, repo->path_repository) < 0)
+			if (repo->gitdir &&
+				prefixcmp(repo->gitdir, repo->workdir) == 0 &&
+				reserved_names_add8dot3(repo, repo->gitdir) < 0)
 				goto on_error;
 		}
 	}
@@ -1177,7 +1316,7 @@
 
 	if (GIT_REPO_VERSION < version) {
 		giterr_set(GITERR_REPOSITORY,
-			"Unsupported repository version %d. Only versions up to %d are supported.",
+			"unsupported repository version %d. Only versions up to %d are supported.",
 			version, GIT_REPO_VERSION);
 		return -1;
 	}
@@ -1185,7 +1324,7 @@
 	return 0;
 }
 
-static int repo_init_create_head(const char *git_dir, const char *ref_name)
+int git_repository_create_head(const char *git_dir, const char *ref_name)
 {
 	git_buf ref_path = GIT_BUF_INIT;
 	git_filebuf ref = GIT_FILEBUF_INIT;
@@ -1271,12 +1410,12 @@
 	int fd;
 
 	if ((fd = p_creat(path, mode)) < 0) {
-		giterr_set(GITERR_OS, "Error while creating '%s'", path);
+		giterr_set(GITERR_OS, "error while creating '%s'", path);
 		return -1;
 	}
 
 	if (p_close(fd) < 0) {
-		giterr_set(GITERR_OS, "Error while closing '%s'", path);
+		giterr_set(GITERR_OS, "error while closing '%s'", path);
 		return -1;
 	}
 
@@ -1505,7 +1644,7 @@
 
 	if (error)
 		giterr_set(GITERR_OS,
-			"Failed to initialize repository with template '%s'", file);
+			"failed to initialize repository with template '%s'", file);
 
 	return error;
 }
@@ -1536,7 +1675,7 @@
 
 	if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) {
 		giterr_set(GITERR_REPOSITORY,
-			"Cannot overwrite gitlink file into path '%s'", in_dir);
+			"cannot overwrite gitlink file into path '%s'", in_dir);
 		error = GIT_EEXISTS;
 		goto cleanup;
 	}
@@ -1590,7 +1729,7 @@
 	if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) {
 		if (git_win32__set_hidden(repo_dir, true) < 0) {
 			giterr_set(GITERR_OS,
-				"Failed to mark Git repository folder as hidden");
+				"failed to mark Git repository folder as hidden");
 			return -1;
 		}
 	}
@@ -1744,7 +1883,7 @@
 			if (git_path_dirname_r(wd_path, repo_path->ptr) < 0)
 				return -1;
 		} else {
-			giterr_set(GITERR_REPOSITORY, "Cannot pick working directory"
+			giterr_set(GITERR_REPOSITORY, "cannot pick working directory"
 				" for non-bare repository that isn't a '.git' directory");
 			return -1;
 		}
@@ -1848,7 +1987,8 @@
 	git_repository_init_options *opts)
 {
 	int error;
-	git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT;
+	git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT,
+		common_path = GIT_BUF_INIT;
 	const char *wd;
 
 	assert(out && given_repo && opts);
@@ -1860,11 +2000,11 @@
 		goto cleanup;
 
 	wd = (opts->flags & GIT_REPOSITORY_INIT_BARE) ? NULL : git_buf_cstr(&wd_path);
-	if (valid_repository_path(&repo_path)) {
+	if (valid_repository_path(&repo_path, &common_path)) {
 
 		if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) {
 			giterr_set(GITERR_REPOSITORY,
-				"Attempt to reinitialize '%s'", given_repo);
+				"attempt to reinitialize '%s'", given_repo);
 			error = GIT_EEXISTS;
 			goto cleanup;
 		}
@@ -1881,7 +2021,7 @@
 				repo_path.ptr, wd, opts)) &&
 			!(error = repo_init_config(
 				repo_path.ptr, wd, opts->flags, opts->mode)))
-			error = repo_init_create_head(
+			error = git_repository_create_head(
 				repo_path.ptr, opts->initial_head);
 	}
 	if (error < 0)
@@ -1893,6 +2033,7 @@
 		error = repo_init_create_origin(*out, opts->origin_url);
 
 cleanup:
+	git_buf_free(&common_path);
 	git_buf_free(&repo_path);
 	git_buf_free(&wd_path);
 
@@ -1922,6 +2063,49 @@
 	return exists;
 }
 
+static int read_worktree_head(git_buf *out, git_repository *repo, const char *name)
+{
+	git_buf path = GIT_BUF_INIT;
+	int err;
+
+	assert(out && repo && name);
+
+	git_buf_clear(out);
+
+	if ((err = git_buf_printf(&path, "%s/worktrees/%s/HEAD", repo->commondir, name)) < 0)
+		goto out;
+	if (!git_path_exists(path.ptr))
+	{
+		err = -1;
+		goto out;
+	}
+
+	if ((err = git_futils_readbuffer(out, path.ptr)) < 0)
+		goto out;
+	git_buf_rtrim(out);
+
+out:
+	git_buf_free(&path);
+
+	return err;
+}
+
+int git_repository_head_detached_for_worktree(git_repository *repo, const char *name)
+{
+	git_buf buf = GIT_BUF_INIT;
+	int ret;
+
+	assert(repo && name);
+
+	if (read_worktree_head(&buf, repo, name) < 0)
+		return -1;
+
+	ret = git__strncmp(buf.ptr, GIT_SYMREF, strlen(GIT_SYMREF)) != 0;
+	git_buf_free(&buf);
+
+	return ret;
+}
+
 int git_repository_head(git_reference **head_out, git_repository *repo)
 {
 	git_reference *head;
@@ -1941,6 +2125,48 @@
 	return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error;
 }
 
+int git_repository_head_for_worktree(git_reference **out, git_repository *repo, const char *name)
+{
+	git_buf buf = GIT_BUF_INIT;
+	git_reference *head;
+	int err;
+
+	assert(out && repo && name);
+
+	*out = NULL;
+
+	if (git_repository_head_detached_for_worktree(repo, name))
+		return -1;
+	if ((err = read_worktree_head(&buf, repo, name)) < 0)
+		goto out;
+
+	/* We can only resolve symbolic references */
+	if (git__strncmp(buf.ptr, GIT_SYMREF, strlen(GIT_SYMREF)))
+	{
+		err = -1;
+		goto out;
+	}
+	git_buf_consume(&buf, buf.ptr + strlen(GIT_SYMREF));
+
+	if ((err = git_reference_lookup(&head, repo, buf.ptr)) < 0)
+		goto out;
+	if (git_reference_type(head) == GIT_REF_OID)
+	{
+		*out = head;
+		err = 0;
+		goto out;
+	}
+
+	err = git_reference_lookup_resolved(
+		out, repo, git_reference_symbolic_target(head), -1);
+	git_reference_free(head);
+
+out:
+	git_buf_free(&buf);
+
+	return err;
+}
+
 int git_repository_head_unborn(git_repository *repo)
 {
 	git_reference *ref = NULL;
@@ -1999,10 +2225,50 @@
 	return is_empty;
 }
 
+int git_repository_item_path(git_buf *out, git_repository *repo, git_repository_item_t item)
+{
+	const char *parent;
+
+	switch (items[item].parent) {
+		case GIT_REPOSITORY_ITEM_GITDIR:
+			parent = git_repository_path(repo);
+			break;
+		case GIT_REPOSITORY_ITEM_WORKDIR:
+			parent = git_repository_workdir(repo);
+			break;
+		case GIT_REPOSITORY_ITEM_COMMONDIR:
+			parent = git_repository_commondir(repo);
+			break;
+		default:
+			giterr_set(GITERR_INVALID, "Invalid item directory");
+			return -1;
+	}
+
+	if (parent == NULL) {
+		giterr_set(GITERR_INVALID, "Path cannot exist in repository");
+		return -1;
+	}
+
+	if (git_buf_sets(out, parent) < 0)
+		return -1;
+
+	if (items[item].name) {
+		if (git_buf_joinpath(out, parent, items[item].name) < 0)
+			return -1;
+	}
+
+	if (items[item].directory) {
+		if (git_path_to_dir(out) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
 const char *git_repository_path(git_repository *repo)
 {
 	assert(repo);
-	return repo->path_repository;
+	return repo->gitdir;
 }
 
 const char *git_repository_workdir(git_repository *repo)
@@ -2015,6 +2281,12 @@
 	return repo->workdir;
 }
 
+const char *git_repository_commondir(git_repository *repo)
+{
+	assert(repo);
+	return repo->commondir;
+}
+
 int git_repository_set_workdir(
 	git_repository *repo, const char *workdir, int update_gitlink)
 {
@@ -2065,6 +2337,12 @@
 	return repo->is_bare;
 }
 
+int git_repository_is_worktree(git_repository *repo)
+{
+	assert(repo);
+	return repo->is_worktree;
+}
+
 int git_repository_set_bare(git_repository *repo)
 {
 	int error;
@@ -2119,7 +2397,7 @@
 
 	git_oid_fmt(orig_head_str, orig_head);
 
-	if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_ORIG_HEAD_FILE)) == 0 &&
+	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_ORIG_HEAD_FILE)) == 0 &&
 		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) == 0 &&
 		(error = git_filebuf_printf(&file, "%.*s\n", GIT_OID_HEXSZ, orig_head_str)) == 0)
 		error = git_filebuf_commit(&file);
@@ -2140,13 +2418,13 @@
 
 	git_buf_sanitize(out);
 
-	if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
+	if (git_buf_joinpath(&path, repo->gitdir, GIT_MERGE_MSG_FILE) < 0)
 		return -1;
 
 	if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) {
 		if (errno == ENOENT)
 			error = GIT_ENOTFOUND;
-		giterr_set(GITERR_OS, "Could not access message file");
+		giterr_set(GITERR_OS, "could not access message file");
 	} else {
 		error = git_futils_readbuffer(out, git_buf_cstr(&path));
 	}
@@ -2161,7 +2439,7 @@
 	git_buf path = GIT_BUF_INIT;
 	int error;
 
-	if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
+	if (git_buf_joinpath(&path, repo->gitdir, GIT_MERGE_MSG_FILE) < 0)
 		return -1;
 
 	error = p_unlink(git_buf_cstr(&path));
@@ -2224,7 +2502,7 @@
 	}
 
 	if (!git__is_sizet(len)) {
-		giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
+		giterr_set(GITERR_OS, "file size overflow for 32-bit systems");
 		error = -1;
 		goto cleanup;
 	}
@@ -2282,6 +2560,12 @@
 	if (error < 0 && error != GIT_ENOTFOUND)
 		goto cleanup;
 
+	if (ref && current->type == GIT_REF_SYMBOLIC && git__strcmp(current->target.symbolic, ref->name) &&
+	    git_reference_is_branch(ref) && git_branch_is_checked_out(ref)) {
+		error = -1;
+		goto cleanup;
+	}
+
 	if (!error) {
 		if (git_reference_is_branch(ref)) {
 			error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE,
@@ -2397,7 +2681,7 @@
 
 	assert(repo);
 
-	if (git_buf_puts(&repo_path, repo->path_repository) < 0)
+	if (git_buf_puts(&repo_path, repo->gitdir) < 0)
 		return -1;
 
 	if (git_path_contains_file(&repo_path, GIT_REBASE_MERGE_INTERACTIVE_FILE))
@@ -2439,7 +2723,7 @@
 	for (error = 0, i = 0; !error && i < files_len; ++i) {
 		const char *path;
 
-		if (git_buf_joinpath(&buf, repo->path_repository, files[i]) < 0)
+		if (git_buf_joinpath(&buf, repo->gitdir, files[i]) < 0)
 			return -1;
 
 		path = git_buf_cstr(&buf);
@@ -2483,7 +2767,7 @@
 	struct stat st;
 	int error;
 
-	if ((error = git_buf_joinpath(&path, repo->path_repository, "shallow")) < 0)
+	if ((error = git_buf_joinpath(&path, repo->gitdir, "shallow")) < 0)
 		return error;
 
 	error = git_path_lstat(path.ptr, &st);
@@ -2538,3 +2822,31 @@
 
 	return 0;
 }
+
+int git_repository_submodule_cache_all(git_repository *repo)
+{
+	int error;
+
+	assert(repo);
+
+	if ((error = git_strmap_alloc(&repo->submodule_cache)))
+		return error;
+
+	error = git_submodule__map(repo, repo->submodule_cache);
+	return error;
+}
+
+int git_repository_submodule_cache_clear(git_repository *repo)
+{
+	git_submodule *sm;
+	assert(repo);
+	if (repo->submodule_cache == NULL) {
+		return 0;
+	}
+	git_strmap_foreach_value(repo->submodule_cache, sm, {
+		git_submodule_free(sm);
+	});
+	git_strmap_free(repo->submodule_cache);
+	repo->submodule_cache = 0;
+	return 0;
+}
diff --git a/src/repository.h b/src/repository.h
index fd679b4..33adfa6 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -46,6 +46,7 @@
 	GIT_CVAR_LOGALLREFUPDATES, /* core.logallrefupdates */
 	GIT_CVAR_PROTECTHFS,    /* core.protectHFS */
 	GIT_CVAR_PROTECTNTFS,   /* core.protectNTFS */
+	GIT_CVAR_FSYNCOBJECTFILES, /* core.fsyncObjectFiles */
 	GIT_CVAR_CACHE_MAX
 } git_cvar_cached;
 
@@ -106,6 +107,8 @@
 	GIT_PROTECTHFS_DEFAULT = GIT_CVAR_FALSE,
 	/* core.protectNTFS */
 	GIT_PROTECTNTFS_DEFAULT = GIT_CVAR_FALSE,
+	/* core.fsyncObjectFiles */
+	GIT_FSYNCOBJECTFILES_DEFAULT = GIT_CVAR_FALSE,
 } git_cvar_value;
 
 /* internal repository init flags */
@@ -126,8 +129,9 @@
 	git_attr_cache *attrcache;
 	git_diff_driver_registry *diff_drivers;
 
-	char *path_repository;
-	char *path_gitlink;
+	char *gitlink;
+	char *gitdir;
+	char *commondir;
 	char *workdir;
 	char *namespace;
 
@@ -137,12 +141,14 @@
 	git_array_t(git_buf) reserved_names;
 
 	unsigned is_bare:1;
+	unsigned is_worktree:1;
 
 	unsigned int lru_counter;
 
 	git_atomic attr_session_key;
 
 	git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
+	git_strmap *submodule_cache;
 };
 
 GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
@@ -151,6 +157,7 @@
 }
 
 int git_repository_head_tree(git_tree **tree, git_repository *repo);
+int git_repository_create_head(const char *git_dir, const char *ref_name);
 
 /*
  * Weak pointers to repository internals.
@@ -182,7 +189,7 @@
 
 	giterr_set(
 		GITERR_REPOSITORY,
-		"Cannot %s. This operation is not allowed against bare repositories.",
+		"cannot %s. This operation is not allowed against bare repositories.",
 		operation_name);
 
 	return GIT_EBAREREPO;
diff --git a/src/reset.c b/src/reset.c
index db0bfb3..066b5db 100644
--- a/src/reset.c
+++ b/src/reset.c
@@ -137,7 +137,7 @@
 		(git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE ||
 		 git_index_has_conflicts(index)))
 	{
-		giterr_set(GITERR_OBJECT, "%s (soft) in the middle of a merge.", ERROR_MSG);
+		giterr_set(GITERR_OBJECT, "%s (soft) in the middle of a merge", ERROR_MSG);
 		error = GIT_EUNMERGED;
 		goto cleanup;
 	}
diff --git a/src/revert.c b/src/revert.c
index c481e7d..747938f 100644
--- a/src/revert.c
+++ b/src/revert.c
@@ -27,7 +27,7 @@
 	git_buf file_path = GIT_BUF_INIT;
 	int error = 0;
 
-	if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_REVERT_HEAD_FILE)) >= 0 &&
+	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_REVERT_HEAD_FILE)) >= 0 &&
 		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_REVERT_FILE_MODE)) >= 0 &&
 		(error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0)
 		error = git_filebuf_commit(&file);
@@ -49,7 +49,7 @@
 	git_buf file_path = GIT_BUF_INIT;
 	int error = 0;
 
-	if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
+	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 ||
 		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_REVERT_FILE_MODE)) < 0 ||
 		(error = git_filebuf_printf(&file, "Revert \"%s\"\n\nThis reverts commit %s.\n",
 		commit_msgline, commit_oidstr)) < 0)
@@ -133,13 +133,13 @@
 	if (git_commit_parentcount(revert_commit) > 1) {
 		if (!mainline)
 			return revert_seterr(revert_commit,
-				"Mainline branch is not specified but %s is a merge commit");
+				"mainline branch is not specified but %s is a merge commit");
 
 		parent = mainline;
 	} else {
 		if (mainline)
 			return revert_seterr(revert_commit,
-				"Mainline branch specified but %s is not a merge commit");
+				"mainline branch specified but %s is not a merge commit");
 
 		parent = git_commit_parentcount(revert_commit);
 	}
diff --git a/src/revparse.c b/src/revparse.c
index aa7e0bd..d5511b4 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -46,7 +46,7 @@
 	int error;
 
 	if (*pattern == '\0') {
-		giterr_set(GITERR_REGEX, "Empty pattern");
+		giterr_set(GITERR_REGEX, "empty pattern");
 		return GIT_EINVALIDSPEC;
 	}
 
@@ -118,7 +118,7 @@
 	if ((error = maybe_describe(object_out, repo, spec)) != GIT_ENOTFOUND)
 		return error;
 
-	giterr_set(GITERR_REFERENCE, "Revspec '%s' not found.", spec);
+	giterr_set(GITERR_REFERENCE, "revspec '%s' not found", spec);
 	return GIT_ENOTFOUND;
 }
 
@@ -245,7 +245,7 @@
 notfound:
 	giterr_set(
 		GITERR_REFERENCE,
-		"Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ,
+		"reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ,
 		git_reference_name(ref), numentries, identifier);
 
 	git_reflog_free(reflog);
@@ -757,7 +757,7 @@
 					 * TODO: support merge-stage path lookup (":2:Makefile")
 					 * and plain index blob lookup (:i-am/a/blob)
 					 */
-					giterr_set(GITERR_INVALID, "Unimplemented");
+					giterr_set(GITERR_INVALID, "unimplemented");
 					error = GIT_ERROR;
 					goto cleanup;
 				}
@@ -816,7 +816,7 @@
 	if (error) {
 		if (error == GIT_EINVALIDSPEC)
 			giterr_set(GITERR_INVALID,
-				"Failed to parse revision specifier - Invalid pattern '%s'", spec);
+				"failed to parse revision specifier - Invalid pattern '%s'", spec);
 
 		git_object_free(base_rev);
 		git_reference_free(reference);
diff --git a/src/revwalk.c b/src/revwalk.c
index 0ada587..77fa9fd 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -15,8 +15,6 @@
 #include "merge.h"
 #include "vector.h"
 
-GIT__USE_OIDMAP
-
 git_commit_list_node *git_revwalk__commit_lookup(
 	git_revwalk *walk, const git_oid *oid)
 {
@@ -25,9 +23,9 @@
 	int ret;
 
 	/* lookup and reserve space if not already present */
-	pos = kh_get(oid, walk->commits, oid);
-	if (pos != kh_end(walk->commits))
-		return kh_value(walk->commits, pos);
+	pos = git_oidmap_lookup_index(walk->commits, oid);
+	if (git_oidmap_valid_index(walk->commits, pos))
+		return git_oidmap_value_at(walk->commits, pos);
 
 	commit = git_commit_list_alloc_node(walk);
 	if (commit == NULL)
@@ -35,9 +33,9 @@
 
 	git_oid_cpy(&commit->oid, oid);
 
-	pos = kh_put(oid, walk->commits, &commit->oid, &ret);
+	pos = git_oidmap_put(walk->commits, &commit->oid, &ret);
 	assert(ret != 0);
-	kh_value(walk->commits, pos) = commit;
+	git_oidmap_set_value_at(walk->commits, pos, commit);
 
 	return commit;
 }
@@ -61,7 +59,7 @@
 		if (from_glob)
 			return 0;
 
-		giterr_set(GITERR_INVALID, "Object is not a committish");
+		giterr_set(GITERR_INVALID, "object is not a committish");
 		return -1;
 	}
 	if (error < 0)
@@ -198,7 +196,7 @@
 
 	if (revspec.flags & GIT_REVPARSE_MERGE_BASE) {
 		/* TODO: support "<commit>...<commit>" */
-		giterr_set(GITERR_INVALID, "Symmetric differences not implemented in revwalk");
+		giterr_set(GITERR_INVALID, "symmetric differences not implemented in revwalk");
 		return GIT_EINVALIDSPEC;
 	}
 
@@ -233,9 +231,12 @@
 {
 	git_commit_list_node *next;
 
-	if ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) {
-		*object_out = next;
-		return 0;
+	while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) {
+		/* Some commits might become uninteresting after being added to the list */
+		if (!next->uninteresting) {
+			*object_out = next;
+			return 0;
+		}
 	}
 
 	giterr_clear();
@@ -290,7 +291,7 @@
 
 
 	while (parents) {
-		git_commit_list_node *commit = git_commit_list_pop(&parents);
+		commit = git_commit_list_pop(&parents);
 
 		while (commit) {
 			if (commit->uninteresting)
@@ -702,7 +703,7 @@
 
 	assert(walk);
 
-	kh_foreach_value(walk->commits, commit, {
+	git_oidmap_foreach_value(walk->commits, commit, {
 		commit->seen = 0;
 		commit->in_degree = 0;
 		commit->topo_delay = 0;
@@ -733,7 +734,7 @@
 
 	if (walk->hide_cb) {
 		/* There is already a callback added */
-		giterr_set(GITERR_INVALID, "There is already a callback added to hide commits in revision walker.");
+		giterr_set(GITERR_INVALID, "there is already a callback added to hide commits in revwalk");
 		return -1;
 	}
 
diff --git a/src/settings.c b/src/settings.c
index cb2317f..24e549e 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -15,6 +15,8 @@
 #include "cache.h"
 #include "global.h"
 #include "object.h"
+#include "refs.h"
+#include "transports/smart.h"
 
 void git_libgit2_version(int *major, int *minor, int *rev)
 {
@@ -29,7 +31,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
@@ -62,7 +66,7 @@
 		break;
 	default:
 		giterr_set(
-			GITERR_INVALID, "Invalid config path selector %d", config_level);
+			GITERR_INVALID, "invalid config path selector %d", config_level);
 	}
 
 	return val;
@@ -191,6 +195,10 @@
 		git_object__strict_input_validation = (va_arg(ap, int) != 0);
 		break;
 
+	case GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION:
+		git_reference__enable_symbolic_ref_target_validation = (va_arg(ap, int) != 0);
+		break;
+
 	case GIT_OPT_SET_SSL_CIPHERS:
 #ifdef GIT_OPENSSL
 		{
@@ -215,6 +223,14 @@
 		}
 		break;
 
+	case GIT_OPT_ENABLE_OFS_DELTA:
+		git_smart__ofs_delta_enabled = (va_arg(ap, int) != 0);
+		break;
+
+	case GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION:
+		git_object__synchronous_writing = (va_arg(ap, int) != 0);
+		break;
+
 	default:
 		giterr_set(GITERR_INVALID, "invalid option key");
 		error = -1;
diff --git a/src/sha1_lookup.c b/src/sha1_lookup.c
index c6b5613..ead26de 100644
--- a/src/sha1_lookup.c
+++ b/src/sha1_lookup.c
@@ -206,7 +206,7 @@
 #endif
 
 		if (!(lo <= mi && mi < hi)) {
-			giterr_set(GITERR_INVALID, "Assertion failure. Binary search invariant is false");
+			giterr_set(GITERR_INVALID, "assertion failure: binary search invariant is false");
 			return -1;
 		}
 
diff --git a/src/signature.c b/src/signature.c
index dcc3797..e792a52 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -25,7 +25,7 @@
 
 static int signature_error(const char *msg)
 {
-	giterr_set(GITERR_INVALID, "Failed to parse signature - %s", msg);
+	giterr_set(GITERR_INVALID, "failed to parse signature - %s", msg);
 	return -1;
 }
 
@@ -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/socket_stream.c b/src/socket_stream.c
index 71f4911..fca4117 100644
--- a/src/socket_stream.c
+++ b/src/socket_stream.c
@@ -57,7 +57,7 @@
 		return -1;
 
 	if (0 != WSACleanup()) {
-		giterr_set(GITERR_OS, "Winsock cleanup failed");
+		giterr_set(GITERR_OS, "winsock cleanup failed");
 		return -1;
 	}
 
@@ -82,13 +82,13 @@
 	WSADATA wsd;
 
 	if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
-		giterr_set(GITERR_OS, "Winsock init failed");
+		giterr_set(GITERR_OS, "winsock init failed");
 		return -1;
 	}
 
 	if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
 		WSACleanup();
-		giterr_set(GITERR_OS, "Winsock init failed");
+		giterr_set(GITERR_OS, "winsock init failed");
 		return -1;
 	}
 #endif
@@ -99,7 +99,7 @@
 
 	if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
 		giterr_set(GITERR_NET,
-			   "Failed to resolve address for %s: %s", st->host, p_gai_strerror(ret));
+			   "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret));
 		return -1;
 	}
 
@@ -121,7 +121,7 @@
 
 	/* Oops, we couldn't connect to any address */
 	if (s == INVALID_SOCKET && p == NULL) {
-		giterr_set(GITERR_OS, "Failed to connect to %s", st->host);
+		giterr_set(GITERR_OS, "failed to connect to %s", st->host);
 		p_freeaddrinfo(info);
 		return -1;
 	}
diff --git a/src/sortedcache.c b/src/sortedcache.c
index 5c2a167..cc322d4 100644
--- a/src/sortedcache.c
+++ b/src/sortedcache.c
@@ -1,7 +1,5 @@
 #include "sortedcache.h"
 
-GIT__USE_STRMAP
-
 int git_sortedcache_new(
 	git_sortedcache **out,
 	size_t item_path_offset,
@@ -27,7 +25,7 @@
 		goto fail;
 
 	if (git_rwlock_init(&sc->lock)) {
-		giterr_set(GITERR_OS, "Failed to initialize lock");
+		giterr_set(GITERR_OS, "failed to initialize lock");
 		goto fail;
 	}
 
@@ -162,7 +160,7 @@
 	GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
 
 	if (git_rwlock_wrlock(&sc->lock) < 0) {
-		giterr_set(GITERR_OS, "Unable to acquire write lock on cache");
+		giterr_set(GITERR_OS, "unable to acquire write lock on cache");
 		return -1;
 	}
 	return 0;
@@ -181,7 +179,7 @@
 	GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
 
 	if (git_rwlock_rdlock(&sc->lock) < 0) {
-		giterr_set(GITERR_OS, "Unable to acquire read lock on cache");
+		giterr_set(GITERR_OS, "unable to acquire read lock on cache");
 		return -1;
 	}
 	return 0;
@@ -200,6 +198,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 +206,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);
 
@@ -285,13 +292,13 @@
 	item_key = ((char *)item) + sc->item_path_offset;
 	memcpy(item_key, key, keylen);
 
-	pos = kh_put(str, sc->map, item_key, &error);
+	pos = git_strmap_put(sc->map, item_key, &error);
 	if (error < 0)
 		goto done;
 
 	if (!error)
-		kh_key(sc->map, pos) = item_key;
-	kh_val(sc->map, pos) = item;
+		git_strmap_set_key_at(sc->map, pos, item_key);
+	git_strmap_set_value_at(sc->map, pos, item);
 
 	error = git_vector_insert(&sc->items, item);
 	if (error < 0)
@@ -364,7 +371,7 @@
 	 */
 
 	if ((item = git_vector_get(&sc->items, pos)) == NULL) {
-		giterr_set(GITERR_INVALID, "Removing item out of range");
+		giterr_set(GITERR_INVALID, "removing item out of range");
 		return GIT_ENOTFOUND;
 	}
 
diff --git a/src/stash.c b/src/stash.c
index f5f4f36..d13220c 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -27,7 +27,7 @@
 
 static int create_error(int error, const char *msg)
 {
-	giterr_set(GITERR_STASH, "Cannot stash changes - %s", msg);
+	giterr_set(GITERR_STASH, "cannot stash changes - %s", msg);
 	return error;
 }
 
@@ -36,7 +36,7 @@
 	int error = git_repository_head(out, repo);
 
 	if (error == GIT_EUNBORNBRANCH)
-		return create_error(error, "You do not have the initial commit yet.");
+		return create_error(error, "you do not have the initial commit yet.");
 
 	return error;
 }
@@ -198,7 +198,7 @@
 			/* Unimplemented */
 			giterr_set(
 				GITERR_INVALID,
-				"Cannot update index. Unimplemented status (%d)",
+				"cannot update index. Unimplemented status (%d)",
 				delta->status);
 			return -1;
 		}
@@ -479,7 +479,7 @@
 		return 0;
 
 	if (!error)
-		return create_error(GIT_ENOTFOUND, "There is nothing to stash.");
+		return create_error(GIT_ENOTFOUND, "there is nothing to stash.");
 
 	return error;
 }
@@ -593,7 +593,7 @@
 	max = git_reflog_entrycount(reflog);
 	if (!max || index > max - 1) {
 		error = GIT_ENOTFOUND;
-		giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index);
+		giterr_set(GITERR_STASH, "no stashed state at position %" PRIuZ, index);
 		goto cleanup;
 	}
 
@@ -1036,7 +1036,7 @@
 
 	if (!max || index > max - 1) {
 		error = GIT_ENOTFOUND;
-		giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index);
+		giterr_set(GITERR_STASH, "no stashed state at position %" PRIuZ, index);
 		goto cleanup;
 	}
 
diff --git a/src/status.c b/src/status.c
index e610f5f..6752b56 100644
--- a/src/status.c
+++ b/src/status.c
@@ -243,13 +243,13 @@
 	GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options");
 
 	if (opts->show > GIT_STATUS_SHOW_WORKDIR_ONLY) {
-		giterr_set(GITERR_INVALID, "Unknown status 'show' option");
+		giterr_set(GITERR_INVALID, "unknown status 'show' option");
 		return -1;
 	}
 
 	if ((opts->flags & GIT_STATUS_OPT_NO_REFRESH) != 0 &&
 		(opts->flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) {
-		giterr_set(GITERR_INVALID, "Updating index from status "
+		giterr_set(GITERR_INVALID, "updating index from status "
 			"is not allowed when index refresh is disabled");
 		return -1;
 	}
@@ -510,13 +510,13 @@
 
 	if (error < 0 && sfi.ambiguous) {
 		giterr_set(GITERR_INVALID,
-			"Ambiguous path '%s' given to git_status_file", sfi.expected);
+			"ambiguous path '%s' given to git_status_file", sfi.expected);
 		error = GIT_EAMBIGUOUS;
 	}
 
 	if (!error && !sfi.count) {
 		giterr_set(GITERR_INVALID,
-			"Attempt to get status of nonexistent file '%s'", path);
+			"attempt to get status of nonexistent file '%s'", path);
 		error = GIT_ENOTFOUND;
 	}
 
diff --git a/src/strmap.c b/src/strmap.c
index b26a13d..de6826d 100644
--- a/src/strmap.c
+++ b/src/strmap.c
@@ -7,6 +7,101 @@
 
 #include "strmap.h"
 
+__KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)
+
+int git_strmap_alloc(git_strmap **map)
+{
+	if ((*map = kh_init(str)) == NULL) {
+		giterr_set_oom();
+		return -1;
+	}
+
+	return 0;
+}
+
+void git_strmap_free(git_strmap *map)
+{
+	kh_destroy(str, map);
+}
+
+void git_strmap_clear(git_strmap *map)
+{
+	kh_clear(str, map);
+}
+
+size_t git_strmap_num_entries(git_strmap *map)
+{
+	return kh_size(map);
+}
+
+size_t git_strmap_lookup_index(git_strmap *map, const char *key)
+{
+	return kh_get(str, map, key);
+}
+
+int git_strmap_valid_index(git_strmap *map, size_t idx)
+{
+	return idx != kh_end(map);
+}
+
+int git_strmap_exists(git_strmap *map, const char *key)
+{
+	return kh_get(str, map, key) != kh_end(map);
+}
+
+int git_strmap_has_data(git_strmap *map, size_t idx)
+{
+	return kh_exist(map, idx);
+}
+
+const char *git_strmap_key(git_strmap *map, size_t idx)
+{
+	return kh_key(map, idx);
+}
+
+void git_strmap_set_key_at(git_strmap *map, size_t idx, char *key)
+{
+	kh_val(map, idx) = key;
+}
+
+void *git_strmap_value_at(git_strmap *map, size_t idx)
+{
+	return kh_val(map, idx);
+}
+
+void git_strmap_set_value_at(git_strmap *map, size_t idx, void *value)
+{
+	kh_val(map, idx) = value;
+}
+
+void git_strmap_delete_at(git_strmap *map, size_t idx)
+{
+	kh_del(str, map, idx);
+}
+
+int git_strmap_put(git_strmap *map, const char *key, int *err)
+{
+	return kh_put(str, map, key, err);
+}
+
+void git_strmap_insert(git_strmap *map, const char *key, void *value, int *rval)
+{
+	khiter_t idx = kh_put(str, map, key, rval);
+
+	if ((*rval) >= 0) {
+		if ((*rval) == 0)
+			kh_key(map, idx) = key;
+		kh_val(map, idx) = value;
+	}
+}
+
+void git_strmap_delete(git_strmap *map, const char *key)
+{
+	khiter_t idx = git_strmap_lookup_index(map, key);
+	if (git_strmap_valid_index(map, idx))
+		git_strmap_delete_at(map, idx);
+}
+
 int git_strmap_next(
 	void **data,
 	git_strmap_iter* iter,
diff --git a/src/strmap.h b/src/strmap.h
index 5209847..802b924 100644
--- a/src/strmap.h
+++ b/src/strmap.h
@@ -20,49 +20,27 @@
 typedef khash_t(str) git_strmap;
 typedef khiter_t git_strmap_iter;
 
-#define GIT__USE_STRMAP \
-	__KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)
+int git_strmap_alloc(git_strmap **map);
+void git_strmap_free(git_strmap *map);
+void git_strmap_clear(git_strmap *map);
 
-#define git_strmap_alloc(hp) \
-	((*(hp) = kh_init(str)) == NULL) ? giterr_set_oom(), -1 : 0
+size_t git_strmap_num_entries(git_strmap *map);
 
-#define git_strmap_free(h)  kh_destroy(str, h), h = NULL
-#define git_strmap_clear(h) kh_clear(str, h)
+size_t git_strmap_lookup_index(git_strmap *map, const char *key);
+int git_strmap_valid_index(git_strmap *map, size_t idx);
 
-#define git_strmap_num_entries(h) kh_size(h)
+int git_strmap_exists(git_strmap *map, const char *key);
+int git_strmap_has_data(git_strmap *map, size_t idx);
 
-#define git_strmap_lookup_index(h, k)  kh_get(str, h, k)
-#define git_strmap_valid_index(h, idx) (idx != kh_end(h))
+const char *git_strmap_key(git_strmap *map, size_t idx);
+void git_strmap_set_key_at(git_strmap *map, size_t idx, char *key);
+void *git_strmap_value_at(git_strmap *map, size_t idx);
+void git_strmap_set_value_at(git_strmap *map, size_t idx, void *value);
+void git_strmap_delete_at(git_strmap *map, size_t idx);
 
-#define git_strmap_exists(h, k) (kh_get(str, h, k) != kh_end(h))
-#define git_strmap_has_data(h, idx) kh_exist(h, idx)
-
-#define git_strmap_key(h, idx)             kh_key(h, idx)
-#define git_strmap_value_at(h, idx)        kh_val(h, idx)
-#define git_strmap_set_value_at(h, idx, v) kh_val(h, idx) = v
-#define git_strmap_delete_at(h, idx)       kh_del(str, h, idx)
-
-#define git_strmap_insert(h, key, val, rval) do { \
-	khiter_t __pos = kh_put(str, h, key, &rval); \
-	if (rval >= 0) { \
-		if (rval == 0) kh_key(h, __pos) = key; \
-		kh_val(h, __pos) = val; \
-	} } while (0)
-
-#define git_strmap_insert2(h, key, val, oldv, rval) do { \
-	khiter_t __pos = kh_put(str, h, key, &rval); \
-	if (rval >= 0) { \
-		if (rval == 0) { \
-			oldv = kh_val(h, __pos); \
-			kh_key(h, __pos) = key; \
-		} else { oldv = NULL; } \
-		kh_val(h, __pos) = val; \
-	} } while (0)
-
-#define git_strmap_delete(h, key) do { \
-	khiter_t __pos = git_strmap_lookup_index(h, key); \
-	if (git_strmap_valid_index(h, __pos)) \
-		git_strmap_delete_at(h, __pos); } while (0)
+int git_strmap_put(git_strmap *map, const char *key, int *err);
+void git_strmap_insert(git_strmap *map, const char *key, void *value, int *rval);
+void git_strmap_delete(git_strmap *map, const char *key);
 
 #define git_strmap_foreach		kh_foreach
 #define git_strmap_foreach_value	kh_foreach_value
diff --git a/src/submodule.c b/src/submodule.c
index 86ad53b..ddd4b06 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -22,6 +22,7 @@
 #include "iterator.h"
 #include "path.h"
 #include "index.h"
+#include "worktree.h"
 
 #define GIT_MODULES_FILE ".gitmodules"
 
@@ -124,8 +125,8 @@
 		return;
 
 	giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ?
-		"No submodule named '%s'" :
-		"Submodule '%s' has not been added yet", name);
+		"no submodule named '%s'" :
+		"submodule '%s' has not been added yet", name);
 }
 
 typedef struct {
@@ -149,40 +150,52 @@
 }
 
 /**
- * Find out the name of a submodule from its path
+ * Release the name map returned by 'load_submodule_names'.
  */
-static int name_from_path(git_buf *out, git_config *cfg, const char *path)
+static void free_submodule_names(git_strmap *names)
+{
+	git_buf *name = 0;
+	if (names == NULL)
+		return;
+	git_strmap_foreach_value(names, name, {
+		git__free(name);
+	});
+	git_strmap_free(names);
+	return;
+}
+
+/**
+ * Map submodule paths to names.
+ * TODO: for some use-cases, this might need case-folding on a
+ * case-insensitive filesystem
+ */
+static int load_submodule_names(git_strmap *out, git_config *cfg)
 {
 	const char *key = "submodule\\..*\\.path";
 	git_config_iterator *iter;
 	git_config_entry *entry;
-	int error;
+	git_buf buf = GIT_BUF_INIT;
+	int rval;
+	int error = 0;
 
 	if ((error = git_config_iterator_glob_new(&iter, cfg, key)) < 0)
 		return error;
 
-	while ((error = git_config_next(&entry, iter)) == 0) {
+	while (git_config_next(&entry, iter) == 0) {
 		const char *fdot, *ldot;
-		/* TODO: this should maybe be strcasecmp on a case-insensitive fs */
-		if (strcmp(path, entry->value) != 0)
-			continue;
-
 		fdot = strchr(entry->name, '.');
 		ldot = strrchr(entry->name, '.');
 
-		git_buf_clear(out);
-		git_buf_put(out, fdot + 1, ldot - fdot - 1);
-		goto cleanup;
+		git_buf_put(&buf, fdot + 1, ldot - fdot - 1);
+		git_strmap_insert(out, entry->value, git_buf_detach(&buf), &rval);
+		if (rval < 0) {
+			giterr_set(GITERR_NOMEMORY, "error inserting submodule into hash table");
+			return -1;
+		}
 	}
 
-	if (error == GIT_ITEROVER) {
-		giterr_set(GITERR_SUBMODULE, "could not find a submodule name for '%s'", path);
-		error = GIT_ENOTFOUND;
-	}
-
-cleanup:
 	git_config_iterator_free(iter);
-	return error;
+	return 0;
 }
 
 int git_submodule_lookup(
@@ -196,6 +209,17 @@
 
 	assert(repo && name);
 
+	if (repo->submodule_cache != NULL) {
+		khiter_t pos = git_strmap_lookup_index(repo->submodule_cache, name);
+		if (git_strmap_valid_index(repo->submodule_cache, pos)) {
+			if (out) {
+				*out = git_strmap_value_at(repo->submodule_cache, pos);
+				GIT_REFCOUNT_INC(*out);
+			}
+			return 0;
+		}
+	}
+
 	if ((error = submodule_alloc(&sm, repo, name)) < 0)
 		return error;
 
@@ -306,7 +330,7 @@
 	if ((error = submodule_alloc(&sm, repo, name)) < 0)
 		return error;
 
-	pos = kh_put(str, map, sm->name, &error);
+	pos = git_strmap_put(map, sm->name, &error);
 	/* nobody can beat us to adding it */
 	assert(error != 0);
 	if (error < 0) {
@@ -324,89 +348,108 @@
 
 static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cfg)
 {
-       int error;
-       git_iterator *i;
-       const git_index_entry *entry;
-       git_buf name = GIT_BUF_INIT;
+	int error;
+	git_iterator *i = NULL;
+	const git_index_entry *entry;
+	git_strmap *names = 0;
 
-       if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0)
-               return error;
+	git_strmap_alloc(&names);
+	if ((error = load_submodule_names(names, cfg)))
+		goto done;
 
-       while (!(error = git_iterator_advance(&entry, i))) {
-               khiter_t pos = git_strmap_lookup_index(map, entry->path);
-               git_submodule *sm;
+	if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0)
+		goto done;
 
-	       git_buf_clear(&name);
-	       if (!name_from_path(&name, cfg, entry->path)) {
-		       git_strmap_lookup_index(map, name.ptr);
-	       }
+	while (!(error = git_iterator_advance(&entry, i))) {
+		khiter_t pos = git_strmap_lookup_index(map, entry->path);
+		git_submodule *sm;
 
-               if (git_strmap_valid_index(map, pos)) {
-                       sm = git_strmap_value_at(map, pos);
+		if (git_strmap_valid_index(map, pos)) {
+			sm = git_strmap_value_at(map, pos);
 
-                       if (S_ISGITLINK(entry->mode))
-                               submodule_update_from_index_entry(sm, entry);
-                       else
-                               sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
-               } else if (S_ISGITLINK(entry->mode)) {
-                       if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name.ptr ? name.ptr : entry->path)) {
-                               submodule_update_from_index_entry(sm, entry);
-                               git_submodule_free(sm);
-                       }
-               }
-       }
+			if (S_ISGITLINK(entry->mode))
+				submodule_update_from_index_entry(sm, entry);
+			else
+				sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
+		} else if (S_ISGITLINK(entry->mode)) {
+			khiter_t name_pos;
+			const char *name;
 
-       if (error == GIT_ITEROVER)
-               error = 0;
+			name_pos = git_strmap_lookup_index(names, entry->path);
+			if (git_strmap_valid_index(names, name_pos)) {
+				name = git_strmap_value_at(names, name_pos);
+			} else {
+				name = entry->path;
+			}
 
-       git_buf_free(&name);
-       git_iterator_free(i);
+			if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name)) {
+				submodule_update_from_index_entry(sm, entry);
+				git_submodule_free(sm);
+			}
+		}
+	}
 
-       return error;
+	if (error == GIT_ITEROVER)
+		error = 0;
+
+done:
+	git_iterator_free(i);
+	free_submodule_names(names);
+
+	return error;
 }
 
 static int submodules_from_head(git_strmap *map, git_tree *head, git_config *cfg)
 {
-       int error;
-       git_iterator *i;
-       const git_index_entry *entry;
-       git_buf name = GIT_BUF_INIT;
+	int error;
+	git_iterator *i = NULL;
+	const git_index_entry *entry;
+	git_strmap *names = 0;
+	git_strmap_alloc(&names);
+	if ((error = load_submodule_names(names, cfg)))
+		goto done;
 
-       if ((error = git_iterator_for_tree(&i, head, NULL)) < 0)
-               return error;
+	if ((error = git_iterator_for_tree(&i, head, NULL)) < 0)
+		goto done;
 
-       while (!(error = git_iterator_advance(&entry, i))) {
-               khiter_t pos = git_strmap_lookup_index(map, entry->path);
-               git_submodule *sm;
+	while (!(error = git_iterator_advance(&entry, i))) {
+		khiter_t pos = git_strmap_lookup_index(map, entry->path);
+		git_submodule *sm;
 
-	       git_buf_clear(&name);
-	       if (!name_from_path(&name, cfg, entry->path)) {
-		       git_strmap_lookup_index(map, name.ptr);
-	       }
+		if (git_strmap_valid_index(map, pos)) {
+			sm = git_strmap_value_at(map, pos);
 
-               if (git_strmap_valid_index(map, pos)) {
-                       sm = git_strmap_value_at(map, pos);
+			if (S_ISGITLINK(entry->mode))
+				submodule_update_from_head_data(sm, entry->mode, &entry->id);
+			else
+				sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
+		} else if (S_ISGITLINK(entry->mode)) {
+			khiter_t name_pos;
+			const char *name;
 
-                       if (S_ISGITLINK(entry->mode))
-                               submodule_update_from_head_data(sm, entry->mode, &entry->id);
-                       else
-                               sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
-               } else if (S_ISGITLINK(entry->mode)) {
-                       if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name.ptr ? name.ptr : entry->path)) {
-                               submodule_update_from_head_data(
-                                       sm, entry->mode, &entry->id);
-                               git_submodule_free(sm);
-                       }
-               }
-       }
+			name_pos = git_strmap_lookup_index(names, entry->path);
+			if (git_strmap_valid_index(names, name_pos)) {
+				name = git_strmap_value_at(names, name_pos);
+			} else {
+				name = entry->path;
+			}
 
-       if (error == GIT_ITEROVER)
-               error = 0;
+			if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name)) {
+				submodule_update_from_head_data(
+					sm, entry->mode, &entry->id);
+				git_submodule_free(sm);
+			}
+		}
+	}
 
-       git_buf_free(&name);
-       git_iterator_free(i);
+	if (error == GIT_ITEROVER)
+		error = 0;
 
-       return error;
+done:
+	git_iterator_free(i);
+	free_submodule_names(names);
+
+	return error;
 }
 
 /* If have_sm is true, sm is populated, otherwise map an repo are. */
@@ -416,7 +459,7 @@
 	git_repository *repo;
 } lfc_data;
 
-static int all_submodules(git_repository *repo, git_strmap *map)
+int git_submodule__map(git_repository *repo, git_strmap *map)
 {
 	int error = 0;
 	git_index *idx = NULL;
@@ -470,12 +513,12 @@
 			goto cleanup;
 	}
 	/* add back submodule information from index */
-	if (idx) {
+	if (mods && idx) {
 		if ((error = submodules_from_index(map, idx, mods)) < 0)
 			goto cleanup;
 	}
 	/* add submodule information from HEAD */
-	if (head) {
+	if (mods && head) {
 		if ((error = submodules_from_head(map, head, mods)) < 0)
 			goto cleanup;
 	}
@@ -509,11 +552,11 @@
 	if ((error = git_strmap_alloc(&submodules)) < 0)
 		return error;
 
-	if ((error = all_submodules(repo, submodules)) < 0)
+	if ((error = git_submodule__map(repo, submodules)) < 0)
 		goto done;
 
 	if (!(error = git_vector_init(
-			&snapshot, kh_size(submodules), submodule_cmp))) {
+			&snapshot, git_strmap_num_entries(submodules), submodule_cmp))) {
 
 		git_strmap_foreach_value(submodules, sm, {
 			if ((error = git_vector_insert(&snapshot, sm)) < 0)
@@ -574,8 +617,10 @@
 	 * Old style: sub-repo goes directly into repo/<name>/.git/
 	 */
 	 if (use_gitlink) {
-		error = git_buf_join3(
-			&repodir, '/', git_repository_path(parent_repo), "modules", path);
+		error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES);
+		if (error < 0)
+			goto cleanup;
+		error = git_buf_joinpath(&repodir, repodir.ptr, path);
 		if (error < 0)
 			goto cleanup;
 
@@ -618,7 +663,7 @@
 		giterr_clear();
 	else {
 		giterr_set(GITERR_SUBMODULE,
-			"Attempt to add submodule '%s' that already exists", path);
+			"attempt to add submodule '%s' that already exists", path);
 		return GIT_EEXISTS;
 	}
 
@@ -628,7 +673,7 @@
 		path += strlen(git_repository_workdir(repo));
 
 	if (git_path_root(path) >= 0) {
-		giterr_set(GITERR_SUBMODULE, "Submodule path must be a relative path");
+		giterr_set(GITERR_SUBMODULE, "submodule path must be a relative path");
 		error = -1;
 		goto cleanup;
 	}
@@ -637,7 +682,7 @@
 
 	if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) {
 		giterr_set(GITERR_SUBMODULE,
-			"Adding submodules to a bare repository is not supported");
+			"adding submodules to a bare repository is not supported");
 		return -1;
 	}
 
@@ -758,7 +803,7 @@
 	/* read stat information for submodule working directory */
 	if (p_stat(path.ptr, &st) < 0) {
 		giterr_set(GITERR_SUBMODULE,
-			"Cannot add submodule without working directory");
+			"cannot add submodule without working directory");
 		error = -1;
 		goto cleanup;
 	}
@@ -771,7 +816,7 @@
 	/* calling git_submodule_open will have set sm->wd_oid if possible */
 	if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
 		giterr_set(GITERR_SUBMODULE,
-			"Cannot add submodule without HEAD to index");
+			"cannot add submodule without HEAD to index");
 		error = -1;
 		goto cleanup;
 	}
@@ -861,7 +906,7 @@
 	} else if (strchr(url, ':') != NULL || url[0] == '/') {
 		error = git_buf_sets(out, url);
 	} else {
-		giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL");
+		giterr_set(GITERR_SUBMODULE, "invalid format for submodule URL");
 		error = -1;
 	}
 
@@ -1042,8 +1087,10 @@
 	 * <repo-dir>/modules/<name>/ with a gitlink in the
 	 * sub-repo workdir directory to that repository.
 	 */
-	error = git_buf_join3(
-		&repodir, '/', git_repository_path(parent_repo), "modules", path);
+	error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES);
+	if (error < 0)
+		goto cleanup;
+	error = git_buf_joinpath(&repodir, repodir.ptr, path);
 	if (error < 0)
 		goto cleanup;
 
@@ -1133,7 +1180,7 @@
 				goto done;
 
 			if (!init) {
-				giterr_set(GITERR_SUBMODULE, "Submodule is not initialized.");
+				giterr_set(GITERR_SUBMODULE, "submodule is not initialized");
 				error = GIT_ERROR;
 				goto done;
 			}
@@ -1160,13 +1207,14 @@
 		 * will checkout the specific commit manually.
 		 */
 		clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
-		update_options.checkout_opts.checkout_strategy = update_options.clone_checkout_strategy;
 
 		if ((error = git_clone(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 ||
 			(error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0 ||
 			(error = git_checkout_head(sub_repo, &update_options.checkout_opts)) != 0)
 			goto done;
 	} else {
+		const git_oid *oid;
+
 		/**
 		 * Work dir is initialized - look up the commit in the parent repository's index,
 		 * update the workdir contents of the subrepository, and set the subrepository's
@@ -1175,8 +1223,14 @@
 		if ((error = git_submodule_open(&sub_repo, sm)) < 0)
 			goto done;
 
+		if ((oid = git_submodule_index_id(sm)) == NULL) {
+			giterr_set(GITERR_SUBMODULE, "could not get ID of submodule in index");
+			error = -1;
+			goto done;
+		}
+
 		/* Look up the target commit in the submodule. */
-		if ((error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJ_COMMIT)) < 0) {
+		if ((error = git_object_lookup(&target_commit, sub_repo, oid, GIT_OBJ_COMMIT)) < 0) {
 			/* If it isn't found then fetch and try again. */
 			if (error != GIT_ENOTFOUND || !update_options.allow_fetch ||
 				(error = lookup_default_remote(&remote, sub_repo)) < 0 ||
@@ -1215,7 +1269,7 @@
 
 	if (!sm->url) {
 		giterr_set(GITERR_SUBMODULE,
-			"No URL configured for submodule '%s'", sm->name);
+			"no URL configured for submodule '%s'", sm->name);
 		return -1;
 	}
 
@@ -1259,7 +1313,7 @@
 
 	if (!sm->url) {
 		giterr_set(GITERR_SUBMODULE,
-			"No URL configured for submodule '%s'", sm->name);
+			"no URL configured for submodule '%s'", sm->name);
 		return -1;
 	}
 
@@ -1502,13 +1556,22 @@
 		return 0;
 	}
 
-	/* refresh the index OID */
-	if (submodule_update_index(sm) < 0)
-		return -1;
+	/* If the user has requested caching submodule state, performing these
+	 * expensive operations (especially `submodule_update_head`, which is
+	 * bottlenecked on `git_repository_head_tree`) eliminates much of the
+	 * advantage.  We will, therefore, interpret the request for caching to
+	 * apply here to and skip them.
+	 */
 
-	/* refresh the HEAD OID */
-	if (submodule_update_head(sm) < 0)
-		return -1;
+	if (sm->repo->submodule_cache == NULL) {
+		/* refresh the index OID */
+		if (submodule_update_index(sm) < 0)
+			return -1;
+
+		/* refresh the HEAD OID */
+		if (submodule_update_head(sm) < 0)
+			return -1;
+	}
 
 	/* for ignore == dirty, don't scan the working directory */
 	if (ign == GIT_SUBMODULE_IGNORE_DIRTY) {
@@ -1566,7 +1629,6 @@
 		location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL);
 }
 
-
 /*
  * INTERNAL FUNCTIONS
  */
@@ -1578,7 +1640,7 @@
 	git_submodule *sm;
 
 	if (!name || !(namelen = strlen(name))) {
-		giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
+		giterr_set(GITERR_SUBMODULE, "invalid submodule name");
 		return -1;
 	}
 
@@ -1630,7 +1692,7 @@
 static int submodule_config_error(const char *property, const char *value)
 {
 	giterr_set(GITERR_INVALID,
-		"Invalid value for submodule '%s' property: '%s'", property, value);
+		"invalid value for submodule '%s' property: '%s'", property, value);
 	return -1;
 }
 
@@ -1813,7 +1875,7 @@
 		goto done;
 	}
 
-	git_strmap_insert(map, sm->name, sm, error);
+	git_strmap_insert(map, sm->name, sm, &error);
 	assert(error != 0);
 	if (error < 0)
 		goto done;
@@ -1968,7 +2030,7 @@
 	if (error == GIT_ENOTFOUND)
 		giterr_set(
 			GITERR_SUBMODULE,
-			"Cannot get default remote for submodule - no local tracking "
+			"cannot get default remote for submodule - no local tracking "
 			"branch for HEAD and origin does not exist");
 
 	return error;
@@ -1977,17 +2039,28 @@
 static int get_url_base(git_buf *url, git_repository *repo)
 {
 	int error;
+	git_worktree *wt = NULL;
 	git_remote *remote = NULL;
 
-	if (!(error = lookup_default_remote(&remote, repo))) {
+	if ((error = lookup_default_remote(&remote, repo)) == 0) {
 		error = git_buf_sets(url, git_remote_url(remote));
-		git_remote_free(remote);
-	}
-	else if (error == GIT_ENOTFOUND) {
-		/* if repository does not have a default remote, use workdir instead */
+		goto out;
+	} else if (error != GIT_ENOTFOUND)
+		goto out;
+	else
 		giterr_clear();
+
+	/* if repository does not have a default remote, use workdir instead */
+	if (git_repository_is_worktree(repo)) {
+		if ((error = git_worktree_open_from_repository(&wt, repo)) < 0)
+			goto out;
+		error = git_buf_sets(url, wt->parent_path);
+	} else
 		error = git_buf_sets(url, git_repository_workdir(repo));
-	}
+
+out:
+	git_remote_free(remote);
+	git_worktree_free(wt);
 
 	return error;
 }
diff --git a/src/submodule.h b/src/submodule.h
index 2ef2031..456a939 100644
--- a/src/submodule.h
+++ b/src/submodule.h
@@ -143,4 +143,7 @@
 extern int git_submodule_parse_update(
 	git_submodule_update_t *out, const char *value);
 
+extern int git_submodule__map(
+	git_repository *repo,
+	git_strmap *map);
 #endif
diff --git a/src/sysdir.c b/src/sysdir.c
index 29e53e2..ed11221 100644
--- a/src/sysdir.c
+++ b/src/sysdir.c
@@ -150,7 +150,7 @@
 	GITERR_CHECK_ERROR(git_sysdir_get(&path, which));
 
 	if (!out || path->size >= outlen) {
-		giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path");
+		giterr_set(GITERR_NOMEMORY, "buffer is too short for the path");
 		return GIT_EBUFS;
 	}
 
@@ -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 */
@@ -241,7 +241,7 @@
 
 done:
 	git_buf_free(path);
-	giterr_set(GITERR_OS, "The %s file '%s' doesn't exist", label, name);
+	giterr_set(GITERR_OS, "the %s file '%s' doesn't exist", label, name);
 	return GIT_ENOTFOUND;
 }
 
diff --git a/src/tag.c b/src/tag.c
index fe840fe..2bf23fc 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -61,7 +61,7 @@
 
 static int tag_error(const char *str)
 {
-	giterr_set(GITERR_TAG, "Failed to parse tag. %s", str);
+	giterr_set(GITERR_TAG, "failed to parse tag: %s", str);
 	return -1;
 }
 
@@ -76,13 +76,13 @@
 	char *search;
 
 	if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)
-		return tag_error("Object field invalid");
+		return tag_error("object field invalid");
 
 	if (buffer + 5 >= buffer_end)
-		return tag_error("Object too short");
+		return tag_error("object too short");
 
 	if (memcmp(buffer, "type ", 5) != 0)
-		return tag_error("Type field not found");
+		return tag_error("type field not found");
 	buffer += 5;
 
 	tag->type = GIT_OBJ_BAD;
@@ -91,7 +91,7 @@
 		size_t type_length = strlen(tag_types[i]);
 
 		if (buffer + type_length >= buffer_end)
-			return tag_error("Object too short");
+			return tag_error("object too short");
 
 		if (memcmp(buffer, tag_types[i], type_length) == 0) {
 			tag->type = i;
@@ -101,19 +101,19 @@
 	}
 
 	if (tag->type == GIT_OBJ_BAD)
-		return tag_error("Invalid object type");
+		return tag_error("invalid object type");
 
 	if (buffer + 4 >= buffer_end)
-		return tag_error("Object too short");
+		return tag_error("object too short");
 
 	if (memcmp(buffer, "tag ", 4) != 0)
-		return tag_error("Tag field not found");
+		return tag_error("tag field not found");
 
 	buffer += 4;
 
 	search = memchr(buffer, '\n', buffer_end - buffer);
 	if (search == NULL)
-		return tag_error("Object too short");
+		return tag_error("object too short");
 
 	text_len = search - buffer;
 
@@ -234,7 +234,7 @@
 
 on_error:
 	git_buf_free(&tag);
-	giterr_set(GITERR_OBJECT, "Failed to create tag annotation.");
+	giterr_set(GITERR_OBJECT, "failed to create tag annotation");
 	return -1;
 }
 
@@ -257,7 +257,7 @@
 	assert(!create_tag_annotation || (tagger && message));
 
 	if (git_object_owner(target) != repo) {
-		giterr_set(GITERR_INVALID, "The given target does not belong to this repository");
+		giterr_set(GITERR_INVALID, "the given target does not belong to this repository");
 		return -1;
 	}
 
@@ -269,7 +269,7 @@
 	 *	reference unless overwriting has explicitly been requested **/
 	if (error == 0 && !allow_ref_overwrite) {
 		git_buf_free(&ref_name);
-		giterr_set(GITERR_TAG, "Tag already exists");
+		giterr_set(GITERR_TAG, "tag already exists");
 		return GIT_EEXISTS;
 	}
 
@@ -349,7 +349,7 @@
 		goto on_error;
 
 	if (tag.type != target_obj->cached.type) {
-		giterr_set(GITERR_TAG, "The type for the given target is invalid");
+		giterr_set(GITERR_TAG, "the type for the given target is invalid");
 		goto on_error;
 	}
 
@@ -366,7 +366,7 @@
 	/** Ensure the tag name doesn't conflict with an already existing
 	 *	reference unless overwriting has explicitly been requested **/
 	if (error == 0 && !allow_ref_overwrite) {
-		giterr_set(GITERR_TAG, "Tag already exists");
+		giterr_set(GITERR_TAG, "tag already exists");
 		return GIT_EEXISTS;
 	}
 
diff --git a/src/thread-utils.h b/src/thread-utils.h
index f016198..2df2aeb 100644
--- a/src/thread-utils.h
+++ b/src/thread-utils.h
@@ -7,6 +7,12 @@
 #ifndef INCLUDE_thread_utils_h__
 #define INCLUDE_thread_utils_h__
 
+#if defined(__GNUC__) && defined(GIT_THREADS)
+# if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1))
+#  error Atomic primitives do not exist on this version of gcc; configure libgit2 with -DTHREADSAFE=OFF
+# endif
+#endif
+
 /* Common operations even if threading has been disabled */
 typedef struct {
 #if defined(GIT_WIN32)
diff --git a/src/trace.c b/src/trace.c
index ee5039f..0f21428 100644
--- a/src/trace.c
+++ b/src/trace.c
@@ -32,7 +32,7 @@
 	GIT_UNUSED(callback);
 
 	giterr_set(GITERR_INVALID,
-		"This version of libgit2 was not built with tracing.");
+		"this version of libgit2 was not built with tracing.");
 	return -1;
 #endif
 }
diff --git a/src/transaction.c b/src/transaction.c
index 2c8a1e8..3d3f35a 100644
--- a/src/transaction.c
+++ b/src/transaction.c
@@ -19,8 +19,6 @@
 #include "git2/sys/refs.h"
 #include "git2/sys/refdb_backend.h"
 
-GIT__USE_STRMAP
-
 typedef enum {
 	TRANSACTION_NONE,
 	TRANSACTION_REFS,
@@ -120,7 +118,7 @@
 	if ((error = git_refdb_lock(&node->payload, tx->db, refname)) < 0)
 		return error;
 
-	git_strmap_insert(tx->locks, node->name, node, error);
+	git_strmap_insert(tx->locks, node->name, node, &error);
 	if (error < 0) 
 		goto cleanup;
 
@@ -323,7 +321,6 @@
 int git_transaction_commit(git_transaction *tx)
 {
 	transaction_node *node;
-	git_strmap_iter pos;
 	int error = 0;
 
 	assert(tx);
@@ -335,11 +332,7 @@
 		return error;
 	}
 
-	for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) {
-		if (!git_strmap_has_data(tx->locks, pos))
-			continue;
-
-		node = git_strmap_value_at(tx->locks, pos);
+	git_strmap_foreach_value(tx->locks, node, {
 		if (node->reflog) {
 			if ((error = tx->db->backend->reflog_write(tx->db->backend, node->reflog)) < 0)
 				return error;
@@ -349,7 +342,7 @@
 			if ((error = update_target(tx->db, node)) < 0)
 				return error;
 		}
-	}
+	});
 
 	return 0;
 }
@@ -358,7 +351,6 @@
 {
 	transaction_node *node;
 	git_pool pool;
-	git_strmap_iter pos;
 
 	assert(tx);
 
@@ -373,16 +365,12 @@
 	}
 
 	/* start by unlocking the ones we've left hanging, if any */
-	for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) {
-		if (!git_strmap_has_data(tx->locks, pos))
-			continue;
-
-		node = git_strmap_value_at(tx->locks, pos);
+	git_strmap_foreach_value(tx->locks, node, {
 		if (node->committed)
 			continue;
 
 		git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL);
-	}
+	});
 
 	git_refdb_free(tx->db);
 	git_strmap_free(tx->locks);
diff --git a/src/transport.c b/src/transport.c
index f08d2dc..b661653 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -121,7 +121,7 @@
 	int error;
 
 	if ((error = transport_find_fn(&fn, url, &param)) == GIT_ENOTFOUND) {
-		giterr_set(GITERR_NET, "Unsupported URL protocol");
+		giterr_set(GITERR_NET, "unsupported URL protocol");
 		return -1;
 	} else if (error < 0)
 		return error;
diff --git a/src/transports/auth_negotiate.c b/src/transports/auth_negotiate.c
index 8b99fc7..7c868c9 100644
--- a/src/transports/auth_negotiate.c
+++ b/src/transports/auth_negotiate.c
@@ -107,13 +107,13 @@
 	challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
 
 	if (challenge_len < 9) {
-		giterr_set(GITERR_NET, "No negotiate challenge sent from server");
+		giterr_set(GITERR_NET, "no negotiate challenge sent from server");
 		error = -1;
 		goto done;
 	} else if (challenge_len > 9) {
 		if (git_buf_decode_base64(&input_buf,
 				ctx->challenge + 10, challenge_len - 10) < 0) {
-			giterr_set(GITERR_NET, "Invalid negotiate challenge from server");
+			giterr_set(GITERR_NET, "invalid negotiate challenge from server");
 			error = -1;
 			goto done;
 		}
@@ -122,7 +122,7 @@
 		input_token.length = input_buf.size;
 		input_token_ptr = &input_token;
 	} else if (ctx->gss_context != GSS_C_NO_CONTEXT) {
-		giterr_set(GITERR_NET, "Could not restart authentication");
+		giterr_set(GITERR_NET, "could not restart authentication");
 		error = -1;
 		goto done;
 	}
@@ -228,7 +228,7 @@
 	gss_release_oid_set(&status_minor, &mechanism_list);
 
 	if (!ctx->oid) {
-		giterr_set(GITERR_NET, "Negotiate authentication is not supported");
+		giterr_set(GITERR_NET, "negotiate authentication is not supported");
 		return -1;
 	}
 
diff --git a/src/transports/cred.c b/src/transports/cred.c
index 49ede48..8e3f644 100644
--- a/src/transports/cred.c
+++ b/src/transports/cred.c
@@ -216,7 +216,7 @@
 	GIT_UNUSED(passphrase);
 
 	giterr_set(GITERR_INVALID,
-		"This version of libgit2 was not built with ssh memory credentials.");
+		"this version of libgit2 was not built with ssh memory credentials.");
 	return -1;
 #endif
 }
diff --git a/src/transports/git.c b/src/transports/git.c
index 6c6acf9..01edfdc 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -45,7 +45,7 @@
 
 	delim = strchr(url, '/');
 	if (delim == NULL) {
-		giterr_set(GITERR_NET, "Malformed URL");
+		giterr_set(GITERR_NET, "malformed URL");
 		return -1;
 	}
 
@@ -240,7 +240,7 @@
 		return 0;
 	}
 
-	giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK");
+	giterr_set(GITERR_NET, "must call UPLOADPACK_LS before UPLOADPACK");
 	return -1;
 }
 
@@ -296,7 +296,7 @@
 		return 0;
 	}
 
-	giterr_set(GITERR_NET, "Must call RECEIVEPACK_LS before RECEIVEPACK");
+	giterr_set(GITERR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK");
 	return -1;
 }
 
diff --git a/src/transports/http.c b/src/transports/http.c
index ca1f504..949e857 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) {
@@ -403,7 +403,7 @@
 	    t->location) {
 
 		if (s->redirect_count >= 7) {
-			giterr_set(GITERR_NET, "Too many redirects");
+			giterr_set(GITERR_NET, "too many redirects");
 			return t->parse_error = PARSE_ERROR_GENERIC;
 		}
 
@@ -428,14 +428,14 @@
 	/* Check for a 200 HTTP status code. */
 	if (parser->status_code != 200) {
 		giterr_set(GITERR_NET,
-			"Unexpected HTTP status code: %d",
+			"unexpected HTTP status code: %d",
 			parser->status_code);
 		return t->parse_error = PARSE_ERROR_GENERIC;
 	}
 
 	/* The response must contain a Content-Type header. */
 	if (!t->content_type) {
-		giterr_set(GITERR_NET, "No Content-Type header in response");
+		giterr_set(GITERR_NET, "no Content-Type header in response");
 		return t->parse_error = PARSE_ERROR_GENERIC;
 	}
 
@@ -455,7 +455,7 @@
 	if (strcmp(t->content_type, git_buf_cstr(&buf))) {
 		git_buf_free(&buf);
 		giterr_set(GITERR_NET,
-			"Invalid Content-Type: %s",
+			"invalid Content-Type: %s",
 			t->content_type);
 		return t->parse_error = PARSE_ERROR_GENERIC;
 	}
@@ -488,7 +488,7 @@
 		return 0;
 
 	if (ctx->buf_size < len) {
-		giterr_set(GITERR_NET, "Can't fit data in the buffer");
+		giterr_set(GITERR_NET, "can't fit data in the buffer");
 		return t->parse_error = PARSE_ERROR_GENERIC;
 	}
 
@@ -624,13 +624,12 @@
 	if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL &&
 	    git_stream_is_encrypted(t->io)) {
 		git_cert *cert;
-		int is_valid;
+		int is_valid = (error == GIT_OK);
 
 		if ((error = git_stream_certificate(&cert, t->io)) < 0)
 			return error;
 
 		giterr_clear();
-		is_valid = error != GIT_ECERTIFICATE;
 		error = t->owner->certificate_check_cb(cert, is_valid, t->connection_data.host, t->owner->message_cb_payload);
 
 		if (error < 0) {
@@ -857,7 +856,7 @@
 	assert(t->connected);
 
 	if (s->sent_request) {
-		giterr_set(GITERR_NET, "Subtransport configured for only one write");
+		giterr_set(GITERR_NET, "subtransport configured for only one write");
 		return -1;
 	}
 
diff --git a/src/transports/local.c b/src/transports/local.c
index 4eae9de..e24e998 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -252,7 +252,7 @@
 	transport_local *t = (transport_local *)transport;
 
 	if (!t->have_refs) {
-		giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
+		giterr_set(GITERR_NET, "the transport has not yet loaded the refs");
 		return -1;
 	}
 
@@ -371,11 +371,12 @@
 	   but we forbid all pushes just in case */
 	if (!remote_repo->is_bare) {
 		error = GIT_EBAREREPO;
-		giterr_set(GITERR_INVALID, "Local push doesn't (yet) support pushing to non-bare repos.");
+		giterr_set(GITERR_INVALID, "local push doesn't (yet) support pushing to non-bare repos.");
 		goto on_error;
 	}
 
-	if ((error = git_buf_joinpath(&odb_path, git_repository_path(remote_repo), "objects/pack")) < 0)
+	if ((error = git_repository_item_path(&odb_path, remote_repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0
+		|| (error = git_buf_joinpath(&odb_path, odb_path.ptr, "pack")) < 0)
 		goto on_error;
 
 	error = git_packbuilder_write(push->pb, odb_path.ptr, 0, transfer_to_push_transfer, (void *) cbs);
diff --git a/src/transports/smart.c b/src/transports/smart.c
index 7a35c39..e4aa26d 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -231,7 +231,7 @@
 	else if (GIT_DIRECTION_PUSH == t->direction)
 		service = GIT_SERVICE_RECEIVEPACK_LS;
 	else {
-		giterr_set(GITERR_NET, "Invalid direction");
+		giterr_set(GITERR_NET, "invalid direction");
 		return -1;
 	}
 
@@ -252,7 +252,7 @@
 		pkt = (git_pkt *)git_vector_get(&t->refs, 0);
 
 		if (!pkt || GIT_PKT_COMMENT != pkt->type) {
-			giterr_set(GITERR_NET, "Invalid response");
+			giterr_set(GITERR_NET, "invalid response");
 			return -1;
 		} else {
 			/* Remove the comment pkt from the list */
@@ -299,7 +299,7 @@
 	transport_smart *t = (transport_smart *)transport;
 
 	if (!t->have_refs) {
-		giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
+		giterr_set(GITERR_NET, "the transport has not yet loaded the refs");
 		return -1;
 	}
 
@@ -319,7 +319,7 @@
 		return -1;
 
 	if (GIT_DIRECTION_FETCH != t->direction) {
-		giterr_set(GITERR_NET, "This operation is only valid for fetch");
+		giterr_set(GITERR_NET, "this operation is only valid for fetch");
 		return -1;
 	}
 
@@ -348,7 +348,7 @@
 		return -1;
 
 	if (GIT_DIRECTION_PUSH != t->direction) {
-		giterr_set(GITERR_NET, "This operation is only valid for push");
+		giterr_set(GITERR_NET, "this operation is only valid for push");
 		return -1;
 	}
 
diff --git a/src/transports/smart.h b/src/transports/smart.h
index 0a0c3fc..b47001f 100644
--- a/src/transports/smart.h
+++ b/src/transports/smart.h
@@ -26,6 +26,8 @@
 #define GIT_CAP_THIN_PACK "thin-pack"
 #define GIT_CAP_SYMREF "symref"
 
+extern bool git_smart__ofs_delta_enabled;
+
 enum git_pkt_type {
 	GIT_PKT_CMD,
 	GIT_PKT_FLUSH,
diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c
index 2297cc9..a661dfe 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -226,7 +226,7 @@
 
 	/* Check for a bit of consistency */
 	if (line[GIT_OID_HEXSZ] != ' ') {
-		giterr_set(GITERR_NET, "Error parsing pkt-line");
+		giterr_set(GITERR_NET, "error parsing pkt-line");
 		error = -1;
 		goto error_out;
 	}
@@ -270,7 +270,7 @@
 
 	line += 3; /* skip "ok " */
 	if (!(ptr = strchr(line, '\n'))) {
-		giterr_set(GITERR_NET, "Invalid packet line");
+		giterr_set(GITERR_NET, "invalid packet line");
 		git__free(pkt);
 		return -1;
 	}
@@ -327,7 +327,7 @@
 	return 0;
 
 out_err:
-	giterr_set(GITERR_NET, "Invalid packet line");
+	giterr_set(GITERR_NET, "invalid packet line");
 	git__free(pkt->ref);
 	git__free(pkt);
 	return -1;
@@ -427,15 +427,23 @@
 	if (bufflen > 0 && bufflen < (size_t)len)
 		return GIT_EBUFS;
 
+	/*
+	 * The length has to be exactly 0 in case of a flush
+	 * packet or greater than PKT_LEN_SIZE, as the decoded
+	 * length includes its own encoded length of four bytes.
+	 */
+	if (len != 0 && len < PKT_LEN_SIZE)
+		return GIT_ERROR;
+
 	line += PKT_LEN_SIZE;
 	/*
-	 * TODO: How do we deal with empty lines? Try again? with the next
-	 * line?
+	 * The Git protocol does not specify empty lines as part
+	 * of the protocol. Not knowing what to do with an empty
+	 * line, we should return an error upon hitting one.
 	 */
 	if (len == PKT_LEN_SIZE) {
-		*head = NULL;
-		*out = line;
-		return 0;
+		giterr_set_str(GITERR_NET, "Invalid empty packet");
+		return GIT_ERROR;
 	}
 
 	if (len == 0) { /* Flush pkt */
@@ -535,7 +543,7 @@
 
 	if (len > 0xffff) {
 		giterr_set(GITERR_NET,
-			"Tried to produce packet with invalid length %" PRIuZ, len);
+			"tried to produce packet with invalid length %" PRIuZ, len);
 		return -1;
 	}
 
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 3448fa7..25e78c6 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -19,6 +19,8 @@
 /* The minimal interval between progress updates (in seconds). */
 #define MIN_PROGRESS_UPDATE_INTERVAL 0.5
 
+bool git_smart__ofs_delta_enabled = true;
+
 int git_smart__store_refs(transport_smart *t, int flushes)
 {
 	gitno_buffer *buf = &t->buffer;
@@ -50,7 +52,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;
 			}
@@ -60,7 +62,7 @@
 
 		gitno_consume(buf, line_end);
 		if (pkt->type == GIT_PKT_ERR) {
-			giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error);
+			giterr_set(GITERR_NET, "remote error: %s", ((git_pkt_err *)pkt)->error);
 			git__free(pkt);
 			return -1;
 		}
@@ -138,7 +140,7 @@
 		if (*ptr == ' ')
 			ptr++;
 
-		if (!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
+		if (git_smart__ofs_delta_enabled && !git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
 			caps->common = caps->ofs_delta = 1;
 			ptr += strlen(GIT_CAP_OFS_DELTA);
 			continue;
@@ -222,8 +224,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 +414,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 +434,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 +730,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;
@@ -759,14 +765,6 @@
 		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);
@@ -821,9 +819,6 @@
 
 		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/transports/ssh.c b/src/transports/ssh.c
index cfd5736..44d02e5 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -83,7 +83,7 @@
 
 done:
 	if (!repo) {
-		giterr_set(GITERR_NET, "Malformed git protocol URL");
+		giterr_set(GITERR_NET, "malformed git protocol URL");
 		return -1;
 	}
 
@@ -274,7 +274,7 @@
 	}
 
 	if (colon == NULL || (colon < start)) {
-		giterr_set(GITERR_NET, "Malformed URL");
+		giterr_set(GITERR_NET, "malformed URL");
 		return -1;
 	}
 
@@ -445,7 +445,7 @@
 		else if (error < 0)
 			return error;
 		else if (!cred) {
-			giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials");
+			giterr_set(GITERR_SSH, "callback failed to initialize SSH credentials");
 			return -1;
 		}
 	}
@@ -478,7 +478,7 @@
 
 	s = libssh2_session_init();
 	if (!s) {
-		giterr_set(GITERR_NET, "Failed to initialize SSH session");
+		giterr_set(GITERR_NET, "failed to initialize SSH session");
 		return -1;
 	}
 
@@ -487,7 +487,7 @@
 	} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
 
 	if (rc != LIBSSH2_ERROR_NONE) {
-		ssh_error(s, "Failed to start SSH session");
+		ssh_error(s, "failed to start SSH session");
 		libssh2_session_free(s);
 		return -1;
 	}
@@ -685,7 +685,7 @@
 		return 0;
 	}
 
-	giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK");
+	giterr_set(GITERR_NET, "must call UPLOADPACK_LS before UPLOADPACK");
 	return -1;
 }
 
@@ -712,7 +712,7 @@
 		return 0;
 	}
 
-	giterr_set(GITERR_NET, "Must call RECEIVEPACK_LS before RECEIVEPACK");
+	giterr_set(GITERR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK");
 	return -1;
 }
 
@@ -844,7 +844,7 @@
 	assert(out);
 	*out = NULL;
 
-	giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support");
+	giterr_set(GITERR_INVALID, "cannot create SSH transport. Library was built without SSH support");
 	return -1;
 #endif
 }
@@ -888,7 +888,7 @@
 	assert(out);
 	*out = NULL;
 
-	giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support");
+	giterr_set(GITERR_INVALID, "cannot create SSH transport. Library was built without SSH support");
 	return -1;
 #endif
 }
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 78e42cf..e8e8480 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -68,7 +68,9 @@
 
 typedef enum {
 	GIT_WINHTTP_AUTH_BASIC = 1,
-	GIT_WINHTTP_AUTH_NEGOTIATE = 2,
+	GIT_WINHTTP_AUTH_NTLM = 2,
+	GIT_WINHTTP_AUTH_NEGOTIATE = 4,
+	GIT_WINHTTP_AUTH_DIGEST = 8,
 } winhttp_authmechanism_t;
 
 typedef struct {
@@ -95,79 +97,71 @@
 	git_cred *cred;
 	git_cred *url_cred;
 	git_cred *proxy_cred;
-	int auth_mechanism;
+	int auth_mechanisms;
 	HINTERNET session;
 	HINTERNET connection;
 } winhttp_subtransport;
 
-static int apply_basic_credential_proxy(HINTERNET request, git_cred *cred)
+static int _apply_userpass_credential(HINTERNET request, DWORD target, DWORD scheme, git_cred *cred)
 {
 	git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
 	wchar_t *user, *pass;
-	int error;
+	int user_len = 0, pass_len = 0, error = 0;
 
-	if ((error = git__utf8_to_16_alloc(&user, c->username)) < 0)
-		return error;
+	if ((error = user_len = git__utf8_to_16_alloc(&user, c->username)) < 0)
+		goto done;
 
-	if ((error = git__utf8_to_16_alloc(&pass, c->password)) < 0)
-		return error;
+	if ((error = pass_len = git__utf8_to_16_alloc(&pass, c->password)) < 0)
+		goto done;
 
-	if (!WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_PROXY, WINHTTP_AUTH_SCHEME_BASIC,
-	                           user, pass, NULL)) {
-		giterr_set(GITERR_OS, "failed to set proxy auth");
+	if (!WinHttpSetCredentials(request, target, scheme, user, pass, NULL)) {
+		giterr_set(GITERR_OS, "failed to set credentials");
 		error = -1;
 	}
 
+done:
+	if (user_len > 0)
+		git__memzero(user, user_len * sizeof(wchar_t));
+
+	if (pass_len > 0)
+		git__memzero(pass, pass_len * sizeof(wchar_t));
+
 	git__free(user);
 	git__free(pass);
 
 	return error;
 }
 
-static int apply_basic_credential(HINTERNET request, git_cred *cred)
+static int apply_userpass_credential_proxy(HINTERNET request, git_cred *cred, int mechanisms)
 {
-	git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
-	git_buf buf = GIT_BUF_INIT, raw = GIT_BUF_INIT;
-	wchar_t *wide = NULL;
-	int error = -1, wide_len;
-
-	git_buf_printf(&raw, "%s:%s", c->username, c->password);
-
-	if (git_buf_oom(&raw) ||
-		git_buf_puts(&buf, "Authorization: Basic ") < 0 ||
-		git_buf_encode_base64(&buf, git_buf_cstr(&raw), raw.size) < 0)
-		goto on_error;
-
-	if ((wide_len = git__utf8_to_16_alloc(&wide, git_buf_cstr(&buf))) < 0) {
-		giterr_set(GITERR_OS, "Failed to convert string to wide form");
-		goto on_error;
+	if (GIT_WINHTTP_AUTH_DIGEST & mechanisms) {
+		return _apply_userpass_credential(request, WINHTTP_AUTH_TARGET_PROXY,
+			WINHTTP_AUTH_SCHEME_DIGEST, cred);
 	}
 
-	if (!WinHttpAddRequestHeaders(request, wide, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
-		giterr_set(GITERR_OS, "Failed to add a header to the request");
-		goto on_error;
-	}
-
-	error = 0;
-
-on_error:
-	/* We were dealing with plaintext passwords, so clean up after ourselves a bit. */
-	if (wide)
-		memset(wide, 0x0, wide_len * sizeof(wchar_t));
-
-	if (buf.size)
-		memset(buf.ptr, 0x0, buf.size);
-
-	if (raw.size)
-		memset(raw.ptr, 0x0, raw.size);
-
-	git__free(wide);
-	git_buf_free(&buf);
-	git_buf_free(&raw);
-	return error;
+	return _apply_userpass_credential(request, WINHTTP_AUTH_TARGET_PROXY,
+		WINHTTP_AUTH_SCHEME_BASIC, cred);
 }
 
-static int apply_default_credentials(HINTERNET request)
+static int apply_userpass_credential(HINTERNET request, int mechanisms, git_cred *cred)
+{
+	DWORD native_scheme;
+
+	if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) ||
+		(mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE)) {
+		native_scheme = WINHTTP_AUTH_SCHEME_NTLM;
+	} else if (mechanisms & GIT_WINHTTP_AUTH_BASIC) {
+		native_scheme = WINHTTP_AUTH_SCHEME_BASIC;
+	} else {
+		giterr_set(GITERR_NET, "invalid authentication scheme");
+		return -1;
+	}
+
+	return _apply_userpass_credential(request, WINHTTP_AUTH_TARGET_SERVER,
+		native_scheme, cred);
+}
+
+static int apply_default_credentials(HINTERNET request, int mechanisms)
 {
 	/* Either the caller explicitly requested that default credentials be passed,
 	 * or our fallback credential callback was invoked and checked that the target
@@ -177,6 +171,12 @@
 	 * to Internet Explorer security zones, but in fact does not. */
 	DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
 
+	if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) == 0 &&
+		(mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) == 0) {
+		giterr_set(GITERR_NET, "invalid authentication scheme");
+		return -1;
+	}
+
 	if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(DWORD)))
 		return -1;
 
@@ -202,7 +202,7 @@
 
 		/* Convert URL to wide characters */
 		if (git__utf8_to_16_alloc(&wide_url, url) < 0) {
-			giterr_set(GITERR_OS, "Failed to convert string to wide form");
+			giterr_set(GITERR_OS, "failed to convert string to wide form");
 			return -1;
 		}
 
@@ -248,8 +248,12 @@
 	git_cert_x509 cert;
 
 	/* If there is no override, we should fail if WinHTTP doesn't think it's fine */
-	if (t->owner->certificate_check_cb == NULL && !valid)
+	if (t->owner->certificate_check_cb == NULL && !valid) {
+		if (!giterr_last())
+			giterr_set(GITERR_NET, "unknown certificate check failure");
+
 		return GIT_ECERTIFICATE;
+	}
 
 	if (t->owner->certificate_check_cb == NULL || !t->connection_data.use_ssl)
 		return 0;
@@ -351,7 +355,7 @@
 
 	/* Convert URL to wide characters */
 	if (git__utf8_to_16_alloc(&s->request_uri, git_buf_cstr(&buf)) < 0) {
-		giterr_set(GITERR_OS, "Failed to convert string to wide form");
+		giterr_set(GITERR_OS, "failed to convert string to wide form");
 		goto on_error;
 	}
 
@@ -366,12 +370,12 @@
 			t->connection_data.use_ssl ? WINHTTP_FLAG_SECURE : 0);
 
 	if (!s->request) {
-		giterr_set(GITERR_OS, "Failed to open request");
+		giterr_set(GITERR_OS, "failed to open request");
 		goto on_error;
 	}
 
 	if (!WinHttpSetTimeouts(s->request, default_timeout, default_connect_timeout, default_timeout, default_timeout)) {
-		giterr_set(GITERR_OS, "Failed to set timeouts for WinHTTP");
+		giterr_set(GITERR_OS, "failed to set timeouts for WinHTTP");
 		goto on_error;
 	}
 
@@ -444,7 +448,7 @@
 			WINHTTP_OPTION_PROXY,
 			&proxy_info,
 			sizeof(WINHTTP_PROXY_INFO))) {
-			giterr_set(GITERR_OS, "Failed to set proxy");
+			giterr_set(GITERR_OS, "failed to set proxy");
 			git__free(proxy_wide);
 			goto on_error;
 		}
@@ -453,7 +457,7 @@
 
 		if (t->proxy_cred) {
 			if (t->proxy_cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT) {
-				if ((error = apply_basic_credential_proxy(s->request, t->proxy_cred)) < 0)
+				if ((error = apply_userpass_credential_proxy(s->request, t->proxy_cred, t->auth_mechanisms)) < 0)
 					goto on_error;
 			}
 		}
@@ -467,7 +471,7 @@
 		WINHTTP_OPTION_DISABLE_FEATURE,
 		&disable_redirects,
 		sizeof(disable_redirects))) {
-			giterr_set(GITERR_OS, "Failed to disable redirects");
+			giterr_set(GITERR_OS, "failed to disable redirects");
 			goto on_error;
 	}
 
@@ -481,7 +485,7 @@
 
 	/* Send Pragma: no-cache header */
 	if (!WinHttpAddRequestHeaders(s->request, pragma_nocache, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
-		giterr_set(GITERR_OS, "Failed to add a header to the request");
+		giterr_set(GITERR_OS, "failed to add a header to the request");
 		goto on_error;
 	}
 
@@ -494,13 +498,13 @@
 			goto on_error;
 
 		if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) {
-			giterr_set(GITERR_OS, "Failed to convert content-type to wide characters");
+			giterr_set(GITERR_OS, "failed to convert content-type to wide characters");
 			goto on_error;
 		}
 
 		if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
 			WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
-			giterr_set(GITERR_OS, "Failed to add a header to the request");
+			giterr_set(GITERR_OS, "failed to add a header to the request");
 			goto on_error;
 		}
 
@@ -511,13 +515,13 @@
 			goto on_error;
 
 		if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) {
-			giterr_set(GITERR_OS, "Failed to convert accept header to wide characters");
+			giterr_set(GITERR_OS, "failed to convert accept header to wide characters");
 			goto on_error;
 		}
 
 		if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
 			WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
-			giterr_set(GITERR_OS, "Failed to add a header to the request");
+			giterr_set(GITERR_OS, "failed to add a header to the request");
 			goto on_error;
 		}
 	}
@@ -527,13 +531,13 @@
 			git_buf_clear(&buf);
 			git_buf_puts(&buf, t->owner->custom_headers.strings[i]);
 			if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) {
-				giterr_set(GITERR_OS, "Failed to convert custom header to wide characters");
+				giterr_set(GITERR_OS, "failed to convert custom header to wide characters");
 				goto on_error;
 			}
 
 			if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
 				WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
-				giterr_set(GITERR_OS, "Failed to add a header to the request");
+				giterr_set(GITERR_OS, "failed to add a header to the request");
 				goto on_error;
 			}
 		}
@@ -550,13 +554,11 @@
 	/* If we have a credential on the subtransport, apply it to the request */
 	if (t->cred &&
 		t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
-		t->auth_mechanism == GIT_WINHTTP_AUTH_BASIC &&
-		apply_basic_credential(s->request, t->cred) < 0)
+		apply_userpass_credential(s->request, t->auth_mechanisms, t->cred) < 0)
 		goto on_error;
 	else if (t->cred &&
 		t->cred->credtype == GIT_CREDTYPE_DEFAULT &&
-		t->auth_mechanism == GIT_WINHTTP_AUTH_NEGOTIATE &&
-		apply_default_credentials(s->request) < 0)
+		apply_default_credentials(s->request, t->auth_mechanisms) < 0)
 		goto on_error;
 
 	/* If no other credentials have been applied and the URL has username and
@@ -565,7 +567,7 @@
 		if (!t->url_cred &&
 			git_cred_userpass_plaintext_new(&t->url_cred, t->connection_data.user, t->connection_data.pass) < 0)
 			goto on_error;
-		if (apply_basic_credential(s->request, t->url_cred) < 0)
+		if (apply_userpass_credential(s->request, GIT_WINHTTP_AUTH_BASIC, t->url_cred) < 0)
 			goto on_error;
 	}
 
@@ -585,30 +587,40 @@
 static int parse_unauthorized_response(
 	HINTERNET request,
 	int *allowed_types,
-	int *auth_mechanism)
+	int *allowed_mechanisms)
 {
 	DWORD supported, first, target;
 
 	*allowed_types = 0;
-	*auth_mechanism = 0;
+	*allowed_mechanisms = 0;
 
-	/* WinHttpQueryHeaders() must be called before WinHttpQueryAuthSchemes(). 
-	 * We can assume this was already done, since we know we are unauthorized. 
+	/* WinHttpQueryHeaders() must be called before WinHttpQueryAuthSchemes().
+	 * We can assume this was already done, since we know we are unauthorized.
 	 */
 	if (!WinHttpQueryAuthSchemes(request, &supported, &first, &target)) {
-		giterr_set(GITERR_OS, "Failed to parse supported auth schemes"); 
+		giterr_set(GITERR_OS, "failed to parse supported auth schemes");
 		return -1;
 	}
 
+	if (WINHTTP_AUTH_SCHEME_NTLM & supported) {
+		*allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
+		*allowed_types |= GIT_CREDTYPE_DEFAULT;
+		*allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE;
+	}
+
+	if (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported) {
+		*allowed_types |= GIT_CREDTYPE_DEFAULT;
+		*allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE;
+	}
+
 	if (WINHTTP_AUTH_SCHEME_BASIC & supported) {
 		*allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
-		*auth_mechanism = GIT_WINHTTP_AUTH_BASIC;
+		*allowed_mechanisms |= GIT_WINHTTP_AUTH_BASIC;
 	}
 
-	if ((WINHTTP_AUTH_SCHEME_NTLM & supported) ||
-		(WINHTTP_AUTH_SCHEME_NEGOTIATE & supported)) {
-		*allowed_types |= GIT_CREDTYPE_DEFAULT;
-		*auth_mechanism = GIT_WINHTTP_AUTH_NEGOTIATE;
+	if (WINHTTP_AUTH_SCHEME_DIGEST & supported) {
+		*allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
+		*allowed_mechanisms |= GIT_WINHTTP_AUTH_DIGEST;
 	}
 
 	return 0;
@@ -629,7 +641,7 @@
 		git_buf_cstr(&buf),	(DWORD)git_buf_len(&buf),
 		&bytes_written)) {
 		git_buf_free(&buf);
-		giterr_set(GITERR_OS, "Failed to write chunk header");
+		giterr_set(GITERR_OS, "failed to write chunk header");
 		return -1;
 	}
 
@@ -639,7 +651,7 @@
 	if (!WinHttpWriteData(request,
 		buffer, (DWORD)len,
 		&bytes_written)) {
-		giterr_set(GITERR_OS, "Failed to write chunk");
+		giterr_set(GITERR_OS, "failed to write chunk");
 		return -1;
 	}
 
@@ -647,7 +659,7 @@
 	if (!WinHttpWriteData(request,
 		"\r\n", 2,
 		&bytes_written)) {
-		giterr_set(GITERR_OS, "Failed to write chunk footer");
+		giterr_set(GITERR_OS, "failed to write chunk footer");
 		return -1;
 	}
 
@@ -660,7 +672,7 @@
 
 	if (t->connection) {
 		if (!WinHttpCloseHandle(t->connection)) {
-			giterr_set(GITERR_OS, "Unable to close connection");
+			giterr_set(GITERR_OS, "unable to close connection");
 			ret = -1;
 		}
 
@@ -669,7 +681,7 @@
 
 	if (t->session) {
 		if (!WinHttpCloseHandle(t->session)) {
-			giterr_set(GITERR_OS, "Unable to close session");
+			giterr_set(GITERR_OS, "unable to close session");
 			ret = -1;
 		}
 
@@ -694,6 +706,38 @@
 	return git_buf_putc(ua, ')');
 }
 
+static void CALLBACK winhttp_status(
+	HINTERNET connection,
+	DWORD_PTR ctx,
+	DWORD code,
+	LPVOID info,
+	DWORD info_len)
+{
+	DWORD status;
+
+	if (code != WINHTTP_CALLBACK_STATUS_SECURE_FAILURE)
+		return;
+
+	status = *((DWORD *)info);
+
+	if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID))
+		giterr_set(GITERR_NET, "SSL certificate issued for different common name");
+	else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID))
+		giterr_set(GITERR_NET, "SSL certificate has expired");
+	else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA))
+		giterr_set(GITERR_NET, "SSL certificate signed by unknown CA");
+	else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT))
+		giterr_set(GITERR_NET, "SSL certificate is invalid");
+	else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED))
+		giterr_set(GITERR_NET, "certificate revocation check failed");
+	else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED))
+		giterr_set(GITERR_NET, "SSL certificate was revoked");
+	else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR))
+		giterr_set(GITERR_NET, "security libraries could not be loaded");
+	else
+		giterr_set(GITERR_NET, "unknown security error %d", status);
+}
+
 static int winhttp_connect(
 	winhttp_subtransport *t)
 {
@@ -714,7 +758,7 @@
 
 	/* Prepare host */
 	if (git__utf8_to_16_alloc(&wide_host, t->connection_data.host) < 0) {
-		giterr_set(GITERR_OS, "Unable to convert host to wide characters");
+		giterr_set(GITERR_OS, "unable to convert host to wide characters");
 		return -1;
 	}
 
@@ -724,7 +768,7 @@
 	}
 
 	if (git__utf8_to_16_alloc(&wide_ua, git_buf_cstr(&ua)) < 0) {
-		giterr_set(GITERR_OS, "Unable to convert host to wide characters");
+		giterr_set(GITERR_OS, "unable to convert host to wide characters");
 		git__free(wide_host);
 		git_buf_free(&ua);
 		return -1;
@@ -741,16 +785,16 @@
 		0);
 
 	if (!t->session) {
-		giterr_set(GITERR_OS, "Failed to init WinHTTP");
+		giterr_set(GITERR_OS, "failed to init WinHTTP");
 		goto on_error;
 	}
 
 	if (!WinHttpSetTimeouts(t->session, default_timeout, default_connect_timeout, default_timeout, default_timeout)) {
-		giterr_set(GITERR_OS, "Failed to set timeouts for WinHTTP");
+		giterr_set(GITERR_OS, "failed to set timeouts for WinHTTP");
 		goto on_error;
 	}
 
-	
+
 	/* Establish connection */
 	t->connection = WinHttpConnect(
 		t->session,
@@ -759,7 +803,12 @@
 		0);
 
 	if (!t->connection) {
-		giterr_set(GITERR_OS, "Failed to connect to host");
+		giterr_set(GITERR_OS, "failed to connect to host");
+		goto on_error;
+	}
+
+	if (WinHttpSetStatusCallback(t->connection, winhttp_status, WINHTTP_CALLBACK_FLAG_SECURE_FAILURE, 0) == WINHTTP_INVALID_STATUS_CALLBACK) {
+		giterr_set(GITERR_OS, "failed to set status callback");
 		goto on_error;
 	}
 
@@ -801,16 +850,15 @@
 	int request_failed = 0, cert_valid = 1, error = 0;
 	DWORD ignore_flags;
 
-	if ((error = do_send_request(s, len, ignore_length)) < 0)
-		request_failed = 1;
-
-	if (request_failed) {
+	giterr_clear();
+	if ((error = do_send_request(s, len, ignore_length)) < 0) {
 		if (GetLastError() != ERROR_WINHTTP_SECURE_FAILURE) {
 			giterr_set(GITERR_OS, "failed to send request");
 			return -1;
-		} else {
-			cert_valid = 0;
 		}
+
+		request_failed = 1;
+		cert_valid = 0;
 	}
 
 	giterr_clear();
@@ -826,14 +874,14 @@
 		return 0;
 
 	ignore_flags = no_check_cert_flags;
-	
+
 	if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) {
 		giterr_set(GITERR_OS, "failed to set security options");
 		return -1;
 	}
 
 	if ((error = do_send_request(s, len, ignore_length)) < 0)
-		giterr_set(GITERR_OS, "failed to send request");
+		giterr_set(GITERR_OS, "failed to send request with unchecked certificate");
 
 	return error;
 }
@@ -853,7 +901,7 @@
 replay:
 	/* Enforce a reasonable cap on the number of replays */
 	if (++replay_count >= 7) {
-		giterr_set(GITERR_NET, "Too many redirects or authentication replays");
+		giterr_set(GITERR_NET, "too many redirects or authentication replays");
 		return -1;
 	}
 
@@ -888,7 +936,7 @@
 			if (!WinHttpWriteData(s->request,
 				"0\r\n\r\n", 5,
 				&bytes_written)) {
-				giterr_set(GITERR_OS, "Failed to write final chunk");
+				giterr_set(GITERR_OS, "failed to write final chunk");
 				return -1;
 			}
 		}
@@ -899,7 +947,7 @@
 			if (INVALID_SET_FILE_POINTER == SetFilePointer(s->post_body,
 					0, 0, FILE_BEGIN) &&
 				NO_ERROR != GetLastError()) {
-				giterr_set(GITERR_OS, "Failed to reset file pointer");
+				giterr_set(GITERR_OS, "failed to reset file pointer");
 				return -1;
 			}
 
@@ -913,14 +961,14 @@
 					&bytes_read, NULL) ||
 					!bytes_read) {
 					git__free(buffer);
-					giterr_set(GITERR_OS, "Failed to read from temp file");
+					giterr_set(GITERR_OS, "failed to read from temp file");
 					return -1;
 				}
 
 				if (!WinHttpWriteData(s->request, buffer,
 					bytes_read, &bytes_written)) {
 					git__free(buffer);
-					giterr_set(GITERR_OS, "Failed to write data");
+					giterr_set(GITERR_OS, "failed to write data");
 					return -1;
 				}
 
@@ -936,7 +984,7 @@
 		}
 
 		if (!WinHttpReceiveResponse(s->request, 0)) {
-			giterr_set(GITERR_OS, "Failed to receive response");
+			giterr_set(GITERR_OS, "failed to receive response");
 			return -1;
 		}
 
@@ -948,7 +996,7 @@
 			WINHTTP_HEADER_NAME_BY_INDEX,
 			&status_code, &status_code_length,
 			WINHTTP_NO_HEADER_INDEX)) {
-				giterr_set(GITERR_OS, "Failed to retrieve status code");
+				giterr_set(GITERR_OS, "failed to retrieve status code");
 				return -1;
 		}
 
@@ -978,7 +1026,7 @@
 				&location_length,
 				WINHTTP_NO_HEADER_INDEX) ||
 				GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
-				giterr_set(GITERR_OS, "Failed to read Location header");
+				giterr_set(GITERR_OS, "failed to read Location header");
 				return -1;
 			}
 
@@ -991,14 +1039,14 @@
 				location,
 				&location_length,
 				WINHTTP_NO_HEADER_INDEX)) {
-				giterr_set(GITERR_OS, "Failed to read Location header");
+				giterr_set(GITERR_OS, "failed to read Location header");
 				git__free(location);
 				return -1;
 			}
 
 			/* Convert the Location header to UTF-8 */
 			if (git__utf16_to_8_alloc(&location8, location) < 0) {
-				giterr_set(GITERR_OS, "Failed to convert Location header to UTF-8");
+				giterr_set(GITERR_OS, "failed to convert Location header to UTF-8");
 				git__free(location);
 				return -1;
 			}
@@ -1029,13 +1077,13 @@
 		if (status_code == HTTP_STATUS_PROXY_AUTH_REQ) {
 			int allowed_types;
 
-			if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0)
+			if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanisms) < 0)
 				return -1;
 
 			/* TODO: extract the username from the url, no payload? */
 			if (t->owner->proxy.credentials) {
 				int cred_error = 1;
-				cred_error = t->owner->proxy.credentials(&t->proxy_cred, t->owner->proxy.url, NULL, allowed_types, NULL);
+				cred_error = t->owner->proxy.credentials(&t->proxy_cred, t->owner->proxy.url, NULL, allowed_types, t->owner->proxy.payload);
 
 				if (cred_error < 0)
 					return cred_error;
@@ -1049,7 +1097,7 @@
 		if (HTTP_STATUS_DENIED == status_code && get_verb == s->verb) {
 			int allowed_types;
 
-			if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0)
+			if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanisms) < 0)
 				return -1;
 
 			if (allowed_types) {
@@ -1090,7 +1138,7 @@
 		}
 
 		if (HTTP_STATUS_OK != status_code) {
-			giterr_set(GITERR_NET, "Request failed with status code: %d", status_code);
+			giterr_set(GITERR_NET, "request failed with status code: %d", status_code);
 			return -1;
 		}
 
@@ -1101,7 +1149,7 @@
 			p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service);
 
 		if (git__utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8) < 0) {
-			giterr_set(GITERR_OS, "Failed to convert expected content-type to wide characters");
+			giterr_set(GITERR_OS, "failed to convert expected content-type to wide characters");
 			return -1;
 		}
 
@@ -1112,12 +1160,12 @@
 			WINHTTP_HEADER_NAME_BY_INDEX,
 			&content_type, &content_type_length,
 			WINHTTP_NO_HEADER_INDEX)) {
-				giterr_set(GITERR_OS, "Failed to retrieve response content-type");
+				giterr_set(GITERR_OS, "failed to retrieve response content-type");
 				return -1;
 		}
 
 		if (wcscmp(expected_content_type, content_type)) {
-			giterr_set(GITERR_NET, "Received unexpected content-type");
+			giterr_set(GITERR_NET, "received unexpected content-type");
 			return -1;
 		}
 
@@ -1129,7 +1177,7 @@
 		(DWORD)buf_size,
 		&dw_bytes_read))
 	{
-		giterr_set(GITERR_OS, "Failed to read data");
+		giterr_set(GITERR_OS, "failed to read data");
 		return -1;
 	}
 
@@ -1152,7 +1200,7 @@
 
 	/* This implementation of write permits only a single call. */
 	if (s->sent_request) {
-		giterr_set(GITERR_NET, "Subtransport configured for only one write");
+		giterr_set(GITERR_NET, "subtransport configured for only one write");
 		return -1;
 	}
 
@@ -1165,7 +1213,7 @@
 			(LPCVOID)buffer,
 			(DWORD)len,
 			&bytes_written)) {
-		giterr_set(GITERR_OS, "Failed to write data");
+		giterr_set(GITERR_OS, "failed to write data");
 		return -1;
 	}
 
@@ -1183,12 +1231,12 @@
 	if (RPC_S_OK != status &&
 		RPC_S_UUID_LOCAL_ONLY != status &&
 		RPC_S_UUID_NO_ADDRESS != status) {
-		giterr_set(GITERR_NET, "Unable to generate name for temp file");
+		giterr_set(GITERR_NET, "unable to generate name for temp file");
 		return -1;
 	}
 
 	if (buffer_len_cch < UUID_LENGTH_CCH + 1) {
-		giterr_set(GITERR_NET, "Buffer too small for name of temp file");
+		giterr_set(GITERR_NET, "buffer too small for name of temp file");
 		return -1;
 	}
 
@@ -1203,7 +1251,7 @@
 		uuid.Data4[4], uuid.Data4[5], uuid.Data4[6], uuid.Data4[7]);
 
 	if (result < UUID_LENGTH_CCH) {
-		giterr_set(GITERR_OS, "Unable to generate name for temp file");
+		giterr_set(GITERR_OS, "unable to generate name for temp file");
 		return -1;
 	}
 
@@ -1215,7 +1263,7 @@
 	size_t len;
 
 	if (!GetTempPathW(buffer_len_cch, buffer)) {
-		giterr_set(GITERR_OS, "Failed to get temp path");
+		giterr_set(GITERR_OS, "failed to get temp path");
 		return -1;
 	}
 
@@ -1258,13 +1306,13 @@
 
 		if (INVALID_HANDLE_VALUE == s->post_body) {
 			s->post_body = NULL;
-			giterr_set(GITERR_OS, "Failed to create temporary file");
+			giterr_set(GITERR_OS, "failed to create temporary file");
 			return -1;
 		}
 	}
 
 	if (!WriteFile(s->post_body, buffer, (DWORD)len, &bytes_written, NULL)) {
-		giterr_set(GITERR_OS, "Failed to write to temporary file");
+		giterr_set(GITERR_OS, "failed to write to temporary file");
 		return -1;
 	}
 
@@ -1291,7 +1339,7 @@
 		if (!WinHttpAddRequestHeaders(s->request,
 			transfer_encoding, (ULONG) -1L,
 			WINHTTP_ADDREQ_FLAG_ADD)) {
-			giterr_set(GITERR_OS, "Failed to add a header to the request");
+			giterr_set(GITERR_OS, "failed to add a header to the request");
 			return -1;
 		}
 
diff --git a/src/tree-cache.c b/src/tree-cache.c
index b37be0f..5480541 100644
--- a/src/tree-cache.c
+++ b/src/tree-cache.c
@@ -137,7 +137,7 @@
 	return 0;
 
  corrupted:
-	giterr_set(GITERR_INDEX, "Corrupted TREE extension in index");
+	giterr_set(GITERR_INDEX, "corrupted TREE extension in index");
 	return -1;
 }
 
@@ -149,7 +149,7 @@
 		return -1;
 
 	if (buffer < buffer_end) {
-		giterr_set(GITERR_INDEX, "Corrupted TREE extension in index (unexpected trailing data)");
+		giterr_set(GITERR_INDEX, "corrupted TREE extension in index (unexpected trailing data)");
 		return -1;
 	}
 
diff --git a/src/tree.c b/src/tree.c
index 5db2446..dba2060 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -20,8 +20,6 @@
 #define TREE_ENTRY_CHECK_NAMELEN(n) \
 	if (n > UINT16_MAX) { giterr_set(GITERR_INVALID, "tree entry path too long"); }
 
-GIT__USE_STRMAP
-
 static bool valid_filemode(const int filemode)
 {
 	return (filemode == GIT_FILEMODE_TREE
@@ -447,7 +445,12 @@
 		if ((nul = memchr(buffer, 0, buffer_end - buffer)) == NULL)
 			return tree_error("Failed to parse tree. Object is corrupted", NULL);
 
-		filename_len = nul - buffer;
+		if ((filename_len = nul - buffer) == 0)
+			return tree_error("Failed to parse tree. Can't parse filename", NULL);
+
+		if ((buffer_end - (nul + 1)) < GIT_OID_RAWSZ)
+			return tree_error("Failed to parse tree. Can't parse OID", NULL);
+
 		/* Allocate the entry */
 		{
 			entry = git_array_alloc(tree->entries);
@@ -500,7 +503,7 @@
 
 	entry->attr = (uint16_t)filemode;
 
-	git_strmap_insert(bld->map, entry->filename, entry, error);
+	git_strmap_insert(bld->map, entry->filename, entry, &error);
 	if (error < 0) {
 		git_tree_entry_free(entry);
 		giterr_set(GITERR_TREE, "failed to append entry %s to the tree builder", filename);
@@ -515,7 +518,8 @@
 	git_repository *repo,
 	git_index *index,
 	const char *dirname,
-	size_t start)
+	size_t start,
+	git_buf *shared_buf)
 {
 	git_treebuilder *bld = NULL;
 	size_t i, entries = git_index_entrycount(index);
@@ -568,7 +572,7 @@
 			GITERR_CHECK_ALLOC(subdir);
 
 			/* Write out the subtree */
-			written = write_tree(&sub_oid, repo, index, subdir, i);
+			written = write_tree(&sub_oid, repo, index, subdir, i, shared_buf);
 			if (written < 0) {
 				git__free(subdir);
 				goto on_error;
@@ -600,7 +604,7 @@
 		}
 	}
 
-	if (git_treebuilder_write(oid, bld) < 0)
+	if (git_treebuilder_write_with_buffer(oid, bld, shared_buf) < 0)
 		goto on_error;
 
 	git_treebuilder_free(bld);
@@ -616,13 +620,14 @@
 {
 	int ret;
 	git_tree *tree;
+	git_buf shared_buf = GIT_BUF_INIT;
 	bool old_ignore_case = false;
 
 	assert(oid && index && repo);
 
 	if (git_index_has_conflicts(index)) {
 		giterr_set(GITERR_INDEX,
-			"Cannot create a tree from a not fully merged index.");
+			"cannot create a tree from a not fully merged index.");
 		return GIT_EUNMERGED;
 	}
 
@@ -641,7 +646,8 @@
 		git_index__set_ignore_case(index, false);
 	}
 
-	ret = write_tree(oid, repo, index, "", 0);
+	ret = write_tree(oid, repo, index, "", 0, &shared_buf);
+	git_buf_free(&shared_buf);
 
 	if (old_ignore_case)
 		git_index__set_ignore_case(index, true);
@@ -746,7 +752,7 @@
 		entry = alloc_entry(filename, strlen(filename), id);
 		GITERR_CHECK_ALLOC(entry);
 
-		git_strmap_insert(bld->map, entry->filename, entry, error);
+		git_strmap_insert(bld->map, entry->filename, entry, &error);
 
 		if (error < 0) {
 			git_tree_entry_free(entry);
@@ -797,19 +803,36 @@
 
 int git_treebuilder_write(git_oid *oid, git_treebuilder *bld)
 {
+	int error;
+	git_buf buffer = GIT_BUF_INIT;
+
+	error = git_treebuilder_write_with_buffer(oid, bld, &buffer);
+
+	git_buf_free(&buffer);
+	return error;
+}
+
+int git_treebuilder_write_with_buffer(git_oid *oid, git_treebuilder *bld, git_buf *tree)
+{
 	int error = 0;
 	size_t i, entrycount;
-	git_buf tree = GIT_BUF_INIT;
 	git_odb *odb;
 	git_tree_entry *entry;
 	git_vector entries;
 
 	assert(bld);
+	assert(tree);
+
+	git_buf_clear(tree);
 
 	entrycount = git_strmap_num_entries(bld->map);
 	if (git_vector_init(&entries, entrycount, entry_sort_cmp) < 0)
 		return -1;
 
+	if (tree->asize == 0 &&
+		(error = git_buf_grow(tree, entrycount * 72)) < 0)
+		return error;
+
 	git_strmap_foreach_value(bld->map, entry, {
 		if (git_vector_insert(&entries, entry) < 0)
 			return -1;
@@ -817,26 +840,21 @@
 
 	git_vector_sort(&entries);
 
-	/* Grow the buffer beforehand to an estimated size */
-	error = git_buf_grow(&tree, entrycount * 72);
-
 	for (i = 0; i < entries.length && !error; ++i) {
 		git_tree_entry *entry = git_vector_get(&entries, i);
 
-		git_buf_printf(&tree, "%o ", entry->attr);
-		git_buf_put(&tree, entry->filename, entry->filename_len + 1);
-		git_buf_put(&tree, (char *)entry->oid->id, GIT_OID_RAWSZ);
+		git_buf_printf(tree, "%o ", entry->attr);
+		git_buf_put(tree, entry->filename, entry->filename_len + 1);
+		git_buf_put(tree, (char *)entry->oid->id, GIT_OID_RAWSZ);
 
-		if (git_buf_oom(&tree))
+		if (git_buf_oom(tree))
 			error = -1;
 	}
 
-
 	if (!error &&
 		!(error = git_repository_odb__weakptr(&odb, bld->repo)))
-		error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
+		error = git_odb_write(oid, odb, tree->ptr, tree->size, GIT_OBJ_TREE);
 
-	git_buf_free(&tree);
 	git_vector_free(&entries);
 
 	return error;
@@ -904,7 +922,7 @@
 	filename_len = subpath_len(path);
 
 	if (filename_len == 0) {
-		giterr_set(GITERR_TREE, "Invalid tree path given");
+		giterr_set(GITERR_TREE, "invalid tree path given");
 		return GIT_ENOTFOUND;
 	}
 
@@ -912,7 +930,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;
 	}
 
@@ -922,7 +940,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;
 		}
 
@@ -1022,7 +1040,7 @@
 	git_buf root_path = GIT_BUF_INIT;
 
 	if (mode != GIT_TREEWALK_POST && mode != GIT_TREEWALK_PRE) {
-		giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk");
+		giterr_set(GITERR_INVALID, "invalid walking mode for tree walk");
 		return -1;
 	}
 
@@ -1159,8 +1177,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;
 
@@ -1195,6 +1213,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;
@@ -1229,7 +1250,7 @@
 				const git_tree_entry *e = git_treebuilder_get(last->bld, basename);
 				if (e && git_tree_entry_type(e) != git_object__type_from_filemode(update->filemode)) {
 					git__free(basename);
-					giterr_set(GITERR_TREE, "Cannot replace '%s' with '%s' at '%s'",
+					giterr_set(GITERR_TREE, "cannot replace '%s' with '%s' at '%s'",
 						   git_object_type2string(git_tree_entry_type(e)),
 						   git_object_type2string(git_object__type_from_filemode(update->filemode)),
 						   update->path);
@@ -1249,7 +1270,7 @@
 				break;
 			}
 			default:
-				giterr_set(GITERR_TREE, "unkown action for update");
+				giterr_set(GITERR_TREE, "unknown action for update");
 				error = -1;
 				goto cleanup;
 		}
diff --git a/src/unix/map.c b/src/unix/map.c
index c55ad1a..9d9b1fe 100644
--- a/src/unix/map.c
+++ b/src/unix/map.c
@@ -52,7 +52,7 @@
 	out->data = mmap(NULL, len, mprot, mflag, fd, offset);
 
 	if (!out->data || out->data == MAP_FAILED) {
-		giterr_set(GITERR_OS, "Failed to mmap. Could not write data");
+		giterr_set(GITERR_OS, "failed to mmap. Could not write data");
 		return -1;
 	}
 
diff --git a/src/unix/posix.h b/src/unix/posix.h
index ad13291..52985fd 100644
--- a/src/unix/posix.h
+++ b/src/unix/posix.h
@@ -40,9 +40,14 @@
 #define p_link(o,n) link(o, n)
 #define p_unlink(p) unlink(p)
 #define p_mkdir(p,m) mkdir(p, m)
-#define p_fsync(fd) fsync(fd)
 extern char *p_realpath(const char *, char *);
 
+GIT_INLINE(int) p_fsync(int fd)
+{
+	p_fsync__cnt++;
+	return fsync(fd);
+}
+
 #define p_recv(s,b,l,f) recv(s,b,l,f)
 #define p_send(s,b,l,f) send(s,b,l,f)
 #define p_inet_pton(a, b, c) inet_pton(a, b, c)
@@ -50,7 +55,7 @@
 #define p_strcasecmp(s1, s2) strcasecmp(s1, s2)
 #define p_strncasecmp(s1, s2, c) strncasecmp(s1, s2, c)
 #define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a)
-#define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__)
+#define p_snprintf(b, c, ...) snprintf(b, c, __VA_ARGS__)
 #define p_mkstemp(p) mkstemp(p)
 #define p_chdir(p) chdir(p)
 #define p_chmod(p,m) chmod(p, m)
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/util.c b/src/util.c
index 76ca711..a44f4c9 100644
--- a/src/util.c
+++ b/src/util.c
@@ -136,7 +136,7 @@
 
 Return:
 	if (ndig == 0) {
-		giterr_set(GITERR_INVALID, "Failed to convert string to long. Not a number");
+		giterr_set(GITERR_INVALID, "failed to convert string to long: not a number");
 		return -1;
 	}
 
@@ -144,7 +144,7 @@
 		*endptr = p;
 
 	if (ovfl) {
-		giterr_set(GITERR_INVALID, "Failed to convert string to long. Overflow error");
+		giterr_set(GITERR_INVALID, "failed to convert string to long: overflow error");
 		return -1;
 	}
 
@@ -169,7 +169,7 @@
 
 	tmp_int = tmp_long & 0xFFFFFFFF;
 	if (tmp_int != tmp_long) {
-		giterr_set(GITERR_INVALID, "Failed to convert. '%s' is too large", nptr);
+		giterr_set(GITERR_INVALID, "failed to convert: '%s' is too large", nptr);
 		return -1;
 	}
 
diff --git a/src/vector.c b/src/vector.c
index baec803..620a1f5 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -406,6 +406,9 @@
 {
 	size_t a, b;
 
+	if (v->length == 0)
+		return;
+
 	a = 0;
 	b = v->length - 1;
 
diff --git a/src/win32/dir.c b/src/win32/dir.c
index c157570..8a724a4 100644
--- a/src/win32/dir.c
+++ b/src/win32/dir.c
@@ -28,7 +28,7 @@
 	new->h = FindFirstFileW(filter_w, &new->f);
 
 	if (new->h == INVALID_HANDLE_VALUE) {
-		giterr_set(GITERR_OS, "Could not open directory '%s'", dir);
+		giterr_set(GITERR_OS, "could not open directory '%s'", dir);
 		git__free(new);
 		return NULL;
 	}
@@ -53,7 +53,7 @@
 	else if (!FindNextFileW(d->h, &d->f)) {
 		if (GetLastError() == ERROR_NO_MORE_FILES)
 			return 0;
-		giterr_set(GITERR_OS, "Could not read from directory '%s'", d->dir);
+		giterr_set(GITERR_OS, "could not read from directory '%s'", d->dir);
 		return -1;
 	}
 
@@ -98,7 +98,7 @@
 	d->h = FindFirstFileW(filter_w, &d->f);
 
 	if (d->h == INVALID_HANDLE_VALUE)
-		giterr_set(GITERR_OS, "Could not open directory '%s'", d->dir);
+		giterr_set(GITERR_OS, "could not open directory '%s'", d->dir);
 	else
 		d->first = 1;
 }
diff --git a/src/win32/findfile.c b/src/win32/findfile.c
index 58c2227..1c768f7 100644
--- a/src/win32/findfile.c
+++ b/src/win32/findfile.c
@@ -38,7 +38,7 @@
 	git_win32_utf8_path utf8_path;
 
 	if (git_win32_path_to_utf8(utf8_path, src) < 0) {
-		giterr_set(GITERR_OS, "Unable to convert path to UTF-8");
+		giterr_set(GITERR_OS, "unable to convert path to UTF-8");
 		return -1;
 	}
 
diff --git a/src/win32/map.c b/src/win32/map.c
index 03a3646..5fcc108 100644
--- a/src/win32/map.c
+++ b/src/win32/map.c
@@ -67,7 +67,7 @@
 
 	if (fh == INVALID_HANDLE_VALUE) {
 		errno = EBADF;
-		giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value");
+		giterr_set(GITERR_OS, "failed to mmap. Invalid handle value");
 		return -1;
 	}
 
@@ -86,13 +86,13 @@
 
 	if (page_offset != 0) { /* offset must be multiple of the allocation granularity */
 		errno = EINVAL;
-		giterr_set(GITERR_OS, "Failed to mmap. Offset must be multiple of allocation granularity");
+		giterr_set(GITERR_OS, "failed to mmap. Offset must be multiple of allocation granularity");
 		return -1;
 	}
 
 	out->fmh = CreateFileMapping(fh, NULL, fmap_prot, 0, 0, NULL);
 	if (!out->fmh || out->fmh == INVALID_HANDLE_VALUE) {
-		giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value");
+		giterr_set(GITERR_OS, "failed to mmap. Invalid handle value");
 		out->fmh = NULL;
 		return -1;
 	}
@@ -103,7 +103,7 @@
 	off_hi = (DWORD)(page_start >> 32);
 	out->data = MapViewOfFile(out->fmh, view_prot, off_hi, off_low, len);
 	if (!out->data) {
-		giterr_set(GITERR_OS, "Failed to mmap. No data written");
+		giterr_set(GITERR_OS, "failed to mmap. No data written");
 		CloseHandle(out->fmh);
 		out->fmh = NULL;
 		return -1;
@@ -121,7 +121,7 @@
 
 	if (map->data) {
 		if (!UnmapViewOfFile(map->data)) {
-			giterr_set(GITERR_OS, "Failed to munmap. Could not unmap view of file");
+			giterr_set(GITERR_OS, "failed to munmap. Could not unmap view of file");
 			error = -1;
 		}
 		map->data = NULL;
@@ -129,7 +129,7 @@
 
 	if (map->fmh) {
 		if (!CloseHandle(map->fmh)) {
-			giterr_set(GITERR_OS, "Failed to munmap. Could not close handle");
+			giterr_set(GITERR_OS, "failed to munmap. Could not close handle");
 			error = -1;
 		}
 		map->fmh = NULL;
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index fea634b..5172627 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -113,6 +113,8 @@
 {
 	HANDLE fh = (HANDLE)_get_osfhandle(fd);
 
+	p_fsync__cnt++;
+
 	if (fh == INVALID_HANDLE_VALUE) {
 		errno = EBADF;
 		return -1;
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_crtdbg_stacktrace.c b/src/win32/w32_crtdbg_stacktrace.c
index a778f41..2dbdaf4 100644
--- a/src/win32/w32_crtdbg_stacktrace.c
+++ b/src/win32/w32_crtdbg_stacktrace.c
@@ -253,11 +253,11 @@
 	bool b_quiet            = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__QUIET);
 
 	if (b_leaks_since_mark && b_leaks_total) {
-		giterr_set(GITERR_INVALID, "Cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL.");
+		giterr_set(GITERR_INVALID, "cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL.");
 		return GIT_ERROR;
 	}
 	if (!b_set_mark && !b_leaks_since_mark && !b_leaks_total) {
-		giterr_set(GITERR_INVALID, "Nothing to do.");
+		giterr_set(GITERR_INVALID, "nothing to do.");
 		return GIT_ERROR;
 	}
 
diff --git a/src/win32/w32_util.c b/src/win32/w32_util.c
index 60311bb..b7b1ffa 100644
--- a/src/win32/w32_util.c
+++ b/src/win32/w32_util.c
@@ -68,7 +68,7 @@
 		newattrs = attrs & ~FILE_ATTRIBUTE_HIDDEN;
 
 	if (attrs != newattrs && !SetFileAttributesW(buf, newattrs)) {
-		giterr_set(GITERR_OS, "Failed to %s hidden bit for '%s'",
+		giterr_set(GITERR_OS, "failed to %s hidden bit for '%s'",
 			hidden ? "set" : "unset", path);
 		return -1;
 	}
diff --git a/src/win32/w32_util.h b/src/win32/w32_util.h
index 2e475e5..77973b5 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/worktree.c b/src/worktree.c
new file mode 100644
index 0000000..393a088
--- /dev/null
+++ b/src/worktree.c
@@ -0,0 +1,500 @@
+/*
+ * 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.
+ */
+
+#include "common.h"
+
+#include "git2/branch.h"
+#include "git2/commit.h"
+#include "git2/worktree.h"
+
+#include "repository.h"
+#include "worktree.h"
+
+static bool is_worktree_dir(const char *dir)
+{
+	git_buf buf = GIT_BUF_INIT;
+	int error;
+
+	if (git_buf_sets(&buf, dir) < 0)
+		return -1;
+
+	error = git_path_contains_file(&buf, "commondir")
+		&& git_path_contains_file(&buf, "gitdir")
+		&& git_path_contains_file(&buf, "HEAD");
+
+	git_buf_free(&buf);
+	return error;
+}
+
+int git_worktree_list(git_strarray *wts, git_repository *repo)
+{
+	git_vector worktrees = GIT_VECTOR_INIT;
+	git_buf path = GIT_BUF_INIT;
+	char *worktree;
+	unsigned i, len;
+	int error;
+
+	assert(wts && repo);
+
+	wts->count = 0;
+	wts->strings = NULL;
+
+	if ((error = git_buf_printf(&path, "%s/worktrees/", repo->commondir)) < 0)
+		goto exit;
+	if (!git_path_exists(path.ptr) || git_path_is_empty_dir(path.ptr))
+		goto exit;
+	if ((error = git_path_dirload(&worktrees, path.ptr, path.size, 0x0)) < 0)
+		goto exit;
+
+	len = path.size;
+
+	git_vector_foreach(&worktrees, i, worktree) {
+		git_buf_truncate(&path, len);
+		git_buf_puts(&path, worktree);
+
+		if (!is_worktree_dir(path.ptr)) {
+			git_vector_remove(&worktrees, i);
+			git__free(worktree);
+		}
+	}
+
+	wts->strings = (char **)git_vector_detach(&wts->count, NULL, &worktrees);
+
+exit:
+	git_buf_free(&path);
+
+	return error;
+}
+
+char *git_worktree__read_link(const char *base, const char *file)
+{
+	git_buf path = GIT_BUF_INIT, buf = GIT_BUF_INIT;
+
+	assert(base && file);
+
+	if (git_buf_joinpath(&path, base, file) < 0)
+		goto err;
+	if (git_futils_readbuffer(&buf, path.ptr) < 0)
+		goto err;
+	git_buf_free(&path);
+
+	git_buf_rtrim(&buf);
+
+	if (!git_path_is_relative(buf.ptr))
+		return git_buf_detach(&buf);
+
+	if (git_buf_sets(&path, base) < 0)
+		goto err;
+	if (git_path_apply_relative(&path, buf.ptr) < 0)
+		goto err;
+	git_buf_free(&buf);
+
+	return git_buf_detach(&path);
+
+err:
+	git_buf_free(&buf);
+	git_buf_free(&path);
+
+	return NULL;
+}
+
+static int write_wtfile(const char *base, const char *file, const git_buf *buf)
+{
+	git_buf path = GIT_BUF_INIT;
+	int err;
+
+	assert(base && file && buf);
+
+	if ((err = git_buf_joinpath(&path, base, file)) < 0)
+		goto out;
+
+	if ((err = git_futils_writebuffer(buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0)
+		goto out;
+
+out:
+	git_buf_free(&path);
+
+	return err;
+}
+
+static int open_worktree_dir(git_worktree **out, const char *parent, const char *dir, const char *name)
+{
+	git_buf gitdir = GIT_BUF_INIT;
+	git_worktree *wt = NULL;
+	int error = 0;
+
+	if (!is_worktree_dir(dir)) {
+		error = -1;
+		goto out;
+	}
+
+	if ((wt = git__calloc(1, sizeof(struct git_repository))) == NULL) {
+		error = -1;
+		goto out;
+	}
+
+	if ((wt->name = git__strdup(name)) == NULL
+	    || (wt->commondir_path = git_worktree__read_link(dir, "commondir")) == NULL
+	    || (wt->gitlink_path = git_worktree__read_link(dir, "gitdir")) == NULL
+	    || (wt->parent_path = git__strdup(parent)) == NULL) {
+		error = -1;
+		goto out;
+	}
+
+	if ((error = git_path_prettify_dir(&gitdir, dir, NULL)) < 0)
+		goto out;
+	wt->gitdir_path = git_buf_detach(&gitdir);
+
+	wt->locked = !!git_worktree_is_locked(NULL, wt);
+
+	*out = wt;
+
+out:
+	if (error)
+		git_worktree_free(wt);
+	git_buf_free(&gitdir);
+
+	return error;
+}
+
+int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name)
+{
+	git_buf path = GIT_BUF_INIT;
+	git_worktree *wt = NULL;
+	int error;
+
+	assert(repo && name);
+
+	*out = NULL;
+
+	if ((error = git_buf_printf(&path, "%s/worktrees/%s", repo->commondir, name)) < 0)
+		goto out;
+
+	if ((error = (open_worktree_dir(out, git_repository_workdir(repo), path.ptr, name))) < 0)
+		goto out;
+
+out:
+	git_buf_free(&path);
+
+	if (error)
+		git_worktree_free(wt);
+
+	return error;
+}
+
+int git_worktree_open_from_repository(git_worktree **out, git_repository *repo)
+{
+	git_buf parent = GIT_BUF_INIT;
+	const char *gitdir, *commondir;
+	char *name = NULL;
+	int error = 0;
+
+	if (!git_repository_is_worktree(repo)) {
+		giterr_set(GITERR_WORKTREE, "cannot open worktree of a non-worktree repo");
+		error = -1;
+		goto out;
+	}
+
+	gitdir = git_repository_path(repo);
+	commondir = git_repository_commondir(repo);
+
+	if ((error = git_path_prettify_dir(&parent, "..", commondir)) < 0)
+		goto out;
+
+	/* The name is defined by the last component in '.git/worktree/%s' */
+	name = git_path_basename(gitdir);
+
+	if ((error = open_worktree_dir(out, parent.ptr, gitdir, name)) < 0)
+		goto out;
+
+out:
+	if (error)
+		free(name);
+	git_buf_free(&parent);
+
+	return error;
+}
+
+void git_worktree_free(git_worktree *wt)
+{
+	if (!wt)
+		return;
+
+	git__free(wt->commondir_path);
+	git__free(wt->gitlink_path);
+	git__free(wt->gitdir_path);
+	git__free(wt->parent_path);
+	git__free(wt->name);
+	git__free(wt);
+}
+
+int git_worktree_validate(const git_worktree *wt)
+{
+	git_buf buf = GIT_BUF_INIT;
+	int err = 0;
+
+	assert(wt);
+
+	git_buf_puts(&buf, wt->gitdir_path);
+	if (!is_worktree_dir(buf.ptr)) {
+		giterr_set(GITERR_WORKTREE,
+			"Worktree gitdir ('%s') is not valid",
+			wt->gitlink_path);
+		err = -1;
+		goto out;
+	}
+
+	if (!git_path_exists(wt->parent_path)) {
+		giterr_set(GITERR_WORKTREE,
+			"Worktree parent directory ('%s') does not exist ",
+			wt->parent_path);
+		err = -2;
+		goto out;
+	}
+
+	if (!git_path_exists(wt->commondir_path)) {
+		giterr_set(GITERR_WORKTREE,
+			"Worktree common directory ('%s') does not exist ",
+			wt->commondir_path);
+		err = -3;
+		goto out;
+	}
+
+out:
+	git_buf_free(&buf);
+
+	return err;
+}
+
+int git_worktree_add(git_worktree **out, git_repository *repo, const char *name, const char *worktree)
+{
+	git_buf gitdir = GIT_BUF_INIT, wddir = GIT_BUF_INIT, buf = GIT_BUF_INIT;
+	git_reference *ref = NULL, *head = NULL;
+	git_commit *commit = NULL;
+	git_repository *wt = NULL;
+	git_checkout_options coopts = GIT_CHECKOUT_OPTIONS_INIT;
+	int err;
+
+	assert(out && repo && name && worktree);
+
+	*out = NULL;
+
+	/* Create gitdir directory ".git/worktrees/<name>" */
+	if ((err = git_buf_joinpath(&gitdir, repo->commondir, "worktrees")) < 0)
+		goto out;
+	if (!git_path_exists(gitdir.ptr))
+		if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0)
+			goto out;
+	if ((err = git_buf_joinpath(&gitdir, gitdir.ptr, name)) < 0)
+		goto out;
+	if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0)
+		goto out;
+	if ((err = git_path_prettify_dir(&gitdir, gitdir.ptr, NULL)) < 0)
+		goto out;
+
+	/* Create worktree work dir */
+	if ((err = git_futils_mkdir(worktree, 0755, GIT_MKDIR_EXCL)) < 0)
+		goto out;
+	if ((err = git_path_prettify_dir(&wddir, worktree, NULL)) < 0)
+		goto out;
+
+	/* Create worktree .git file */
+	if ((err = git_buf_printf(&buf, "gitdir: %s\n", gitdir.ptr)) < 0)
+		goto out;
+	if ((err = write_wtfile(wddir.ptr, ".git", &buf)) < 0)
+		goto out;
+
+	/* Create gitdir files */
+	if ((err = git_path_prettify_dir(&buf, repo->commondir, NULL) < 0)
+	    || (err = git_buf_putc(&buf, '\n')) < 0
+	    || (err = write_wtfile(gitdir.ptr, "commondir", &buf)) < 0)
+		goto out;
+	if ((err = git_buf_joinpath(&buf, wddir.ptr, ".git")) < 0
+	    || (err = git_buf_putc(&buf, '\n')) < 0
+	    || (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0)
+		goto out;
+
+	/* Create new branch */
+	if ((err = git_repository_head(&head, repo)) < 0)
+		goto out;
+	if ((err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0)
+		goto out;
+	if ((err = git_branch_create(&ref, repo, name, commit, false)) < 0)
+		goto out;
+
+	/* Set worktree's HEAD */
+	if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0)
+		goto out;
+	if ((err = git_repository_open(&wt, wddir.ptr)) < 0)
+		goto out;
+
+	/* Checkout worktree's HEAD */
+	coopts.checkout_strategy = GIT_CHECKOUT_FORCE;
+	if ((err = git_checkout_head(wt, &coopts)) < 0)
+		goto out;
+
+	/* Load result */
+	if ((err = git_worktree_lookup(out, repo, name)) < 0)
+		goto out;
+
+out:
+	git_buf_free(&gitdir);
+	git_buf_free(&wddir);
+	git_buf_free(&buf);
+	git_reference_free(ref);
+	git_reference_free(head);
+	git_commit_free(commit);
+	git_repository_free(wt);
+
+	return err;
+}
+
+int git_worktree_lock(git_worktree *wt, char *creason)
+{
+	git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
+	int err;
+
+	assert(wt);
+
+	if ((err = git_worktree_is_locked(NULL, wt)) < 0)
+		goto out;
+
+	if ((err = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0)
+		goto out;
+
+	if (creason)
+		git_buf_attach_notowned(&buf, creason, strlen(creason));
+
+	if ((err = git_futils_writebuffer(&buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0)
+		goto out;
+
+	wt->locked = 1;
+
+out:
+	git_buf_free(&path);
+
+	return err;
+}
+
+int git_worktree_unlock(git_worktree *wt)
+{
+	git_buf path = GIT_BUF_INIT;
+
+	assert(wt);
+
+	if (!git_worktree_is_locked(NULL, wt))
+		return 0;
+
+	if (git_buf_joinpath(&path, wt->gitdir_path, "locked") < 0)
+		return -1;
+
+	if (p_unlink(path.ptr) != 0) {
+		git_buf_free(&path);
+		return -1;
+	}
+
+	wt->locked = 0;
+
+	git_buf_free(&path);
+
+	return 0;
+}
+
+int git_worktree_is_locked(git_buf *reason, const git_worktree *wt)
+{
+	git_buf path = GIT_BUF_INIT;
+	int ret;
+
+	assert(wt);
+
+	if (reason)
+		git_buf_clear(reason);
+
+	if ((ret = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0)
+		goto out;
+	if ((ret = git_path_exists(path.ptr)) && reason)
+		git_futils_readbuffer(reason, path.ptr);
+
+out:
+	git_buf_free(&path);
+
+	return ret;
+}
+
+int git_worktree_is_prunable(git_worktree *wt, unsigned flags)
+{
+	git_buf reason = GIT_BUF_INIT;
+
+	if ((flags & GIT_WORKTREE_PRUNE_LOCKED) == 0 &&
+		git_worktree_is_locked(&reason, wt))
+	{
+		if (!reason.size)
+			git_buf_attach_notowned(&reason, "no reason given", 15);
+		giterr_set(GITERR_WORKTREE, "Not pruning locked working tree: '%s'", reason.ptr);
+		git_buf_free(&reason);
+
+		return 0;
+	}
+
+	if ((flags & GIT_WORKTREE_PRUNE_VALID) == 0 &&
+		git_worktree_validate(wt) == 0)
+	{
+		giterr_set(GITERR_WORKTREE, "Not pruning valid working tree");
+		return 0;
+	}
+
+	return 1;
+}
+
+int git_worktree_prune(git_worktree *wt, unsigned flags)
+{
+	git_buf path = GIT_BUF_INIT;
+	char *wtpath;
+	int err;
+
+	if (!git_worktree_is_prunable(wt, flags)) {
+		err = -1;
+		goto out;
+	}
+
+	/* Delete gitdir in parent repository */
+	if ((err = git_buf_printf(&path, "%s/worktrees/%s", wt->commondir_path, wt->name)) < 0)
+		goto out;
+	if (!git_path_exists(path.ptr))
+	{
+		giterr_set(GITERR_WORKTREE, "Worktree gitdir '%s' does not exist", path.ptr);
+		err = -1;
+		goto out;
+	}
+	if ((err = git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)) < 0)
+		goto out;
+
+	/* Skip deletion of the actual working tree if it does
+	 * not exist or deletion was not requested */
+	if ((flags & GIT_WORKTREE_PRUNE_WORKING_TREE) == 0 ||
+		!git_path_exists(wt->gitlink_path))
+	{
+		goto out;
+	}
+
+	if ((wtpath = git_path_dirname(wt->gitlink_path)) == NULL)
+		goto out;
+	git_buf_attach(&path, wtpath, 0);
+	if (!git_path_exists(path.ptr))
+	{
+		giterr_set(GITERR_WORKTREE, "Working tree '%s' does not exist", path.ptr);
+		err = -1;
+		goto out;
+	}
+	if ((err = git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)) < 0)
+		goto out;
+
+out:
+	git_buf_free(&path);
+
+	return err;
+}
diff --git a/src/worktree.h b/src/worktree.h
new file mode 100644
index 0000000..57c2e65
--- /dev/null
+++ b/src/worktree.h
@@ -0,0 +1,35 @@
+/*
+ * 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_worktree_h__
+#define INCLUDE_worktree_h__
+
+#include "git2/common.h"
+#include "git2/worktree.h"
+
+struct git_worktree {
+	/* Name of the working tree. This is the name of the
+	 * containing directory in the `$PARENT/.git/worktrees/`
+	 * directory. */
+	char *name;
+
+	/* Path to the .git file in the working tree's repository */
+	char *gitlink_path;
+	/* Path to the .git directory inside the parent's
+	 * worktrees directory */
+	char *gitdir_path;
+	/* Path to the common directory contained in the parent
+	 * repository */
+	char *commondir_path;
+	/* Path to the parent's working directory */
+	char *parent_path;
+
+	int locked:1;
+};
+
+char *git_worktree__read_link(const char *base, const char *file);
+
+#endif
diff --git a/src/zstream.c b/src/zstream.c
index d9ad4ca..141b49b 100644
--- a/src/zstream.c
+++ b/src/zstream.c
@@ -21,9 +21,9 @@
 	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");
+		giterr_set(GITERR_ZLIB, "unknown compression error");
 
 	return -1;
 }
diff --git a/tests/attr/ignore.c b/tests/attr/ignore.c
index f1fe1c7..3a19389 100644
--- a/tests/attr/ignore.c
+++ b/tests/attr/ignore.c
@@ -21,8 +21,8 @@
 {
 	int is_ignored = 0;
 
-	cl_git_pass_(
-		git_ignore_path_is_ignored(&is_ignored, g_repo, filepath), file, line);
+	cl_git_expect(
+		git_ignore_path_is_ignored(&is_ignored, g_repo, filepath), 0, file, line);
 
 	clar__assert_equal(
 		file, line, "expected != is_ignored", 1, "%d",
diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c
index 4a0314a..b3b860c 100644
--- a/tests/checkout/tree.c
+++ b/tests/checkout/tree.c
@@ -422,6 +422,41 @@
 	cl_assert(git_path_exists("testrepo/new.txt"));
 }
 
+void test_checkout_tree__pathlist_checkout_ignores_non_matches(void)
+{
+	char *entries[] = { "branch_file.txt", "link_to_new.txt" };
+
+	/* reset to beginning of history (i.e. just a README file) */
+
+	g_opts.checkout_strategy =
+		GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+	cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
+
+	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
+
+	cl_assert(git_path_exists("testrepo/README"));
+	cl_assert(git_path_exists("testrepo/branch_file.txt"));
+	cl_assert(git_path_exists("testrepo/link_to_new.txt"));
+	cl_assert(git_path_exists("testrepo/new.txt"));
+
+	git_object_free(g_object);
+	cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
+
+	g_opts.checkout_strategy =
+		GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
+	g_opts.paths.strings = entries;
+	g_opts.paths.count = 2;
+
+	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+	cl_assert(git_path_exists("testrepo/README"));
+	cl_assert(!git_path_exists("testrepo/branch_file.txt"));
+	cl_assert(!git_path_exists("testrepo/link_to_new.txt"));
+	cl_assert(git_path_exists("testrepo/new.txt"));
+}
+
 void test_checkout_tree__can_disable_pattern_match(void)
 {
 	char *entries[] = { "b*.txt" };
diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c
index 314d344..bd10c00 100644
--- a/tests/clar_libgit2.c
+++ b/tests/clar_libgit2.c
@@ -4,12 +4,20 @@
 #include "git2/sys/repository.h"
 
 void cl_git_report_failure(
-	int error, const char *file, int line, const char *fncall)
+	int error, int expected, const char *file, int line, const char *fncall)
 {
 	char msg[4096];
 	const git_error *last = giterr_last();
-	p_snprintf(msg, 4096, "error %d - %s",
-		error, last ? last->message : "<no message>");
+
+	if (expected)
+		p_snprintf(msg, 4096, "error %d (expected %d) - %s",
+			error, expected, last ? last->message : "<no message>");
+	else if (error || last)
+		p_snprintf(msg, 4096, "error %d - %s",
+			error, last ? last->message : "<no message>");
+	else
+		p_snprintf(msg, 4096, "no error, expected non-zero return");
+
 	clar__assert(0, file, line, fncall, msg, 1);
 }
 
diff --git a/tests/clar_libgit2.h b/tests/clar_libgit2.h
index d7e6353..c72d37d 100644
--- a/tests/clar_libgit2.h
+++ b/tests/clar_libgit2.h
@@ -12,13 +12,15 @@
  *
  * Use this wrapper around all `git_` library calls that return error codes!
  */
-#define cl_git_pass(expr) cl_git_pass_((expr), __FILE__, __LINE__)
+#define cl_git_pass(expr) cl_git_expect((expr), 0, __FILE__, __LINE__)
 
-#define cl_git_pass_(expr, file, line) do { \
+#define cl_git_fail_with(error, expr) cl_git_expect((expr), error, __FILE__, __LINE__)
+
+#define cl_git_expect(expr, expected, file, line) do { \
 	int _lg2_error; \
 	giterr_clear(); \
-	if ((_lg2_error = (expr)) != 0) \
-		cl_git_report_failure(_lg2_error, file, line, "Function call failed: " #expr); \
+	if ((_lg2_error = (expr)) != expected) \
+		cl_git_report_failure(_lg2_error, expected, file, line, "Function call failed: " #expr); \
 	} while (0)
 
 /**
@@ -26,9 +28,11 @@
  * just for consistency. Use with `git_` library
  * calls that are supposed to fail!
  */
-#define cl_git_fail(expr) cl_must_fail(expr)
-
-#define cl_git_fail_with(expr, error) cl_assert_equal_i(error,expr)
+#define cl_git_fail(expr) do { \
+	giterr_clear(); \
+	if ((expr) == 0) \
+		cl_git_report_failure(0, 0, __FILE__, __LINE__, "Function call succeeded: " #expr); \
+	} while (0)
 
 /**
  * Like cl_git_pass, only for Win32 error code conventions
@@ -37,11 +41,56 @@
 	int _win32_res; \
 	if ((_win32_res = (expr)) == 0) { \
 		giterr_set(GITERR_OS, "Returned: %d, system error code: %d", _win32_res, GetLastError()); \
-		cl_git_report_failure(_win32_res, __FILE__, __LINE__, "System call failed: " #expr); \
+		cl_git_report_failure(_win32_res, 0, __FILE__, __LINE__, "System call failed: " #expr); \
 	} \
 	} while(0)
 
-void cl_git_report_failure(int, const char *, int, const char *);
+/**
+ * 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, int, const char *, int, const char *);
 
 #define cl_assert_at_line(expr,file,line) \
 	clar__assert((expr) != 0, file, line, "Expression is not true: " #expr, NULL, 1)
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/filebuf.c b/tests/core/filebuf.c
index 04a380b..ef7ac6b 100644
--- a/tests/core/filebuf.c
+++ b/tests/core/filebuf.c
@@ -187,6 +187,35 @@
 	cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES));
 }
 
+void test_core_filebuf__symlink_follow_absolute_paths(void)
+{
+	git_filebuf file = GIT_FILEBUF_INIT;
+	git_buf source = GIT_BUF_INIT, target = GIT_BUF_INIT;
+
+#ifdef GIT_WIN32
+	cl_skip();
+#endif
+
+	cl_git_pass(git_buf_joinpath(&source, clar_sandbox_path(), "linkdir/link"));
+	cl_git_pass(git_buf_joinpath(&target, clar_sandbox_path(), "linkdir/target"));
+	cl_git_pass(p_mkdir("linkdir", 0777));
+	cl_git_pass(p_symlink(target.ptr, source.ptr));
+
+	cl_git_pass(git_filebuf_open(&file, source.ptr, 0, 0666));
+	cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+
+	cl_assert_equal_i(true, git_path_exists("linkdir/target.lock"));
+
+	cl_git_pass(git_filebuf_commit(&file));
+	cl_assert_equal_i(true, git_path_exists("linkdir/target"));
+
+	git_filebuf_cleanup(&file);
+	git_buf_free(&source);
+	git_buf_free(&target);
+
+	cl_git_pass(git_futils_rmdir_r("linkdir", NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
 void test_core_filebuf__symlink_depth(void)
 {
 	git_filebuf file = GIT_FILEBUF_INIT;
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/oidmap.c b/tests/core/oidmap.c
index 556a6ca..617da54 100644
--- a/tests/core/oidmap.c
+++ b/tests/core/oidmap.c
@@ -1,8 +1,6 @@
 #include "clar_libgit2.h"
 #include "oidmap.h"
 
-GIT__USE_OIDMAP
-
 typedef struct {
 	git_oid oid;
 	size_t extra;
@@ -33,23 +31,23 @@
 		khiter_t pos;
 		int ret;
 
-		pos = kh_get(oid, map, &items[i].oid);
-		cl_assert(pos == kh_end(map));
+		pos = git_oidmap_lookup_index(map, &items[i].oid);
+		cl_assert(!git_oidmap_valid_index(map, pos));
 
-		pos = kh_put(oid, map, &items[i].oid, &ret);
+		pos = git_oidmap_put(map, &items[i].oid, &ret);
 		cl_assert(ret != 0);
 
-		kh_val(map, pos) = &items[i];
+		git_oidmap_set_value_at(map, pos, &items[i]);
 	}
 
 
 	for (i = 0; i < NITEMS; ++i) {
 		khiter_t pos;
 
-		pos = kh_get(oid, map, &items[i].oid);
-		cl_assert(pos != kh_end(map));
+		pos = git_oidmap_lookup_index(map, &items[i].oid);
+		cl_assert(git_oidmap_valid_index(map, pos));
 
-		cl_assert_equal_p(kh_val(map, pos), &items[i]);
+		cl_assert_equal_p(git_oidmap_value_at(map, pos), &items[i]);
 	}
 
 	git_oidmap_free(map);
@@ -87,23 +85,23 @@
 		khiter_t pos;
 		int ret;
 
-		pos = kh_get(oid, map, &items[i].oid);
-		cl_assert(pos == kh_end(map));
+		pos = git_oidmap_lookup_index(map, &items[i].oid);
+		cl_assert(!git_oidmap_valid_index(map, pos));
 
-		pos = kh_put(oid, map, &items[i].oid, &ret);
+		pos = git_oidmap_put(map, &items[i].oid, &ret);
 		cl_assert(ret != 0);
 
-		kh_val(map, pos) = &items[i];
+		git_oidmap_set_value_at(map, pos, &items[i]);
 	}
 
 
 	for (i = 0; i < NITEMS; ++i) {
 		khiter_t pos;
 
-		pos = kh_get(oid, map, &items[i].oid);
-		cl_assert(pos != kh_end(map));
+		pos = git_oidmap_lookup_index(map, &items[i].oid);
+		cl_assert(git_oidmap_valid_index(map, pos));
 
-		cl_assert_equal_p(kh_val(map, pos), &items[i]);
+		cl_assert_equal_p(git_oidmap_value_at(map, pos), &items[i]);
 	}
 
 	git_oidmap_free(map);
diff --git a/tests/core/path.c b/tests/core/path.c
index 71c6eda..fefe2ae 100644
--- a/tests/core/path.c
+++ b/tests/core/path.c
@@ -89,8 +89,12 @@
 	check_dirname(REP16("/abc"), REP15("/abc"));
 
 #ifdef GIT_WIN32
+	check_dirname("C:/", "C:/");
+	check_dirname("C:", "C:/");
 	check_dirname("C:/path/", "C:/");
 	check_dirname("C:/path", "C:/");
+	check_dirname("//computername/", "//computername/");
+	check_dirname("//computername", "//computername/");
 	check_dirname("//computername/path/", "//computername/");
 	check_dirname("//computername/path", "//computername/");
 	check_dirname("//computername/sub/path/", "//computername/sub");
diff --git a/tests/core/posix.c b/tests/core/posix.c
index 26ae360..1724620 100644
--- a/tests/core/posix.c
+++ b/tests/core/posix.c
@@ -94,10 +94,7 @@
 	cl_assert(p_inet_pton(AF_INET, "10.foo.bar.1", &addr) == 0);
 
 	/* Test unsupported address families */
-	cl_git_fail(p_inet_pton(12, "52.472", NULL)); /* AF_DECnet */
-	cl_assert_equal_i(EAFNOSUPPORT, errno);
-
-	cl_git_fail(p_inet_pton(5, "315.124", NULL)); /* AF_CHAOS */
+	cl_git_fail(p_inet_pton(INT_MAX-1, "52.472", &addr));
 	cl_assert_equal_i(EAFNOSUPPORT, errno);
 }
 
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/core/sha1.c b/tests/core/sha1.c
new file mode 100644
index 0000000..c5b20f6
--- /dev/null
+++ b/tests/core/sha1.c
@@ -0,0 +1,64 @@
+#include "clar_libgit2.h"
+#include "hash.h"
+
+#define FIXTURE_DIR "sha1"
+
+void test_core_sha1__initialize(void)
+{
+	cl_fixture_sandbox(FIXTURE_DIR);
+}
+
+void test_core_sha1__cleanup(void)
+{
+	cl_fixture_cleanup(FIXTURE_DIR);
+}
+
+static int sha1_file(git_oid *oid, const char *filename)
+{
+	git_hash_ctx ctx;
+	char buf[2048];
+	int fd, ret;
+	ssize_t read_len;
+
+	fd = p_open(filename, O_RDONLY);
+	cl_assert(fd >= 0);
+
+	cl_git_pass(git_hash_ctx_init(&ctx));
+
+	while ((read_len = p_read(fd, buf, 2048)) > 0)
+		cl_git_pass(git_hash_update(&ctx, buf, (size_t)read_len));
+
+	cl_assert_equal_i(0, read_len);
+	p_close(fd);
+
+	ret = git_hash_final(oid, &ctx);
+	git_hash_ctx_cleanup(&ctx);
+
+	return ret;
+}
+
+void test_core_sha1__sum(void)
+{
+	git_oid oid, expected;
+
+	cl_git_pass(sha1_file(&oid, FIXTURE_DIR "/hello_c"));
+	git_oid_fromstr(&expected, "4e72679e3ea4d04e0c642f029e61eb8056c7ed94");
+	cl_assert_equal_oid(&expected, &oid);
+}
+
+/* test that sha1 collision detection works when enabled */
+void test_core_sha1__detect_collision_attack(void)
+{
+	git_oid oid, expected;
+
+#ifdef GIT_SHA1_COLLISIONDETECT
+	GIT_UNUSED(expected);
+	cl_git_fail(sha1_file(&oid, FIXTURE_DIR "/shattered-1.pdf"));
+	cl_assert_equal_s("SHA1 collision attack detected", giterr_last()->message);
+#else
+	cl_git_pass(sha1_file(&oid, FIXTURE_DIR "/shattered-1.pdf"));
+	git_oid_fromstr(&expected, "38762cf7f55934b34d179ae6a4c80cadccbb7f0a");
+	cl_assert_equal_oid(&expected, &oid);
+#endif
+}
+
diff --git a/tests/core/strmap.c b/tests/core/strmap.c
index 3b4276a..2fa594d 100644
--- a/tests/core/strmap.c
+++ b/tests/core/strmap.c
@@ -1,8 +1,6 @@
 #include "clar_libgit2.h"
 #include "strmap.h"
 
-GIT__USE_STRMAP
-
 git_strmap *g_table;
 
 void test_core_strmap__initialize(void)
@@ -36,7 +34,7 @@
 		for (j = 0, over = i / 26; over > 0; j++, over = over / 26)
 			str[j] = 'A' + (over % 26);
 
-		git_strmap_insert(table, str, str, err);
+		git_strmap_insert(table, str, str, &err);
 		cl_assert(err >= 0);
 	}
 
diff --git a/tests/core/vector.c b/tests/core/vector.c
index 336254c..c2e5d3f 100644
--- a/tests/core/vector.c
+++ b/tests/core/vector.c
@@ -404,4 +404,6 @@
 
 	for (i = 0; i < 5; i++)
 		cl_assert_equal_p(out2[i], git_vector_get(&v, i));
+
+	git_vector_free(&v);
 }
diff --git a/tests/diff/format_email.c b/tests/diff/format_email.c
index 9f8fe31..c3c0410 100644
--- a/tests/diff/format_email.c
+++ b/tests/diff/format_email.c
@@ -113,7 +113,7 @@
 	"Also test if new paragraphs are included correctly.\n" \
 	"---\n" \
 	" file3.txt | 1 +\n" \
-	" 1 file changed, 1 insertion(+), 0 deletions(-)\n" \
+	" 1 file changed, 1 insertion(+)\n" \
 	"\n" \
 	"diff --git a/file3.txt b/file3.txt\n" \
 	"index 9a2d780..7309653 100644\n" \
@@ -156,7 +156,7 @@
 	"---\n" \
 	" file2.txt | 5 +++++\n" \
 	" file3.txt | 5 +++++\n" \
-	" 2 files changed, 10 insertions(+), 0 deletions(-)\n" \
+	" 2 files changed, 10 insertions(+)\n" \
 	" create mode 100644 file2.txt\n" \
 	" create mode 100644 file3.txt\n" \
 	"\n" \
diff --git a/tests/diff/parse.c b/tests/diff/parse.c
index a06813d..acb6eb8 100644
--- a/tests/diff/parse.c
+++ b/tests/diff/parse.c
@@ -196,3 +196,74 @@
 
 	cl_git_sandbox_cleanup();
 }
+
+static int file_cb(const git_diff_delta *delta, float progress, void *payload)
+{
+    int *called = (int *) payload;
+    GIT_UNUSED(delta);
+    GIT_UNUSED(progress);
+    (*called)++;
+    return 0;
+}
+
+void test_diff_parse__foreach_works_with_parsed_patch(void)
+{
+	const char patch[] =
+	    "diff --git a/obj1 b/obj2\n"
+	    "index 1234567..7654321 10644\n"
+	    "--- a/obj1\n"
+	    "+++ b/obj2\n"
+	    "@@ -1 +1 @@\n"
+	    "-abcde\n"
+	    "+12345\n";
+	int called = 0;
+	git_diff *diff;
+
+	cl_git_pass(git_diff_from_buffer(&diff, patch, strlen(patch)));
+	cl_git_pass(git_diff_foreach(diff, file_cb, NULL, NULL, NULL, &called));
+	cl_assert_equal_i(called, 1);
+
+	git_diff_free(diff);
+}
+
+void test_diff_parse__parsing_minimal_patch_succeeds(void)
+{
+	const char patch[] =
+	    "diff --git a/obj1 b/obj2\n"
+	    "index 1234567..7654321 10644\n"
+	    "--- a/obj1\n"
+	    "+++ b/obj2\n"
+	    "@@ -1 +1 @@\n"
+	    "-a\n"
+	    "+\n";
+	git_buf buf = GIT_BUF_INIT;
+	git_diff *diff;
+
+	cl_git_pass(git_diff_from_buffer(&diff, patch, strlen(patch)));
+	cl_git_pass(git_diff_to_buf(&buf, diff, GIT_DIFF_FORMAT_PATCH));
+	cl_assert_equal_s(patch, buf.ptr);
+
+	git_diff_free(diff);
+	git_buf_free(&buf);
+}
+
+void test_diff_parse__patch_roundtrip_succeeds(void)
+{
+	const char buf1[] = "a\n", buf2[] = "b\n";
+	git_buf patchbuf = GIT_BUF_INIT, diffbuf = GIT_BUF_INIT;
+	git_patch *patch;
+	git_diff *diff;
+
+	cl_git_pass(git_patch_from_buffers(&patch, buf1, strlen(buf1), "obj1", buf2, strlen(buf2), "obj2", NULL));
+	cl_git_pass(git_patch_to_buf(&patchbuf, patch));
+
+	cl_git_pass(git_diff_from_buffer(&diff, patchbuf.ptr, patchbuf.size));
+	cl_git_pass(git_diff_to_buf(&diffbuf, diff, GIT_DIFF_FORMAT_PATCH));
+
+	cl_assert_equal_s(patchbuf.ptr, diffbuf.ptr);
+
+	git_patch_free(patch);
+	git_diff_free(diff);
+	git_buf_free(&patchbuf);
+	git_buf_free(&diffbuf);
+}
diff --git a/tests/diff/stats.c b/tests/diff/stats.c
index 8f146e2..3171a44 100644
--- a/tests/diff/stats.c
+++ b/tests/diff/stats.c
@@ -114,6 +114,42 @@
 	git_buf_free(&buf);
 }
 
+void test_diff_stats__shortstat_noinsertions(void)
+{
+	git_buf buf = GIT_BUF_INIT;
+	const char *stat =
+	" 1 file changed, 2 deletions(-)\n";
+
+	diff_stats_from_commit_oid(
+		&_stats, "06b7b69a62cbd1e53c6c4e0c3f16473dcfdb4af6", false);
+
+	cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
+	cl_assert_equal_sz(0, git_diff_stats_insertions(_stats));
+	cl_assert_equal_sz(2, git_diff_stats_deletions(_stats));
+
+	cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_SHORT, 0));
+	cl_assert_equal_s(stat, git_buf_cstr(&buf));
+	git_buf_free(&buf);
+}
+
+void test_diff_stats__shortstat_nodeletions(void)
+{
+	git_buf buf = GIT_BUF_INIT;
+	const char *stat =
+	" 1 file changed, 3 insertions(+)\n";
+
+	diff_stats_from_commit_oid(
+		&_stats, "5219b9784f9a92d7bd7cb567a6d6a21bfb86697e", false);
+
+	cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
+	cl_assert_equal_sz(3, git_diff_stats_insertions(_stats));
+	cl_assert_equal_sz(0, git_diff_stats_deletions(_stats));
+
+	cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_SHORT, 0));
+	cl_assert_equal_s(stat, git_buf_cstr(&buf));
+	git_buf_free(&buf);
+}
+
 void test_diff_stats__rename(void)
 {
 	git_buf buf = GIT_BUF_INIT;
diff --git a/tests/fetchhead/nonetwork.c b/tests/fetchhead/nonetwork.c
index 3b750af..ea4b70e 100644
--- a/tests/fetchhead/nonetwork.c
+++ b/tests/fetchhead/nonetwork.c
@@ -293,7 +293,7 @@
 	cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\tinvalid-merge\t\n");
 	cl_git_fail(git_repository_fetchhead_foreach(g_repo, read_noop, NULL));
 
-	cl_assert(git__prefixcmp(giterr_last()->message, "Invalid for-merge") == 0);
+	cl_assert(git__prefixcmp(giterr_last()->message, "invalid for-merge") == 0);
 }
 
 void test_fetchhead_nonetwork__invalid_description(void)
@@ -304,7 +304,7 @@
 	cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\n");
 	cl_git_fail(git_repository_fetchhead_foreach(g_repo, read_noop, NULL));
 
-	cl_assert(git__prefixcmp(giterr_last()->message, "Invalid description") == 0);
+	cl_assert(git__prefixcmp(giterr_last()->message, "invalid description") == 0);
 }
 
 static int assert_master_for_merge(const char *ref, const char *url, const git_oid *id, unsigned int is_merge, void *data)
diff --git a/tests/iterator/workdir.c b/tests/iterator/workdir.c
index 28fcc0d..f33fd98 100644
--- a/tests/iterator/workdir.c
+++ b/tests/iterator/workdir.c
@@ -613,9 +613,11 @@
 		"heads/ident",
 		"heads/long-file-name",
 		"heads/master",
+		"heads/merge-conflict",
 		"heads/packed-test",
 		"heads/subtrees",
 		"heads/test",
+		"heads/testrepo-worktree",
 		"tags/e90810b",
 		"tags/foo/bar",
 		"tags/foo/foo/bar",
@@ -628,7 +630,7 @@
 
 	cl_git_pass(git_iterator_for_filesystem(
 		&i, "testrepo/.git/refs", NULL));
-	expect_iterator_items(i, 13, expect_base, 13, expect_base);
+	expect_iterator_items(i, 15, expect_base, 15, expect_base);
 	git_iterator_free(i);
 }
 
diff --git a/tests/merge/trees/renames.c b/tests/merge/trees/renames.c
index d7721c8..853bf2f 100644
--- a/tests/merge/trees/renames.c
+++ b/tests/merge/trees/renames.c
@@ -242,6 +242,8 @@
 		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" },
 	};
 
+	opts.flags &= ~GIT_MERGE_FIND_RENAMES;
+
 	cl_git_pass(merge_trees_from_branches(&index, repo,
 		BRANCH_RENAME_OURS, BRANCH_RENAME_THEIRS,
 		&opts));
@@ -250,3 +252,25 @@
 
 	git_index_free(index);
 }
+
+void test_merge_trees_renames__submodules(void)
+{
+	git_index *index;
+	git_merge_options *opts = NULL;
+
+	struct merge_index_entry merge_index_entries[] = {
+		{ 0100644, "cd3e8d4aa06bdc781f264171030bc28f2b370fee", 0, ".gitmodules" },
+		{ 0100644, "4dd1ef7569b18d92d93c0a35bb6b93049137b355", 1, "file.txt" },
+		{ 0100644, "a2d8d1824c68541cca94ffb90f79291eba495921", 2, "file.txt" },
+		{ 0100644, "63ec604d491161ddafdae4179843c26d54bd999a", 3, "file.txt" },
+		{ 0160000, "0000000000000000000000000000000000000001", 1, "submodule1" },
+		{ 0160000, "0000000000000000000000000000000000000002", 3, "submodule1" },
+		{ 0160000, "0000000000000000000000000000000000000003", 0, "submodule2" },
+	};
+
+	cl_git_pass(merge_trees_from_branches(&index, repo,
+		"submodule_rename1", "submodule_rename2",
+		opts));
+	cl_assert(merge_test_index(index, merge_index_entries, 7));
+	git_index_free(index);
+}
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/odb/backend/nobackend.c b/tests/odb/backend/nobackend.c
index 783641e..3c4f344 100644
--- a/tests/odb/backend/nobackend.c
+++ b/tests/odb/backend/nobackend.c
@@ -40,7 +40,7 @@
 	cl_git_fail(git_odb_write(&id, odb, "Hello world!\n", 13, GIT_OBJ_BLOB));
 
 	err = giterr_last();
-	cl_assert_equal_s(err->message, "Cannot write object - unsupported in the loaded odb backends");
+	cl_assert_equal_s(err->message, "cannot write object - unsupported in the loaded odb backends");
 
 	git_odb_free(odb);
 }
diff --git a/tests/odb/freshen.c b/tests/odb/freshen.c
index d8d6c02..f41e436 100644
--- a/tests/odb/freshen.c
+++ b/tests/odb/freshen.c
@@ -17,36 +17,101 @@
 	cl_git_sandbox_cleanup();
 }
 
-#define LOOSE_STR "hey\n"
-#define LOOSE_ID  "1385f264afb75a56a5bec74243be9b367ba4ca08"
-#define LOOSE_FN  "13/85f264afb75a56a5bec74243be9b367ba4ca08"
+static void set_time_wayback(struct stat *out, const char *fn)
+{
+	git_buf fullpath = GIT_BUF_INIT;
+	struct p_timeval old[2];
 
-void test_odb_freshen__loose_object(void)
+	old[0].tv_sec = 1234567890;
+	old[0].tv_usec = 0;
+	old[1].tv_sec = 1234567890;
+	old[1].tv_usec = 0;
+
+	git_buf_joinpath(&fullpath, "testrepo.git/objects", fn);
+
+	cl_must_pass(p_utimes(git_buf_cstr(&fullpath), old));
+	cl_must_pass(p_lstat(git_buf_cstr(&fullpath), out));
+	git_buf_free(&fullpath);
+}
+
+#define LOOSE_STR     "my new file\n"
+#define LOOSE_BLOB_ID "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"
+#define LOOSE_BLOB_FN "a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd"
+
+void test_odb_freshen__loose_blob(void)
 {
 	git_oid expected_id, id;
 	struct stat before, after;
-	struct p_timeval old_times[2];
 
-	cl_git_pass(git_oid_fromstr(&expected_id, LOOSE_ID));
+	cl_git_pass(git_oid_fromstr(&expected_id, LOOSE_BLOB_ID));
+	set_time_wayback(&before, LOOSE_BLOB_FN);
 
-	old_times[0].tv_sec = 1234567890;
-	old_times[0].tv_usec = 0;
-	old_times[1].tv_sec = 1234567890;
-	old_times[1].tv_usec = 0;
-
-	/* set time to way back */
-	cl_must_pass(p_utimes("testrepo.git/objects/" LOOSE_FN, old_times));
-	cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_FN, &before));
-
-	cl_git_pass(git_odb_write(&id, odb, LOOSE_STR, CONST_STRLEN(LOOSE_STR),
-		GIT_OBJ_BLOB));
+	/* make sure we freshen a blob */
+	cl_git_pass(git_blob_create_frombuffer(&id, repo, LOOSE_STR, CONST_STRLEN(LOOSE_STR)));
 	cl_assert_equal_oid(&expected_id, &id);
-	cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_FN, &after));
+	cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_BLOB_FN, &after));
 
 	cl_assert(before.st_atime < after.st_atime);
 	cl_assert(before.st_mtime < after.st_mtime);
 }
 
+#define LOOSE_TREE_ID "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162"
+#define LOOSE_TREE_FN "94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162"
+
+void test_odb_freshen__loose_tree(void)
+{
+	git_oid expected_id, id;
+	git_tree *tree;
+	struct stat before, after;
+
+	cl_git_pass(git_oid_fromstr(&expected_id, LOOSE_TREE_ID));
+	set_time_wayback(&before, LOOSE_TREE_FN);
+
+	cl_git_pass(git_tree_lookup(&tree, repo, &expected_id));
+	cl_git_pass(git_tree_create_updated(&id, repo, tree, 0, NULL));
+
+	/* make sure we freshen a tree */
+	cl_assert_equal_oid(&expected_id, &id);
+	cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_TREE_FN, &after));
+
+	cl_assert(before.st_atime < after.st_atime);
+	cl_assert(before.st_mtime < after.st_mtime);
+
+	git_tree_free(tree);
+}
+
+void test_odb_freshen__tree_during_commit(void)
+{
+	git_oid tree_id, parent_id, commit_id;
+	git_tree *tree;
+	git_commit *parent;
+	git_signature *signature;
+	struct stat before, after;
+
+	cl_git_pass(git_oid_fromstr(&tree_id, LOOSE_TREE_ID));
+	cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
+	set_time_wayback(&before, LOOSE_TREE_FN);
+
+	cl_git_pass(git_oid_fromstr(&parent_id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"));
+	cl_git_pass(git_commit_lookup(&parent, repo, &parent_id));
+
+	cl_git_pass(git_signature_new(&signature,
+		"Refresher", "refresher@example.com", 1488547083, 0));
+
+	cl_git_pass(git_commit_create(&commit_id, repo, NULL,
+		signature, signature, NULL, "New commit pointing to old tree",
+		tree, 1, (const git_commit **)&parent));
+
+	/* make sure we freshen the tree the commit points to */
+	cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_TREE_FN, &after));
+	cl_assert(before.st_atime < after.st_atime);
+	cl_assert(before.st_mtime < after.st_mtime);
+
+	git_signature_free(signature);
+	git_commit_free(parent);
+	git_tree_free(tree);
+}
+
 #define PACKED_STR "Testing a readme.txt\n"
 #define PACKED_ID  "6336846bd5c88d32f93ae57d846683e61ab5c530"
 #define PACKED_FN  "pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack"
diff --git a/tests/odb/loose.c b/tests/odb/loose.c
index c91927c..dd686aa 100644
--- a/tests/odb/loose.c
+++ b/tests/odb/loose.c
@@ -3,6 +3,7 @@
 #include "git2/odb_backend.h"
 #include "posix.h"
 #include "loose_data.h"
+#include "repository.h"
 
 #ifdef __ANDROID_API__
 # define S_IREAD        S_IRUSR
@@ -56,11 +57,13 @@
 
 void test_odb_loose__initialize(void)
 {
+	p_fsync__cnt = 0;
 	cl_must_pass(p_mkdir("test-objects", GIT_OBJECT_DIR_MODE));
 }
 
 void test_odb_loose__cleanup(void)
 {
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION, 0));
 	cl_fixture_cleanup("test-objects");
 }
 
@@ -150,3 +153,55 @@
 {
 	test_write_object_permission(0777, 0666, 0777, 0666);
 }
+
+static void write_object_to_loose_odb(int fsync)
+{
+	git_odb *odb;
+	git_odb_backend *backend;
+	git_oid oid;
+
+	cl_git_pass(git_odb_new(&odb));
+	cl_git_pass(git_odb_backend_loose(&backend, "test-objects", -1, fsync, 0777, 0666));
+	cl_git_pass(git_odb_add_backend(odb, backend, 1));
+	cl_git_pass(git_odb_write(&oid, odb, "Test data\n", 10, GIT_OBJ_BLOB));
+	git_odb_free(odb);
+}
+
+void test_odb_loose__does_not_fsync_by_default(void)
+{
+	write_object_to_loose_odb(0);
+	cl_assert_equal_sz(0, p_fsync__cnt);
+}
+
+void test_odb_loose__fsync_obeys_odb_option(void)
+{
+	write_object_to_loose_odb(1);
+	cl_assert(p_fsync__cnt > 0);
+}
+
+void test_odb_loose__fsync_obeys_global_setting(void)
+{
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION, 1));
+	write_object_to_loose_odb(0);
+	cl_assert(p_fsync__cnt > 0);
+}
+
+void test_odb_loose__fsync_obeys_repo_setting(void)
+{
+	git_repository *repo;
+	git_odb *odb;
+	git_oid oid;
+
+	cl_git_pass(git_repository_init(&repo, "test-objects", 1));
+	cl_git_pass(git_repository_odb__weakptr(&odb, repo));
+	cl_git_pass(git_odb_write(&oid, odb, "No fsync here\n", 14, GIT_OBJ_BLOB));
+	cl_assert(p_fsync__cnt == 0);
+	git_repository_free(repo);
+
+	cl_git_pass(git_repository_open(&repo, "test-objects"));
+	cl_repo_set_bool(repo, "core.fsyncObjectFiles", true);
+	cl_git_pass(git_repository_odb__weakptr(&odb, repo));
+	cl_git_pass(git_odb_write(&oid, odb, "Now fsync\n", 10, GIT_OBJ_BLOB));
+	cl_assert(p_fsync__cnt > 0);
+	git_repository_free(repo);
+}
diff --git a/tests/online/badssl.c b/tests/online/badssl.c
index 66b090d..aa4c24d 100644
--- a/tests/online/badssl.c
+++ b/tests/online/badssl.c
@@ -10,37 +10,71 @@
 static bool g_has_ssl = false;
 #endif
 
+static int cert_check_assert_invalid(git_cert *cert, int valid, const char* host, void *payload)
+{
+	GIT_UNUSED(cert); GIT_UNUSED(host); GIT_UNUSED(payload);
+
+	cl_assert_equal_i(0, valid);
+
+	return GIT_ECERTIFICATE;
+}
+
 void test_online_badssl__expired(void)
 {
+	git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+	opts.fetch_opts.callbacks.certificate_check = cert_check_assert_invalid;
+
 	if (!g_has_ssl)
 		cl_skip();
 
 	cl_git_fail_with(GIT_ECERTIFICATE,
 			 git_clone(&g_repo, "https://expired.badssl.com/fake.git", "./fake", NULL));
+
+	cl_git_fail_with(GIT_ECERTIFICATE,
+			 git_clone(&g_repo, "https://expired.badssl.com/fake.git", "./fake", &opts));
 }
 
 void test_online_badssl__wrong_host(void)
 {
+	git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+	opts.fetch_opts.callbacks.certificate_check = cert_check_assert_invalid;
+
 	if (!g_has_ssl)
 		cl_skip();
 
 	cl_git_fail_with(GIT_ECERTIFICATE,
 			 git_clone(&g_repo, "https://wrong.host.badssl.com/fake.git", "./fake", NULL));
+	cl_git_fail_with(GIT_ECERTIFICATE,
+			 git_clone(&g_repo, "https://wrong.host.badssl.com/fake.git", "./fake", &opts));
 }
 
 void test_online_badssl__self_signed(void)
 {
+	git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+	opts.fetch_opts.callbacks.certificate_check = cert_check_assert_invalid;
+
 	if (!g_has_ssl)
 		cl_skip();
 
 	cl_git_fail_with(GIT_ECERTIFICATE,
 			 git_clone(&g_repo, "https://self-signed.badssl.com/fake.git", "./fake", NULL));
+	cl_git_fail_with(GIT_ECERTIFICATE,
+			 git_clone(&g_repo, "https://self-signed.badssl.com/fake.git", "./fake", &opts));
 }
 
 void test_online_badssl__old_cipher(void)
 {
+	git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+	opts.fetch_opts.callbacks.certificate_check = cert_check_assert_invalid;
+
+	/* FIXME: we don't actually reject RC4 anywhere, figure out what to tweak */
+	cl_skip();
+
 	if (!g_has_ssl)
 		cl_skip();
 
-	cl_git_fail(git_clone(&g_repo, "https://rc4.badssl.com/fake.git", "./fake", NULL));
+	cl_git_fail_with(GIT_ECERTIFICATE,
+			 git_clone(&g_repo, "https://rc4.badssl.com/fake.git", "./fake", NULL));
+	cl_git_fail_with(GIT_ECERTIFICATE,
+			 git_clone(&g_repo, "https://rc4.badssl.com/fake.git", "./fake", &opts));
 }
diff --git a/tests/online/fetchhead.c b/tests/online/fetchhead.c
index 9aaad25..c1ac06d 100644
--- a/tests/online/fetchhead.c
+++ b/tests/online/fetchhead.c
@@ -126,6 +126,8 @@
 
 	cl_git_pass(git_branch_lookup(&ref, g_repo, "explicit-refspec", GIT_BRANCH_ALL));
 	cl_assert_equal_i(refs + 1, count_references());
+
+	git_reference_free(ref);
 }
 
 void test_online_fetchhead__empty_dst_refspec_creates_no_branch(void)
diff --git a/tests/pack/indexer.c b/tests/pack/indexer.c
index 49a106d..1e514b2 100644
--- a/tests/pack/indexer.c
+++ b/tests/pack/indexer.c
@@ -125,3 +125,44 @@
 		git_indexer_free(idx);
 	}
 }
+
+static int find_tmp_file_recurs(void *opaque, git_buf *path)
+{
+	int error = 0;
+	git_buf *first_tmp_file = opaque;
+	struct stat st;
+
+	if ((error = p_lstat_posixly(path->ptr, &st)) < 0)
+		return error;
+
+	if (S_ISDIR(st.st_mode))
+		return git_path_direach(path, 0, find_tmp_file_recurs, opaque);
+
+	/* This is the template that's used in git_futils_mktmp. */
+	if (strstr(git_buf_cstr(path), "_git2_") != NULL)
+		return git_buf_sets(first_tmp_file, git_buf_cstr(path));
+
+	return 0;
+}
+
+void test_pack_indexer__no_tmp_files(void)
+{
+	git_indexer *idx = NULL;
+	git_buf path = GIT_BUF_INIT;
+	git_buf first_tmp_file = GIT_BUF_INIT;
+
+	/* Precondition: there are no temporary files. */
+	cl_git_pass(git_buf_sets(&path, clar_sandbox_path()));
+	cl_git_pass(find_tmp_file_recurs(&first_tmp_file, &path));
+	git_buf_free(&path);
+	cl_assert(git_buf_len(&first_tmp_file) == 0);
+
+	cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
+	git_indexer_free(idx);
+
+	cl_git_pass(git_buf_sets(&path, clar_sandbox_path()));
+	cl_git_pass(find_tmp_file_recurs(&first_tmp_file, &path));
+	git_buf_free(&path);
+	cl_assert(git_buf_len(&first_tmp_file) == 0);
+	git_buf_free(&first_tmp_file);
+}
diff --git a/tests/pack/packbuilder.c b/tests/pack/packbuilder.c
index 29f3e2d..1d7bece 100644
--- a/tests/pack/packbuilder.c
+++ b/tests/pack/packbuilder.c
@@ -23,6 +23,7 @@
 	cl_git_pass(git_vector_init(&_commits, 0, NULL));
 	_commits_is_initialized = 1;
 	memset(&_stats, 0, sizeof(_stats));
+	p_fsync__cnt = 0;
 }
 
 void test_pack_packbuilder__cleanup(void)
@@ -30,6 +31,8 @@
 	git_oid *o;
 	unsigned int i;
 
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION, 0));
+
 	if (_commits_is_initialized) {
 		_commits_is_initialized = 0;
 		git_vector_foreach(&_commits, i, o) {
@@ -188,6 +191,40 @@
 	test_write_pack_permission(0666, 0666);
 }
 
+void test_pack_packbuilder__does_not_fsync_by_default(void)
+{
+	seed_packbuilder();
+	git_packbuilder_write(_packbuilder, ".", 0666, NULL, NULL);
+	cl_assert_equal_sz(0, p_fsync__cnt);
+}
+
+/* We fsync the packfile and index.  On non-Windows, we also fsync
+ * the parent directories.
+ */
+#ifdef GIT_WIN32
+static int expected_fsyncs = 2;
+#else
+static int expected_fsyncs = 4;
+#endif
+
+void test_pack_packbuilder__fsync_global_setting(void)
+{
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION, 1));
+	p_fsync__cnt = 0;
+	seed_packbuilder();
+	git_packbuilder_write(_packbuilder, ".", 0666, NULL, NULL);
+	cl_assert_equal_sz(expected_fsyncs, p_fsync__cnt);
+}
+
+void test_pack_packbuilder__fsync_repo_setting(void)
+{
+	cl_repo_set_bool(_repo, "core.fsyncObjectFiles", true);
+	p_fsync__cnt = 0;
+	seed_packbuilder();
+	git_packbuilder_write(_packbuilder, ".", 0666, NULL, NULL);
+	cl_assert_equal_sz(expected_fsyncs, p_fsync__cnt);
+}
+
 static int foreach_cb(void *buf, size_t len, void *payload)
 {
 	git_indexer *idx = (git_indexer *) payload;
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/rebase/submodule.c b/tests/rebase/submodule.c
new file mode 100644
index 0000000..7a38ab8
--- /dev/null
+++ b/tests/rebase/submodule.c
@@ -0,0 +1,65 @@
+#include "clar_libgit2.h"
+#include "git2/checkout.h"
+#include "git2/rebase.h"
+#include "posix.h"
+#include "signature.h"
+
+#include <fcntl.h>
+
+static git_repository *repo;
+static git_signature *signature;
+
+// Fixture setup and teardown
+void test_rebase_submodule__initialize(void)
+{
+	repo = cl_git_sandbox_init("rebase-submodule");
+	cl_git_pass(git_signature_new(&signature,
+		"Rebaser", "rebaser@rebaser.rb", 1405694510, 0));
+}
+
+void test_rebase_submodule__cleanup(void)
+{
+	git_signature_free(signature);
+	cl_git_sandbox_cleanup();
+}
+
+void test_rebase_submodule__init_untracked(void)
+{
+	git_rebase *rebase;
+	git_reference *branch_ref, *upstream_ref;
+	git_annotated_commit *branch_head, *upstream_head;
+	git_buf untracked_path = GIT_BUF_INIT;
+	FILE *fp;
+	git_submodule *submodule;
+	git_config *config;
+
+	cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus"));
+	cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+	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));
+
+	git_repository_config(&config, repo);
+
+	cl_git_pass(git_config_set_string(config, "submodule.my-submodule.url", git_repository_path(repo)));
+
+	git_config_free(config);
+
+	cl_git_pass(git_submodule_lookup(&submodule, repo, "my-submodule"));
+	cl_git_pass(git_submodule_update(submodule, 1, NULL));
+
+	git_buf_printf(&untracked_path, "%s/my-submodule/untracked", git_repository_workdir(repo));
+	fp = fopen(git_buf_cstr(&untracked_path), "w");
+	fprintf(fp, "An untracked file in a submodule should not block a rebase\n");
+	fclose(fp);
+	git_buf_free(&untracked_path);
+
+	cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+	git_submodule_free(submodule);
+	git_annotated_commit_free(branch_head);
+	git_annotated_commit_free(upstream_head);
+	git_reference_free(branch_ref);
+	git_reference_free(upstream_ref);
+	git_rebase_free(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/refs/branches/upstream.c b/tests/refs/branches/upstream.c
index 8f2e7a2..82f5665 100644
--- a/tests/refs/branches/upstream.c
+++ b/tests/refs/branches/upstream.c
@@ -175,7 +175,7 @@
 	cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/test"));
 	cl_git_pass(git_reference_create(&ref, repo, "refs/remotes/matching/master", git_reference_target(branch), 1, "fetch"));
 	cl_git_fail(git_branch_set_upstream(branch, "matching/master"));
-	cl_assert_equal_s("Could not determine remote for 'refs/remotes/matching/master'",
+	cl_assert_equal_s("could not determine remote for 'refs/remotes/matching/master'",
 			  giterr_last()->message);
 
 	/* we can't set it automatically, so let's test the user setting it by hand */
diff --git a/tests/refs/create.c b/tests/refs/create.c
index 6d5a5f1..4ecc605 100644
--- a/tests/refs/create.c
+++ b/tests/refs/create.c
@@ -12,19 +12,22 @@
 
 void test_refs_create__initialize(void)
 {
-   g_repo = cl_git_sandbox_init("testrepo");
+	g_repo = cl_git_sandbox_init("testrepo");
+	p_fsync__cnt = 0;
 }
 
 void test_refs_create__cleanup(void)
 {
-   cl_git_sandbox_cleanup();
+	cl_git_sandbox_cleanup();
 
 	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, 1));
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION, 0));
 }
 
 void test_refs_create__symbolic(void)
 {
-   // create a new symbolic reference
+	/* create a new symbolic reference */
 	git_reference *new_reference, *looked_up_ref, *resolved_ref;
 	git_repository *repo2;
 	git_oid id;
@@ -65,9 +68,57 @@
 	git_reference_free(resolved_ref);
 }
 
+void test_refs_create__symbolic_with_arbitrary_content(void)
+{
+	git_reference *new_reference, *looked_up_ref;
+	git_repository *repo2;
+	git_oid id;
+
+	const char *new_head_tracker = "ANOTHER_HEAD_TRACKER";
+	const char *arbitrary_target = "ARBITRARY DATA";
+
+	git_oid_fromstr(&id, current_master_tip);
+
+	/* Attempt to create symbolic ref with arbitrary data in target
+	 * fails by default
+	 */
+	cl_git_fail(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, arbitrary_target, 0, NULL));
+
+	git_libgit2_opts(GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, 0);
+
+	/* With strict target validation disabled, ref creation succeeds */
+	cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, arbitrary_target, 0, NULL));
+
+	/* Ensure the reference can be looked-up... */
+	cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker));
+	cl_assert(git_reference_type(looked_up_ref) & GIT_REF_SYMBOLIC);
+	cl_assert(reference_is_packed(looked_up_ref) == 0);
+	cl_assert_equal_s(looked_up_ref->name, new_head_tracker);
+	git_reference_free(looked_up_ref);
+
+	/* Ensure the target is what we expect it to be */
+	cl_assert_equal_s(git_reference_symbolic_target(new_reference), arbitrary_target);
+
+	/* Similar test with a fresh new repository object */
+	cl_git_pass(git_repository_open(&repo2, "testrepo"));
+
+	/* Ensure the reference can be looked-up... */
+	cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head_tracker));
+	cl_assert(git_reference_type(looked_up_ref) & GIT_REF_SYMBOLIC);
+	cl_assert(reference_is_packed(looked_up_ref) == 0);
+	cl_assert_equal_s(looked_up_ref->name, new_head_tracker);
+
+	/* Ensure the target is what we expect it to be */
+	cl_assert_equal_s(git_reference_symbolic_target(new_reference), arbitrary_target);
+
+	git_repository_free(repo2);
+	git_reference_free(new_reference);
+	git_reference_free(looked_up_ref);
+}
+
 void test_refs_create__deep_symbolic(void)
 {
-   // create a deep symbolic reference
+	/* create a deep symbolic reference */
 	git_reference *new_reference, *looked_up_ref, *resolved_ref;
 	git_oid id;
 
@@ -87,7 +138,7 @@
 
 void test_refs_create__oid(void)
 {
-   // create a new OID reference
+	/* create a new OID reference */
 	git_reference *new_reference, *looked_up_ref;
 	git_repository *repo2;
 	git_oid id;
@@ -248,3 +299,69 @@
 
 	test_win32_name("refs/heads/com1");
 }
+
+/* Creating a loose ref involves fsync'ing the reference, the
+ * reflog and (on non-Windows) the containing directories.
+ * Creating a packed ref involves fsync'ing the packed ref file
+ * and (on non-Windows) the containing directory.
+ */
+#ifdef GIT_WIN32
+static int expected_fsyncs_create = 2, expected_fsyncs_compress = 1;
+#else
+static int expected_fsyncs_create = 4, expected_fsyncs_compress = 2;
+#endif
+
+static void count_fsyncs(size_t *create_count, size_t *compress_count)
+{
+	git_reference *ref = NULL;
+	git_refdb *refdb;
+	git_oid id;
+
+	p_fsync__cnt = 0;
+
+	git_oid_fromstr(&id, current_master_tip);
+	cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/fsync_test", &id, 0, "log message"));
+	git_reference_free(ref);
+
+	*create_count = p_fsync__cnt;
+	p_fsync__cnt = 0;
+
+	cl_git_pass(git_repository_refdb(&refdb, g_repo));
+	cl_git_pass(git_refdb_compress(refdb));
+	git_refdb_free(refdb);
+
+	*compress_count = p_fsync__cnt;
+	p_fsync__cnt = 0;
+}
+
+void test_refs_create__does_not_fsync_by_default(void)
+{
+	size_t create_count, compress_count;
+	count_fsyncs(&create_count, &compress_count);
+
+	cl_assert_equal_i(0, create_count);
+	cl_assert_equal_i(0, compress_count);
+}
+
+void test_refs_create__fsyncs_when_global_opt_set(void)
+{
+	size_t create_count, compress_count;
+
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION, 1));
+	count_fsyncs(&create_count, &compress_count);
+
+	cl_assert_equal_i(expected_fsyncs_create, create_count);
+	cl_assert_equal_i(expected_fsyncs_compress, compress_count);
+}
+
+void test_refs_create__fsyncs_when_repo_config_set(void)
+{
+	size_t create_count, compress_count;
+
+	cl_repo_set_bool(g_repo, "core.fsyncObjectFiles", true);
+
+	count_fsyncs(&create_count, &compress_count);
+
+	cl_assert_equal_i(expected_fsyncs_create, create_count);
+	cl_assert_equal_i(expected_fsyncs_compress, compress_count);
+}
diff --git a/tests/refs/list.c b/tests/refs/list.c
index 374943b..f7ca3f7 100644
--- a/tests/refs/list.c
+++ b/tests/refs/list.c
@@ -36,7 +36,7 @@
 	/* We have exactly 12 refs in total if we include the packed ones:
 	 * there is a reference that exists both in the packfile and as
 	 * loose, but we only list it once */
-	cl_assert_equal_i((int)ref_list.count, 15);
+	cl_assert_equal_i((int)ref_list.count, 17);
 
 	git_strarray_free(&ref_list);
 }
@@ -51,7 +51,7 @@
 		"144344043ba4d4a405da03de3844aa829ae8be0e\n");
 
 	cl_git_pass(git_reference_list(&ref_list, g_repo));
-	cl_assert_equal_i((int)ref_list.count, 15);
+	cl_assert_equal_i((int)ref_list.count, 17);
 
 	git_strarray_free(&ref_list);
 }
diff --git a/tests/refs/namespaces.c b/tests/refs/namespaces.c
new file mode 100644
index 0000000..bb6bb1c
--- /dev/null
+++ b/tests/refs/namespaces.c
@@ -0,0 +1,36 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+static git_repository *g_repo;
+
+void test_refs_namespaces__initialize(void)
+{
+	g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_namespaces__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+void test_refs_namespaces__get_and_set(void)
+{
+	cl_assert_equal_s(NULL, git_repository_get_namespace(g_repo));
+
+	cl_git_pass(git_repository_set_namespace(g_repo, "namespace"));
+	cl_assert_equal_s("namespace", git_repository_get_namespace(g_repo));
+
+	cl_git_pass(git_repository_set_namespace(g_repo, NULL));
+	cl_assert_equal_s(NULL, git_repository_get_namespace(g_repo));
+}
+
+void test_refs_namespaces__namespace_doesnt_show_normal_refs(void)
+{
+	static git_strarray ref_list;
+
+	cl_git_pass(git_repository_set_namespace(g_repo, "namespace"));
+	cl_git_pass(git_reference_list(&ref_list, g_repo));
+	cl_assert_equal_i(0, ref_list.count);
+	git_strarray_free(&ref_list);
+}
diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c
index fdb1550..2522421 100644
--- a/tests/refs/reflog/reflog.c
+++ b/tests/refs/reflog/reflog.c
@@ -4,7 +4,7 @@
 #include "git2/reflog.h"
 #include "reflog.h"
 
-
+static const char *merge_reflog_message = "commit (merge): Merge commit";
 static const char *new_ref = "refs/heads/test-reflog";
 static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
 #define commit_msg "commit: bla bla"
@@ -261,7 +261,7 @@
 	error = giterr_last();
 
 	cl_assert(error != NULL);
-	cl_assert_equal_s("Unable to parse OID - contains invalid characters", error->message);
+	cl_assert_equal_s("unable to parse OID - contains invalid characters", error->message);
 
 	git_reference_free(ref);
 	git_buf_free(&logpath);
@@ -448,3 +448,45 @@
 
 	assert_no_reflog_update();
 }
+
+void test_refs_reflog_reflog__show_merge_for_merge_commits(void)
+{
+	git_oid b1_oid;
+	git_oid b2_oid;
+	git_oid merge_commit_oid;
+	git_commit *b1_commit;
+	git_commit *b2_commit;
+	git_signature *s;
+	git_commit *parent_commits[2];
+	git_tree *tree;
+	git_reflog *log;
+	const git_reflog_entry *entry;
+
+	cl_git_pass(git_signature_now(&s, "alice", "alice@example.com"));
+
+	cl_git_pass(git_reference_name_to_id(&b1_oid, g_repo, "HEAD"));
+	cl_git_pass(git_reference_name_to_id(&b2_oid, g_repo, "refs/heads/test"));
+
+	cl_git_pass(git_commit_lookup(&b1_commit, g_repo, &b1_oid));
+	cl_git_pass(git_commit_lookup(&b2_commit, g_repo, &b2_oid));
+
+	parent_commits[0] = b1_commit;
+	parent_commits[1] = b2_commit;
+
+	cl_git_pass(git_commit_tree(&tree, b1_commit));
+
+	cl_git_pass(git_commit_create(&merge_commit_oid,
+		g_repo, "HEAD", s, s, NULL,
+		"Merge commit", tree,
+		2, (const struct git_commit **) parent_commits));
+
+	cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
+	entry = git_reflog_entry_byindex(log, 0);
+	cl_assert_equal_s(merge_reflog_message, git_reflog_entry_message(entry));
+
+	git_reflog_free(log);
+	git_tree_free(tree);
+	git_commit_free(b1_commit);
+	git_commit_free(b2_commit);
+	git_signature_free(s);
+}
diff --git a/tests/repo/discover.c b/tests/repo/discover.c
index 358daee..abb7bd1 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,103 @@
 	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);
+}
+
+void test_repo_discover__discovery_starting_at_system_root_causes_no_hang(void)
+{
+#ifdef GIT_WIN32
+	git_buf out = GIT_BUF_INIT;
+	cl_git_fail(git_repository_discover(&out, "C:/", 0, NULL));
+	cl_git_fail(git_repository_discover(&out, "//localhost/", 0, NULL));
+#endif
+}
diff --git a/tests/repo/env.c b/tests/repo/env.c
index 5a89c0d..6404f88 100644
--- a/tests/repo/env.c
+++ b/tests/repo/env.c
@@ -56,8 +56,8 @@
 static void env_pass_(const char *path, const char *file, int line)
 {
 	git_repository *repo;
-	cl_git_pass_(git_repository_open_ext(NULL, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), file, line);
-	cl_git_pass_(git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), file, line);
+	cl_git_expect(git_repository_open_ext(NULL, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), 0, file, line);
+	cl_git_expect(git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), 0, file, line);
 	cl_assert_at_line(git__suffixcmp(git_repository_path(repo), "attr/.git/") == 0, file, line);
 	cl_assert_at_line(git__suffixcmp(git_repository_workdir(repo), "attr/") == 0, file, line);
 	cl_assert_at_line(!git_repository_is_bare(repo), file, line);
@@ -98,24 +98,24 @@
 	cl_git_pass(git_oid_fromstr(&oid_a, "45141a79a77842c59a63229403220a4e4be74e3d"));
 	cl_git_pass(git_oid_fromstr(&oid_t, "1385f264afb75a56a5bec74243be9b367ba4ca08"));
 	cl_git_pass(git_oid_fromstr(&oid_p, "0df1a5865c8abfc09f1f2182e6a31be550e99f07"));
-	cl_git_pass_(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL), file, line);
+	cl_git_expect(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL), 0, file, line);
 
 	if (a) {
-		cl_git_pass_(git_object_lookup(&object, repo, &oid_a, GIT_OBJ_BLOB), file, line);
+		cl_git_expect(git_object_lookup(&object, repo, &oid_a, GIT_OBJ_BLOB), 0, file, line);
 		git_object_free(object);
 	} else {
 		cl_git_fail_at_line(git_object_lookup(&object, repo, &oid_a, GIT_OBJ_BLOB), file, line);
 	}
 
 	if (t) {
-		cl_git_pass_(git_object_lookup(&object, repo, &oid_t, GIT_OBJ_BLOB), file, line);
+		cl_git_expect(git_object_lookup(&object, repo, &oid_t, GIT_OBJ_BLOB), 0, file, line);
 		git_object_free(object);
 	} else {
 		cl_git_fail_at_line(git_object_lookup(&object, repo, &oid_t, GIT_OBJ_BLOB), file, line);
 	}
 
 	if (p) {
-		cl_git_pass_(git_object_lookup(&object, repo, &oid_p, GIT_OBJ_COMMIT), file, line);
+		cl_git_expect(git_object_lookup(&object, repo, &oid_p, GIT_OBJ_COMMIT), 0, file, line);
 		git_object_free(object);
 	} else {
 		cl_git_fail_at_line(git_object_lookup(&object, repo, &oid_p, GIT_OBJ_COMMIT), file, line);
diff --git a/tests/resources/diff_format_email/.gitted/index b/tests/resources/diff_format_email/.gitted/index
index d94f87d..092a888 100644
--- a/tests/resources/diff_format_email/.gitted/index
+++ b/tests/resources/diff_format_email/.gitted/index
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/06/b7b69a62cbd1e53c6c4e0c3f16473dcfdb4af6 b/tests/resources/diff_format_email/.gitted/objects/06/b7b69a62cbd1e53c6c4e0c3f16473dcfdb4af6
new file mode 100644
index 0000000..37588f1
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/06/b7b69a62cbd1e53c6c4e0c3f16473dcfdb4af6
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/52/19b9784f9a92d7bd7cb567a6d6a21bfb86697e b/tests/resources/diff_format_email/.gitted/objects/52/19b9784f9a92d7bd7cb567a6d6a21bfb86697e
new file mode 100644
index 0000000..534e3b0
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/52/19b9784f9a92d7bd7cb567a6d6a21bfb86697e
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/53/525d4cc3ef3ba4a5cbf69492fdffb4e4a74558 b/tests/resources/diff_format_email/.gitted/objects/53/525d4cc3ef3ba4a5cbf69492fdffb4e4a74558
new file mode 100644
index 0000000..b74d31f
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/53/525d4cc3ef3ba4a5cbf69492fdffb4e4a74558
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/a7/a65f98355b5a7567bcc395f6f7936c9252cf57 b/tests/resources/diff_format_email/.gitted/objects/a7/a65f98355b5a7567bcc395f6f7936c9252cf57
new file mode 100644
index 0000000..36a4b7b
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/a7/a65f98355b5a7567bcc395f6f7936c9252cf57
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/c7/1a05d36025c806496a74d46d7d596eb23295c4 b/tests/resources/diff_format_email/.gitted/objects/c7/1a05d36025c806496a74d46d7d596eb23295c4
new file mode 100644
index 0000000..2822fe3
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/c7/1a05d36025c806496a74d46d7d596eb23295c4
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/d3/b6b38486f620b5b532a8cc6e0198ab7c3f52e4 b/tests/resources/diff_format_email/.gitted/objects/d3/b6b38486f620b5b532a8cc6e0198ab7c3f52e4
new file mode 100644
index 0000000..5b2e664
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/d3/b6b38486f620b5b532a8cc6e0198ab7c3f52e4
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/refs/heads/master b/tests/resources/diff_format_email/.gitted/refs/heads/master
index 3bc734d..4024b97 100644
--- a/tests/resources/diff_format_email/.gitted/refs/heads/master
+++ b/tests/resources/diff_format_email/.gitted/refs/heads/master
@@ -1 +1 @@
-627e7e12d87e07a83fad5b6bfa25e86ead4a5270
+5219b9784f9a92d7bd7cb567a6d6a21bfb86697e
diff --git a/tests/resources/diff_format_email/file3.txt b/tests/resources/diff_format_email/file3.txt
index 7309653..c71a05d 100644
--- a/tests/resources/diff_format_email/file3.txt
+++ b/tests/resources/diff_format_email/file3.txt
@@ -4,3 +4,4 @@
 file3
 file3
 file3
+file3
diff --git a/tests/resources/merge-resolve/.gitted/objects/2a/f2d9bcbc73723ac988bb202d4397f72a6ca7a0 b/tests/resources/merge-resolve/.gitted/objects/2a/f2d9bcbc73723ac988bb202d4397f72a6ca7a0
new file mode 100644
index 0000000..0d65823
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2a/f2d9bcbc73723ac988bb202d4397f72a6ca7a0
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/37/f53a5a14f64e91089a39ea58e71c87d81df765 b/tests/resources/merge-resolve/.gitted/objects/37/f53a5a14f64e91089a39ea58e71c87d81df765
new file mode 100644
index 0000000..95327ed
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/37/f53a5a14f64e91089a39ea58e71c87d81df765
@@ -0,0 +1 @@
+x•ŽAjC1D»ö)´ّe}%zƒ^Àߖã,þwprý:tVÃÀ›™Ô¶í6ÀÃÇèªÀRBB‡Lh—,‹ª·9„À+‹%r¹Ç®û€h1¨•è¥%£p’"Åæ”=¥ÙâIue£¶ßù{†ŸÚ¶ß¶ÃIgúrçëmÔÇú™Úö–„ÙÏ-œ23/‡þ“'|óæRã~U˜‹k{ªæ£dJï
\ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/4d/d1ef7569b18d92d93c0a35bb6b93049137b355 b/tests/resources/merge-resolve/.gitted/objects/4d/d1ef7569b18d92d93c0a35bb6b93049137b355
new file mode 100644
index 0000000..86a21ad
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4d/d1ef7569b18d92d93c0a35bb6b93049137b355
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/63/ec604d491161ddafdae4179843c26d54bd999a b/tests/resources/merge-resolve/.gitted/objects/63/ec604d491161ddafdae4179843c26d54bd999a
new file mode 100644
index 0000000..bc74da5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/63/ec604d491161ddafdae4179843c26d54bd999a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/68/f7c02064019d89e40e51d7776b6f67914420a2 b/tests/resources/merge-resolve/.gitted/objects/68/f7c02064019d89e40e51d7776b6f67914420a2
new file mode 100644
index 0000000..809a5b3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/68/f7c02064019d89e40e51d7776b6f67914420a2
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/94/29c05dd6f6f39fc567b4ce923b16df5d3d7a7a b/tests/resources/merge-resolve/.gitted/objects/94/29c05dd6f6f39fc567b4ce923b16df5d3d7a7a
new file mode 100644
index 0000000..d4d93f5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/94/29c05dd6f6f39fc567b4ce923b16df5d3d7a7a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a1/07e18a58f38c46086c8f8f1dcd54c40154eeb6 b/tests/resources/merge-resolve/.gitted/objects/a1/07e18a58f38c46086c8f8f1dcd54c40154eeb6
new file mode 100644
index 0000000..598c6a7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a1/07e18a58f38c46086c8f8f1dcd54c40154eeb6
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a2/d8d1824c68541cca94ffb90f79291eba495921 b/tests/resources/merge-resolve/.gitted/objects/a2/d8d1824c68541cca94ffb90f79291eba495921
new file mode 100644
index 0000000..2d3d947
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a2/d8d1824c68541cca94ffb90f79291eba495921
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/bc/85d1aad435ff3705a8c30ace85f7542c5736cb b/tests/resources/merge-resolve/.gitted/objects/bc/85d1aad435ff3705a8c30ace85f7542c5736cb
new file mode 100644
index 0000000..ae529fe
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/bc/85d1aad435ff3705a8c30ace85f7542c5736cb
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c8/26ef8b17b5cd2c4a0023f265f3a423b3aa0388 b/tests/resources/merge-resolve/.gitted/objects/c8/26ef8b17b5cd2c4a0023f265f3a423b3aa0388
new file mode 100644
index 0000000..b655d7c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c8/26ef8b17b5cd2c4a0023f265f3a423b3aa0388
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/cd/3e8d4aa06bdc781f264171030bc28f2b370fee b/tests/resources/merge-resolve/.gitted/objects/cd/3e8d4aa06bdc781f264171030bc28f2b370fee
new file mode 100644
index 0000000..144225d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cd/3e8d4aa06bdc781f264171030bc28f2b370fee
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e2/6b8888956137218d8589368a3e606cf50fbb56 b/tests/resources/merge-resolve/.gitted/objects/e2/6b8888956137218d8589368a3e606cf50fbb56
new file mode 100644
index 0000000..d4ec2b9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e2/6b8888956137218d8589368a3e606cf50fbb56
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/merge-resolve/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f9/7da95f156121bea8f978623628f4cbdbf30b36 b/tests/resources/merge-resolve/.gitted/objects/f9/7da95f156121bea8f978623628f4cbdbf30b36
new file mode 100644
index 0000000..d785511
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f9/7da95f156121bea8f978623628f4cbdbf30b36
@@ -0,0 +1 @@
+xŽ1!E­9½‰…)Œ±±´óÃ0¸[¬–×¯à¯^^ò’Ïu]—®§)zщ²%ÊÞA). dgˆ¡DðCt“zS“W×dM‹X²cÁb3gðì/’‚¢½Ïµé[þPËú1×u«/}–at}.}ÞӉëzÑÖc€Æ¡>š15ìxÙåß^ÝiµúêòHø
\ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename1 b/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename1
new file mode 100644
index 0000000..0ed914f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename1
@@ -0,0 +1 @@
+f97da95f156121bea8f978623628f4cbdbf30b36
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename2 b/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename2
new file mode 100644
index 0000000..8e020cc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename2
@@ -0,0 +1 @@
+37f53a5a14f64e91089a39ea58e71c87d81df765
diff --git a/tests/resources/rebase-submodule/.gitmodules b/tests/resources/rebase-submodule/.gitmodules
new file mode 100644
index 0000000..f36de77
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "my-submodule"]
+	path = my-submodule
+	url = bogus
diff --git a/tests/resources/rebase-submodule/.gitted/HEAD b/tests/resources/rebase-submodule/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/rebase-submodule/.gitted/ORIG_HEAD b/tests/resources/rebase-submodule/.gitted/ORIG_HEAD
new file mode 100644
index 0000000..a1ccbb4
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/ORIG_HEAD
@@ -0,0 +1 @@
+5fdf086684daae0a8bc61a81afe178edc1e556e7
diff --git a/tests/resources/rebase-submodule/.gitted/config b/tests/resources/rebase-submodule/.gitted/config
new file mode 100644
index 0000000..af2095f
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/config
@@ -0,0 +1,9 @@
+[core]
+	repositoryformatversion = 0
+	filemode = true
+	bare = false
+	logallrefupdates = true
+[branch "asparagus"]
+	rebase = true
+[branch "master"]
+	rebase = true
diff --git a/tests/resources/rebase-submodule/.gitted/description b/tests/resources/rebase-submodule/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/rebase-submodule/.gitted/index b/tests/resources/rebase-submodule/.gitted/index
new file mode 100644
index 0000000..b63efab
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/index
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/info/exclude b/tests/resources/rebase-submodule/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/rebase-submodule/.gitted/info/refs b/tests/resources/rebase-submodule/.gitted/info/refs
new file mode 100644
index 0000000..230a649
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/info/refs
@@ -0,0 +1,4 @@
+c64ea52df5b31efd7b73769418dc9e25b8803d25	refs/heads/asparagus
+c64ea52df5b31efd7b73769418dc9e25b8803d25	refs/remotes/origin/HEAD
+c64ea52df5b31efd7b73769418dc9e25b8803d25	refs/remotes/origin/asparagus
+02a35db3f24db554b757b3009bc782784267c743	refs/remotes/origin/master
diff --git a/tests/resources/rebase-submodule/.gitted/objects/01/971e2453a407e4b9f6c865e2c37f4db21da294 b/tests/resources/rebase-submodule/.gitted/objects/01/971e2453a407e4b9f6c865e2c37f4db21da294
new file mode 100644
index 0000000..308b386
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/01/971e2453a407e4b9f6c865e2c37f4db21da294
@@ -0,0 +1 @@
+xUÎAjÃ0ЮuŠ¹@ÂX–fd%¡4Rjˆ¬"ËÉõë–tÑÕ|>ÿÁÄZÊÜAã[o)G2R1ä ­ qbthçˆ&Bԗoié?ûM»<Èà"Z¢Ì¨EÇÀL¦èÄdå·þY\ýcøØڒœ¤ÿ†sÖu¾Œµ¼Ã`œ³dˆhÕÞîOö]üçK}øû¼ž_÷XÛíO;Ãú¥ÕE<¬[(U¶{Rß1ØJ
\ No newline at end of file
diff --git a/tests/resources/rebase-submodule/.gitted/objects/17/f8ae8ebdd08a4bb272f61b897b308ad42b1b12 b/tests/resources/rebase-submodule/.gitted/objects/17/f8ae8ebdd08a4bb272f61b897b308ad42b1b12
new file mode 100644
index 0000000..79e4c48
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/17/f8ae8ebdd08a4bb272f61b897b308ad42b1b12
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911 b/tests/resources/rebase-submodule/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911
new file mode 100644
index 0000000..99b5e6d
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a b/tests/resources/rebase-submodule/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a
new file mode 100644
index 0000000..0163985
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5 b/tests/resources/rebase-submodule/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5
new file mode 100644
index 0000000..d4776d8
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598 b/tests/resources/rebase-submodule/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598
new file mode 100644
index 0000000..6aaf79f
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096 b/tests/resources/rebase-submodule/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096
new file mode 100644
index 0000000..ed1de3a
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/7c/71f7606bd3bfb25d063c970804e7fc00b9605b b/tests/resources/rebase-submodule/.gitted/objects/7c/71f7606bd3bfb25d063c970804e7fc00b9605b
new file mode 100644
index 0000000..ef923e7
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/7c/71f7606bd3bfb25d063c970804e7fc00b9605b
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f b/tests/resources/rebase-submodule/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f
new file mode 100644
index 0000000..fe8b157
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20 b/tests/resources/rebase-submodule/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20
new file mode 100644
index 0000000..54f9b66
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/ab/6cf22b4c67a274aa8d31b5877d92341e8c2a9c b/tests/resources/rebase-submodule/.gitted/objects/ab/6cf22b4c67a274aa8d31b5877d92341e8c2a9c
new file mode 100644
index 0000000..5acdf36
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/ab/6cf22b4c67a274aa8d31b5877d92341e8c2a9c
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f b/tests/resources/rebase-submodule/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f
new file mode 100644
index 0000000..2bbf28f
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f
@@ -0,0 +1 @@
+x%P1nÄ0ëìWð¹CNE§N7¶è¡:*‰’p¬Ô’/¸ßWÎmI‘”ú$=^^ŸŸ._ï?¿¸~|žC¸°ã¼6©yTȄA¨(#1eôÌÓ鴓.ˆ†áÀ(Hto@̸K-aë°Õ°…¡´²“sá1r6)&)8¸Å·TêÖa¶<0ׇ¿JÙ¢Ý[‡ŒK‡5IJ²²­ÈÀªcáÁ¸q͓쌫r_Íۇ"u^@ÐÈ7~X)—›÷2¸ Ýâ	G…,æ¥f¬R¸ùå`BÚúÂ4¶3£½ÁvQŸø¤›HÖ©¦Öu­êa²b SwÞcJ q…)fƔèæO‚ùvû×;‡í«ŒŸ
\ No newline at end of file
diff --git a/tests/resources/rebase-submodule/.gitted/objects/f3/6de77de6f53dddafeb024ecaf375e45c3d9ddd b/tests/resources/rebase-submodule/.gitted/objects/f3/6de77de6f53dddafeb024ecaf375e45c3d9ddd
new file mode 100644
index 0000000..cd330a7
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/f3/6de77de6f53dddafeb024ecaf375e45c3d9ddd
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d b/tests/resources/rebase-submodule/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d
new file mode 100644
index 0000000..f655d12
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/packed-refs b/tests/resources/rebase-submodule/.gitted/packed-refs
new file mode 100644
index 0000000..44ac842
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled 
+c64ea52df5b31efd7b73769418dc9e25b8803d25 refs/heads/asparagus
diff --git a/tests/resources/rebase-submodule/.gitted/refs/heads/asparagus b/tests/resources/rebase-submodule/.gitted/refs/heads/asparagus
new file mode 100644
index 0000000..e6adde8
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/refs/heads/asparagus
@@ -0,0 +1 @@
+17f8ae8ebdd08a4bb272f61b897b308ad42b1b12
diff --git a/tests/resources/rebase-submodule/.gitted/refs/heads/master b/tests/resources/rebase-submodule/.gitted/refs/heads/master
new file mode 100644
index 0000000..2523d49
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/refs/heads/master
@@ -0,0 +1 @@
+01971e2453a407e4b9f6c865e2c37f4db21da294
diff --git a/tests/resources/rebase-submodule/asparagus.txt b/tests/resources/rebase-submodule/asparagus.txt
new file mode 100644
index 0000000..ffb36e5
--- /dev/null
+++ b/tests/resources/rebase-submodule/asparagus.txt
@@ -0,0 +1,10 @@
+ASPARAGUS SOUP.
+
+Take four large bunches of asparagus, scrape it nicely, cut off one inch
+of the tops, and lay them in water, chop the stalks and put them on the
+fire with a piece of bacon, a large onion cut up, and pepper and salt;
+add two quarts of water, boil them till the stalks are quite soft, then
+pulp them through a sieve, and strain the water to it, which must be put
+back in the pot; put into it a chicken cut up, with the tops of
+asparagus which had been laid by, boil it until these last articles are
+sufficiently done, thicken with flour, butter and milk, and serve it up.
diff --git a/tests/resources/rebase-submodule/beef.txt b/tests/resources/rebase-submodule/beef.txt
new file mode 100644
index 0000000..68f6182
--- /dev/null
+++ b/tests/resources/rebase-submodule/beef.txt
@@ -0,0 +1,22 @@
+BEEF SOUP.
+
+Take the hind shin of beef, cut off all the flesh off the leg-bone,
+which must be taken away entirely, or the soup will be greasy. Wash the
+meat clean and lay it in a pot, sprinkle over it one small
+table-spoonful of pounded black pepper, and two of salt; three onions
+the size of a hen's egg, cut small, six small carrots scraped and cut
+up, two small turnips pared and cut into dice; pour on three quarts of
+water, cover the pot close, and keep it gently and steadily boiling five
+hours, which will leave about three pints of clear soup; do not let the
+pot boil over, but take off the scum carefully, as it rises. When it has
+boiled four hours, put in a small bundle of thyme and parsley, and a
+pint of celery cut small, or a tea-spoonful of celery seed pounded.
+These latter ingredients would lose their delicate flavour if boiled too
+much. Just before you take it up, brown it in the following manner: put
+a small table-spoonful of nice brown sugar into an iron skillet, set it
+on the fire and stir it till it melts and looks very dark, pour into it
+a ladle full of the soup, a little at a time; stirring it all the while.
+Strain this browning and mix it well with the soup; take out the bundle
+of thyme and parsley, put the nicest pieces of meat in your tureen, and
+pour on the soup and vegetables; put in some toasted bread cut in dice,
+and serve it up.
diff --git a/tests/resources/rebase-submodule/bouilli.txt b/tests/resources/rebase-submodule/bouilli.txt
new file mode 100644
index 0000000..4b7c565
--- /dev/null
+++ b/tests/resources/rebase-submodule/bouilli.txt
@@ -0,0 +1,18 @@
+SOUP WITH BOUILLI.
+
+Take the nicest part of the thick brisket of beef, about eight pounds,
+put it into a pot with every thing directed for the other soup; make it
+exactly in the same way, only put it on an hour sooner, that you may
+have time to prepare the bouilli; after it has boiled five hours, take
+out the beef, cover up the soup and set it near the fire that it may
+keep hot. Take the skin off the beef, have the yelk of an egg well
+beaten, dip a feather in it and wash the top of your beef, sprinkle over
+it the crumb of stale bread finely grated, put it in a Dutch oven
+previously heated, put the top on with coals enough to brown, but not
+burn the beef; let it stand nearly an hour, and prepare your gravy
+thus:--Take a sufficient quantity of soup and the vegetables boiled in
+it; add to it a table-spoonful of red wine, and two of mushroom catsup,
+thicken with a little bit of butter and a little brown flour; make it
+very hot, pour it in your dish, and put the beef on it. Garnish it with
+green pickle, cut in thin slices, serve up the soup in a tureen with
+bits of toasted bread.
diff --git a/tests/resources/rebase-submodule/gravy.txt b/tests/resources/rebase-submodule/gravy.txt
new file mode 100644
index 0000000..c4e6cca
--- /dev/null
+++ b/tests/resources/rebase-submodule/gravy.txt
@@ -0,0 +1,8 @@
+GRAVY SOUP.
+
+Get eight pounds of coarse lean beef--wash it clean and lay it in your
+pot, put in the same ingredients as for the shin soup, with the same
+quantity of water, and follow the process directed for that. Strain the
+soup through a sieve, and serve it up clear, with nothing more than
+toasted bread in it; two table-spoonsful of mushroom catsup will add a
+fine flavour to the soup.
diff --git a/tests/resources/rebase-submodule/my-submodule b/tests/resources/rebase-submodule/my-submodule
new file mode 160000
index 0000000..efad0b1
--- /dev/null
+++ b/tests/resources/rebase-submodule/my-submodule
@@ -0,0 +1 @@
+Subproject commit efad0b11c47cb2f0220cbd6f5b0f93bb99064b00
diff --git a/tests/resources/rebase-submodule/oyster.txt b/tests/resources/rebase-submodule/oyster.txt
new file mode 100644
index 0000000..68af1fc
--- /dev/null
+++ b/tests/resources/rebase-submodule/oyster.txt
@@ -0,0 +1,13 @@
+OYSTER SOUP.
+
+Wash and drain two quarts of oysters, put them on with three quarts of
+water, three onions chopped up, two or three slices of lean ham, pepper
+and salt; boil it till reduced one-half, strain it through a sieve,
+return the liquid into the pot, put in one quart of fresh oysters, boil
+it till they are sufficiently done, and thicken the soup with four
+spoonsful of flour, two gills of rich cream, and the yelks of six new
+laid eggs beaten well; boil it a few minutes after the thickening is put
+in. Take care that it does not curdle, and that the flour is not in
+lumps; serve it up with the last oysters that were put in. If the
+flavour of thyme be agreeable, you may put in a little, but take care
+that it does not boil in it long enough to discolour the soup.
diff --git a/tests/resources/rebase-submodule/veal.txt b/tests/resources/rebase-submodule/veal.txt
new file mode 100644
index 0000000..a7b0665
--- /dev/null
+++ b/tests/resources/rebase-submodule/veal.txt
@@ -0,0 +1,18 @@
+VEAL SOUP.
+
+Put into a pot three quarts of water, three onions cut small, one
+spoonful of black pepper pounded, and two of salt, with two or three
+slices of lean ham; let it boil steadily two hours; skim it
+occasionally, then put into it a shin of veal, let it boil two hours
+longer; take out the slices of ham, and skim off the grease if any
+should rise, take a gill of good cream, mix with it two table-spoonsful
+of flour very nicely, and the yelks of two eggs beaten well, strain this
+mixture, and add some chopped parsley; pour some soup on by degrees,
+stir it well, and pour it into the pot, continuing to stir until it has
+boiled two or three minutes to take off the raw taste of the eggs. If
+the cream be not perfectly sweet, and the eggs quite new, the thickening
+will curdle in the soup. For a change you may put a dozen ripe tomatos
+in, first taking off their skins, by letting them stand a few minutes in
+hot water, when they may be easily peeled. When made in this way you
+must thicken it with the flour only. Any part of the veal may be used,
+but the shin or knuckle is the nicest.
diff --git a/tests/resources/sha1/hello_c b/tests/resources/sha1/hello_c
new file mode 100644
index 0000000..45950b2
--- /dev/null
+++ b/tests/resources/sha1/hello_c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+	printf("Hello, %s\n", "world");
+}
diff --git a/tests/resources/sha1/shattered-1.pdf b/tests/resources/sha1/shattered-1.pdf
new file mode 100644
index 0000000..ba9aaa1
--- /dev/null
+++ b/tests/resources/sha1/shattered-1.pdf
Binary files differ
diff --git a/tests/resources/submodules-worktree-child/.gitted b/tests/resources/submodules-worktree-child/.gitted
new file mode 100644
index 0000000..03286f5
--- /dev/null
+++ b/tests/resources/submodules-worktree-child/.gitted
@@ -0,0 +1 @@
+gitdir: ../submodules/testrepo/.git/worktrees/submodules-worktree-child
diff --git a/tests/resources/submodules-worktree-child/README b/tests/resources/submodules-worktree-child/README
new file mode 100644
index 0000000..a823312
--- /dev/null
+++ b/tests/resources/submodules-worktree-child/README
@@ -0,0 +1 @@
+hey there
diff --git a/tests/resources/submodules-worktree-child/branch_file.txt b/tests/resources/submodules-worktree-child/branch_file.txt
new file mode 100644
index 0000000..3697d64
--- /dev/null
+++ b/tests/resources/submodules-worktree-child/branch_file.txt
@@ -0,0 +1,2 @@
+hi
+bye!
diff --git a/tests/resources/submodules-worktree-child/new.txt b/tests/resources/submodules-worktree-child/new.txt
new file mode 100644
index 0000000..a71586c
--- /dev/null
+++ b/tests/resources/submodules-worktree-child/new.txt
@@ -0,0 +1 @@
+my new file
diff --git a/tests/resources/submodules-worktree-parent/.gitmodules b/tests/resources/submodules-worktree-parent/.gitmodules
new file mode 100644
index 0000000..78308c9
--- /dev/null
+++ b/tests/resources/submodules-worktree-parent/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "testrepo"]
+	path = testrepo
+	url = /Users/rb/src/libgit2/tests/resources/testrepo.git
diff --git a/tests/resources/submodules-worktree-parent/.gitted b/tests/resources/submodules-worktree-parent/.gitted
new file mode 100644
index 0000000..87bd9ae
--- /dev/null
+++ b/tests/resources/submodules-worktree-parent/.gitted
@@ -0,0 +1 @@
+gitdir: ../submodules/.git/worktrees/submodules-worktree-parent
diff --git a/tests/resources/submodules-worktree-parent/deleted b/tests/resources/submodules-worktree-parent/deleted
new file mode 100644
index 0000000..092bfb9
--- /dev/null
+++ b/tests/resources/submodules-worktree-parent/deleted
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/submodules-worktree-parent/modified b/tests/resources/submodules-worktree-parent/modified
new file mode 100644
index 0000000..092bfb9
--- /dev/null
+++ b/tests/resources/submodules-worktree-parent/modified
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/submodules-worktree-parent/unmodified b/tests/resources/submodules-worktree-parent/unmodified
new file mode 100644
index 0000000..092bfb9
--- /dev/null
+++ b/tests/resources/submodules-worktree-parent/unmodified
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/submodules/.gitted/logs/refs/heads/submodules-worktree-parent b/tests/resources/submodules/.gitted/logs/refs/heads/submodules-worktree-parent
new file mode 100644
index 0000000..65e9885
--- /dev/null
+++ b/tests/resources/submodules/.gitted/logs/refs/heads/submodules-worktree-parent
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 97896810b3210244a62a82458b8e0819ecfc6850 Patrick Steinhardt <ps@pks.im> 1447084240 +0100	branch: Created from HEAD
diff --git a/tests/resources/submodules/.gitted/refs/heads/submodules-worktree-parent b/tests/resources/submodules/.gitted/refs/heads/submodules-worktree-parent
new file mode 100644
index 0000000..32b9358
--- /dev/null
+++ b/tests/resources/submodules/.gitted/refs/heads/submodules-worktree-parent
@@ -0,0 +1 @@
+97896810b3210244a62a82458b8e0819ecfc6850
diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/HEAD b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/HEAD
new file mode 100644
index 0000000..a07134b
--- /dev/null
+++ b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/submodules-worktree-parent
diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/ORIG_HEAD b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/ORIG_HEAD
new file mode 100644
index 0000000..32b9358
--- /dev/null
+++ b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/ORIG_HEAD
@@ -0,0 +1 @@
+97896810b3210244a62a82458b8e0819ecfc6850
diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/commondir b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/commondir
new file mode 100644
index 0000000..aab0408
--- /dev/null
+++ b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/commondir
@@ -0,0 +1 @@
+../..
diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/gitdir b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/gitdir
new file mode 100644
index 0000000..eaaf13b
--- /dev/null
+++ b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/gitdir
@@ -0,0 +1 @@
+../../../../submodules-worktree-parent/.git
diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/index b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/index
new file mode 100644
index 0000000..5b68f18
--- /dev/null
+++ b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/index
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/config b/tests/resources/submodules/testrepo/.gitted/config
index d6dcad1..8e55711 100644
--- a/tests/resources/submodules/testrepo/.gitted/config
+++ b/tests/resources/submodules/testrepo/.gitted/config
@@ -4,9 +4,6 @@
 	bare = false
 	logallrefupdates = true
 	ignorecase = true
-[remote "origin"]
-	fetch = +refs/heads/*:refs/remotes/origin/*
-	url = /Users/rb/src/libgit2/tests/resources/testrepo.git
 [branch "master"]
 	remote = origin
 	merge = refs/heads/master
diff --git a/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/submodules-worktree-child b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/submodules-worktree-child
new file mode 100644
index 0000000..dd4650f
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/submodules-worktree-child
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Patrick Steinhardt <ps@pks.im> 1447084252 +0100	branch: Created from HEAD
diff --git a/tests/resources/submodules/testrepo/.gitted/refs/heads/submodules-worktree-child b/tests/resources/submodules/testrepo/.gitted/refs/heads/submodules-worktree-child
new file mode 100644
index 0000000..3d8f0a4
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/refs/heads/submodules-worktree-child
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/HEAD b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/HEAD
new file mode 100644
index 0000000..ef82bd4
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/submodules-worktree-child
diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/ORIG_HEAD b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/ORIG_HEAD
new file mode 100644
index 0000000..3d8f0a4
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/ORIG_HEAD
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/commondir b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/commondir
new file mode 100644
index 0000000..aab0408
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/commondir
@@ -0,0 +1 @@
+../..
diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/gitdir b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/gitdir
new file mode 100644
index 0000000..b0ef96e
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/gitdir
@@ -0,0 +1 @@
+../../../../../submodules-worktree-child/.git
diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/index b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/index
new file mode 100644
index 0000000..52a42f9
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/index
Binary files differ
diff --git a/tests/resources/testrepo-worktree/.gitted b/tests/resources/testrepo-worktree/.gitted
new file mode 100644
index 0000000..fe4556a
--- /dev/null
+++ b/tests/resources/testrepo-worktree/.gitted
@@ -0,0 +1 @@
+gitdir: ../testrepo/.git/worktrees/testrepo-worktree
diff --git a/tests/resources/testrepo-worktree/README b/tests/resources/testrepo-worktree/README
new file mode 100644
index 0000000..a823312
--- /dev/null
+++ b/tests/resources/testrepo-worktree/README
@@ -0,0 +1 @@
+hey there
diff --git a/tests/resources/testrepo-worktree/branch_file.txt b/tests/resources/testrepo-worktree/branch_file.txt
new file mode 100644
index 0000000..3697d64
--- /dev/null
+++ b/tests/resources/testrepo-worktree/branch_file.txt
@@ -0,0 +1,2 @@
+hi
+bye!
diff --git a/tests/resources/testrepo-worktree/link_to_new.txt b/tests/resources/testrepo-worktree/link_to_new.txt
new file mode 120000
index 0000000..c0528fd
--- /dev/null
+++ b/tests/resources/testrepo-worktree/link_to_new.txt
@@ -0,0 +1 @@
+new.txt
\ No newline at end of file
diff --git a/tests/resources/testrepo-worktree/new.txt b/tests/resources/testrepo-worktree/new.txt
new file mode 100644
index 0000000..a71586c
--- /dev/null
+++ b/tests/resources/testrepo-worktree/new.txt
@@ -0,0 +1 @@
+my new file
diff --git a/tests/resources/testrepo/.gitted/logs/refs/heads/testrepo-worktree b/tests/resources/testrepo/.gitted/logs/refs/heads/testrepo-worktree
new file mode 100644
index 0000000..93ab5f0
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/logs/refs/heads/testrepo-worktree
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 099fabac3a9ea935598528c27f866e34089c2eff Patrick Steinhardt <ps@pks.im> 1442484463 +0200	branch: Created from HEAD
diff --git a/tests/resources/testrepo/.gitted/objects/9b/1719f5cf069568785080a0bbabbe7c377e22ae b/tests/resources/testrepo/.gitted/objects/9b/1719f5cf069568785080a0bbabbe7c377e22ae
new file mode 100644
index 0000000..13e3f58
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/9b/1719f5cf069568785080a0bbabbe7c377e22ae
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/a3/8d028f71eaa590febb7d716b1ca32350cf70da b/tests/resources/testrepo/.gitted/objects/a3/8d028f71eaa590febb7d716b1ca32350cf70da
new file mode 100644
index 0000000..4df22ec
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/a3/8d028f71eaa590febb7d716b1ca32350cf70da
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/ad/edac69457183c8265c8a9614c1c4fed31d1ff3 b/tests/resources/testrepo/.gitted/objects/ad/edac69457183c8265c8a9614c1c4fed31d1ff3
new file mode 100644
index 0000000..c054fc0
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/ad/edac69457183c8265c8a9614c1c4fed31d1ff3
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/refs/heads/merge-conflict b/tests/resources/testrepo/.gitted/refs/heads/merge-conflict
new file mode 100644
index 0000000..3e24a24
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/merge-conflict
@@ -0,0 +1 @@
+a38d028f71eaa590febb7d716b1ca32350cf70da
diff --git a/tests/resources/testrepo/.gitted/refs/heads/testrepo-worktree b/tests/resources/testrepo/.gitted/refs/heads/testrepo-worktree
new file mode 100644
index 0000000..f31fe78
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/testrepo-worktree
@@ -0,0 +1 @@
+099fabac3a9ea935598528c27f866e34089c2eff
diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/HEAD b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/HEAD
new file mode 100644
index 0000000..1b8637e
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/testrepo-worktree
diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/commondir b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/commondir
new file mode 100644
index 0000000..aab0408
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/commondir
@@ -0,0 +1 @@
+../..
diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/gitdir b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/gitdir
new file mode 100644
index 0000000..0d37a57
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/gitdir
@@ -0,0 +1 @@
+../../../../testrepo-worktree/.git
diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/index b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/index
new file mode 100644
index 0000000..4114190
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/index
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/logs/HEAD b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/logs/HEAD
new file mode 100644
index 0000000..3bede50
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/logs/HEAD
@@ -0,0 +1 @@
+099fabac3a9ea935598528c27f866e34089c2eff 099fabac3a9ea935598528c27f866e34089c2eff Patrick Steinhardt <ps@pks.im> 1442484463 +0200	checkout: moving from 099fabac3a9ea935598528c27f866e34089c2eff to testrepo-worktree
diff --git a/tests/revwalk/basic.c b/tests/revwalk/basic.c
index 89140bc..547050c 100644
--- a/tests/revwalk/basic.c
+++ b/tests/revwalk/basic.c
@@ -177,7 +177,7 @@
 		/* walking */;
 
 	/* git log --branches --oneline | wc -l => 16 */
-	cl_assert_equal_i(18, i);
+	cl_assert_equal_i(19, i);
 }
 
 void test_revwalk_basic__push_head(void)
@@ -331,6 +331,40 @@
 	cl_assert_equal_i(i, 0);
 }
 
+void test_revwalk_basic__topo_crash(void)
+{
+	git_oid oid;
+	git_oid_fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+
+	revwalk_basic_setup_walk(NULL);
+	git_revwalk_sorting(_walk, GIT_SORT_TOPOLOGICAL);
+
+	cl_git_pass(git_revwalk_push(_walk, &oid));
+	cl_git_pass(git_revwalk_hide(_walk, &oid));
+
+	git_revwalk_next(&oid, _walk);
+}
+
+void test_revwalk_basic__from_new_to_old(void)
+{
+	git_oid from_oid, to_oid, oid;
+	int i = 0;
+
+	revwalk_basic_setup_walk(NULL);
+	git_revwalk_sorting(_walk, GIT_SORT_TIME);
+
+	cl_git_pass(git_oid_fromstr(&to_oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644"));
+	cl_git_pass(git_oid_fromstr(&from_oid, "a4a7dce85cf63874e984719f4fdd239f5145052f"));
+
+	cl_git_pass(git_revwalk_push(_walk, &to_oid));
+	cl_git_pass(git_revwalk_hide(_walk, &from_oid));
+
+	while (git_revwalk_next(&oid, _walk) == 0)
+		i++;
+
+	cl_assert_equal_i(i, 0);
+}
+
 void test_revwalk_basic__push_range(void)
 {
 	revwalk_basic_setup_walk(NULL);
diff --git a/tests/status/ignore.c b/tests/status/ignore.c
index c4878b2..251de39 100644
--- a/tests/status/ignore.c
+++ b/tests/status/ignore.c
@@ -20,8 +20,8 @@
 	bool expected, const char *filepath, const char *file, int line)
 {
 	int is_ignored = 0;
-	cl_git_pass_(
-		git_status_should_ignore(&is_ignored, g_repo, filepath), file, line);
+	cl_git_expect(
+		git_status_should_ignore(&is_ignored, g_repo, filepath), 0, file, line);
 	clar__assert(
 		(expected != 0) == (is_ignored != 0),
 		file, line, "expected != is_ignored", filepath, 1);
diff --git a/tests/submodule/lookup.c b/tests/submodule/lookup.c
index 148f927..e36fc44 100644
--- a/tests/submodule/lookup.c
+++ b/tests/submodule/lookup.c
@@ -388,3 +388,28 @@
 	cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
 	cl_assert_equal_i(8, data.count);
 }
+
+void test_submodule_lookup_cached(void) {
+	git_submodule *sm;
+	git_submodule *sm2;
+	/* See that the simple tests still pass. */
+
+	git_repository_submodule_cache_all(g_repo);
+	test_submodule_lookup__simple_lookup();
+	git_repository_submodule_cache_clear(g_repo);
+	test_submodule_lookup__simple_lookup();
+
+	/* Check that subsequent calls return different objects when cached. */
+	git_repository_submodule_cache_all(g_repo);
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+	cl_git_pass(git_submodule_lookup(&sm2, g_repo, "sm_unchanged"));
+	cl_assert_equal_p(sm, sm2);
+	git_submodule_free(sm2);
+
+	/* and that we get new objects again after clearing the cache. */
+	git_repository_submodule_cache_clear(g_repo);
+	cl_git_pass(git_submodule_lookup(&sm2, g_repo, "sm_unchanged"));
+	cl_assert(sm != sm2);
+	git_submodule_free(sm);
+	git_submodule_free(sm2);
+}
diff --git a/tests/submodule/submodule_helpers.c b/tests/submodule/submodule_helpers.c
index 6c2b9cf..cd541ea 100644
--- a/tests/submodule/submodule_helpers.c
+++ b/tests/submodule/submodule_helpers.c
@@ -204,7 +204,7 @@
 	git_submodule *sm;
 	int error = git_submodule_lookup(&sm, repo, name);
 	if (error)
-		cl_git_report_failure(error, file, line, msg);
+		cl_git_report_failure(error, 0, file, line, msg);
 	cl_assert_at_line(sm != NULL, file, line);
 	git_submodule_free(sm);
 }
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
 }
diff --git a/tests/worktree/config.c b/tests/worktree/config.c
new file mode 100644
index 0000000..3ab317b
--- /dev/null
+++ b/tests/worktree/config.c
@@ -0,0 +1,45 @@
+#include "clar_libgit2.h"
+#include "worktree_helpers.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+static worktree_fixture fixture =
+	WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+void test_worktree_config__initialize(void)
+{
+	setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_config__cleanup(void)
+{
+	cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_config__open(void)
+{
+	git_config *cfg;
+
+	cl_git_pass(git_repository_config(&cfg, fixture.worktree));
+	cl_assert(cfg != NULL);
+
+	git_config_free(cfg);
+}
+
+void test_worktree_config__set(void)
+{
+	git_config *cfg;
+	int32_t val;
+
+	cl_git_pass(git_repository_config(&cfg, fixture.worktree));
+	cl_git_pass(git_config_set_int32(cfg, "core.dummy", 5));
+	git_config_free(cfg);
+
+	// reopen to verify configuration has been set in the
+	// common dir
+	cl_git_pass(git_repository_config(&cfg, fixture.repo));
+	cl_git_pass(git_config_get_int32(&val, cfg, "core.dummy"));
+	cl_assert_equal_i(val, 5);
+	git_config_free(cfg);
+}
diff --git a/tests/worktree/merge.c b/tests/worktree/merge.c
new file mode 100644
index 0000000..36cc2a6
--- /dev/null
+++ b/tests/worktree/merge.c
@@ -0,0 +1,121 @@
+#include "clar_libgit2.h"
+
+#include "worktree_helpers.h"
+#include "merge/merge_helpers.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+#define MASTER_BRANCH "refs/heads/master"
+#define CONFLICT_BRANCH "refs/heads/merge-conflict"
+
+#define CONFLICT_BRANCH_FILE_TXT \
+	"<<<<<<< HEAD\n" \
+	"hi\n" \
+	"bye!\n" \
+	"=======\n" \
+	"conflict\n" \
+	">>>>>>> merge-conflict\n" \
+
+static worktree_fixture fixture =
+	WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+static const char *merge_files[] = {
+	GIT_MERGE_HEAD_FILE,
+	GIT_ORIG_HEAD_FILE,
+	GIT_MERGE_MODE_FILE,
+	GIT_MERGE_MSG_FILE,
+};
+
+void test_worktree_merge__initialize(void)
+{
+	setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_merge__cleanup(void)
+{
+	cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_merge__merge_head(void)
+{
+	git_reference *theirs_ref, *ref;
+	git_annotated_commit *theirs;
+
+	cl_git_pass(git_reference_lookup(&theirs_ref, fixture.worktree, CONFLICT_BRANCH));
+	cl_git_pass(git_annotated_commit_from_ref(&theirs, fixture.worktree, theirs_ref));
+	cl_git_pass(git_merge(fixture.worktree, (const git_annotated_commit **)&theirs, 1, NULL, NULL));
+
+	cl_git_pass(git_reference_lookup(&ref, fixture.worktree, GIT_MERGE_HEAD_FILE));
+
+	git_reference_free(ref);
+	git_reference_free(theirs_ref);
+	git_annotated_commit_free(theirs);
+}
+
+void test_worktree_merge__merge_setup(void)
+{
+	git_reference *ours_ref, *theirs_ref;
+	git_annotated_commit *ours, *theirs;
+	git_buf path = GIT_BUF_INIT;
+	unsigned i;
+
+	cl_git_pass(git_reference_lookup(&ours_ref, fixture.worktree, MASTER_BRANCH));
+	cl_git_pass(git_annotated_commit_from_ref(&ours, fixture.worktree, ours_ref));
+
+	cl_git_pass(git_reference_lookup(&theirs_ref, fixture.worktree, CONFLICT_BRANCH));
+	cl_git_pass(git_annotated_commit_from_ref(&theirs, fixture.worktree, theirs_ref));
+
+	cl_git_pass(git_merge__setup(fixture.worktree,
+		    ours, (const git_annotated_commit **)&theirs, 1));
+
+	for (i = 0; i < ARRAY_SIZE(merge_files); i++) {
+		git_buf_clear(&path);
+		cl_git_pass(git_buf_printf(&path, "%s/%s",
+			    fixture.worktree->gitdir, merge_files[i]));
+		cl_assert(git_path_exists(path.ptr));
+	}
+
+	git_buf_free(&path);
+	git_reference_free(ours_ref);
+	git_reference_free(theirs_ref);
+	git_annotated_commit_free(ours);
+	git_annotated_commit_free(theirs);
+}
+
+void test_worktree_merge__merge_conflict(void)
+{
+	git_buf path = GIT_BUF_INIT, buf = GIT_BUF_INIT;
+	git_reference *theirs_ref;
+	git_annotated_commit *theirs;
+	git_index *index;
+	const git_index_entry *entry;
+	size_t i, conflicts = 0;
+
+	cl_git_pass(git_reference_lookup(&theirs_ref, fixture.worktree, CONFLICT_BRANCH));
+	cl_git_pass(git_annotated_commit_from_ref(&theirs, fixture.worktree, theirs_ref));
+
+	cl_git_pass(git_merge(fixture.worktree,
+		    (const git_annotated_commit **)&theirs, 1, NULL, NULL));
+
+	cl_git_pass(git_repository_index(&index, fixture.worktree));
+	for (i = 0; i < git_index_entrycount(index); i++) {
+		cl_assert(entry = git_index_get_byindex(index, i));
+
+		if (git_index_entry_is_conflict(entry))
+			conflicts++;
+	}
+	cl_assert_equal_sz(conflicts, 3);
+
+	git_reference_free(theirs_ref);
+	git_annotated_commit_free(theirs);
+	git_index_free(index);
+
+	cl_git_pass(git_buf_joinpath(&path, fixture.worktree->workdir, "branch_file.txt"));
+	cl_git_pass(git_futils_readbuffer(&buf, path.ptr));
+	cl_assert_equal_s(buf.ptr, CONFLICT_BRANCH_FILE_TXT);
+
+	git_buf_free(&path);
+	git_buf_free(&buf);
+}
+
diff --git a/tests/worktree/open.c b/tests/worktree/open.c
new file mode 100644
index 0000000..74b9007
--- /dev/null
+++ b/tests/worktree/open.c
@@ -0,0 +1,143 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "worktree.h"
+#include "worktree_helpers.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+static worktree_fixture fixture =
+	WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+static void assert_worktree_valid(git_repository *wt, const char *parentdir, const char *wtdir)
+{
+	git_buf path = GIT_BUF_INIT;
+
+	cl_assert(wt->is_worktree);
+
+	cl_git_pass(git_buf_joinpath(&path, clar_sandbox_path(), wtdir));
+	cl_git_pass(git_path_prettify(&path, path.ptr, NULL));
+	cl_git_pass(git_path_to_dir(&path));
+	cl_assert_equal_s(wt->workdir, path.ptr);
+
+	cl_git_pass(git_buf_joinpath(&path, path.ptr, ".git"));
+	cl_git_pass(git_path_prettify(&path, path.ptr, NULL));
+	cl_assert_equal_s(wt->gitlink, path.ptr);
+
+	cl_git_pass(git_buf_joinpath(&path, clar_sandbox_path(), parentdir));
+	cl_git_pass(git_buf_joinpath(&path, path.ptr, ".git"));
+	cl_git_pass(git_buf_joinpath(&path, path.ptr, "worktrees"));
+	cl_git_pass(git_buf_joinpath(&path, path.ptr, wtdir));
+	cl_git_pass(git_path_prettify(&path, path.ptr, NULL));
+	cl_git_pass(git_path_to_dir(&path));
+	cl_assert_equal_s(wt->gitdir, path.ptr);
+
+	git_buf_free(&path);
+}
+
+void test_worktree_open__initialize(void)
+{
+	setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_open__cleanup(void)
+{
+	cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_open__repository(void)
+{
+	assert_worktree_valid(fixture.worktree, COMMON_REPO, WORKTREE_REPO);
+}
+
+void test_worktree_open__repository_through_workdir(void)
+{
+	git_repository *wt;
+
+	cl_git_pass(git_repository_open(&wt, WORKTREE_REPO));
+	assert_worktree_valid(wt, COMMON_REPO, WORKTREE_REPO);
+
+	git_repository_free(wt);
+}
+
+void test_worktree_open__repository_through_gitlink(void)
+{
+	git_repository *wt;
+
+	cl_git_pass(git_repository_open(&wt, WORKTREE_REPO "/.git"));
+	assert_worktree_valid(wt, COMMON_REPO, WORKTREE_REPO);
+
+	git_repository_free(wt);
+}
+
+void test_worktree_open__repository_through_gitdir(void)
+{
+	git_buf gitdir_path = GIT_BUF_INIT;
+	git_repository *wt;
+
+	cl_git_pass(git_buf_joinpath(&gitdir_path, COMMON_REPO, ".git"));
+	cl_git_pass(git_buf_joinpath(&gitdir_path, gitdir_path.ptr, "worktrees"));
+	cl_git_pass(git_buf_joinpath(&gitdir_path, gitdir_path.ptr, "testrepo-worktree"));
+
+	cl_git_pass(git_repository_open(&wt, gitdir_path.ptr));
+	assert_worktree_valid(wt, COMMON_REPO, WORKTREE_REPO);
+
+	git_buf_free(&gitdir_path);
+	git_repository_free(wt);
+}
+
+void test_worktree_open__open_discovered_worktree(void)
+{
+	git_buf path = GIT_BUF_INIT;
+	git_repository *repo;
+
+	cl_git_pass(git_repository_discover(&path,
+		git_repository_workdir(fixture.worktree), false, NULL));
+	cl_git_pass(git_repository_open(&repo, path.ptr));
+	cl_assert_equal_s(git_repository_workdir(fixture.worktree),
+		git_repository_workdir(repo));
+
+	git_buf_free(&path);
+	git_repository_free(repo);
+}
+
+void test_worktree_open__repository_with_nonexistent_parent(void)
+{
+	git_repository *repo;
+
+	cleanup_fixture_worktree(&fixture);
+
+	cl_fixture_sandbox(WORKTREE_REPO);
+	cl_git_pass(p_chdir(WORKTREE_REPO));
+	cl_git_pass(cl_rename(".gitted", ".git"));
+	cl_git_pass(p_chdir(".."));
+
+	cl_git_fail(git_repository_open(&repo, WORKTREE_REPO));
+
+	cl_fixture_cleanup(WORKTREE_REPO);
+}
+
+void test_worktree_open__open_from_repository(void)
+{
+	git_worktree *opened, *lookedup;
+
+	cl_git_pass(git_worktree_open_from_repository(&opened, fixture.worktree));
+	cl_git_pass(git_worktree_lookup(&lookedup, fixture.repo, WORKTREE_REPO));
+
+	cl_assert_equal_s(opened->name, lookedup->name);
+	cl_assert_equal_s(opened->gitdir_path, lookedup->gitdir_path);
+	cl_assert_equal_s(opened->gitlink_path, lookedup->gitlink_path);
+	cl_assert_equal_s(opened->parent_path, lookedup->parent_path);
+	cl_assert_equal_s(opened->commondir_path, lookedup->commondir_path);
+	cl_assert_equal_i(opened->locked, lookedup->locked);
+
+	git_worktree_free(opened);
+	git_worktree_free(lookedup);
+}
+
+void test_worktree_open__open_from_nonworktree_fails(void)
+{
+	git_worktree *wt;
+
+	cl_git_fail(git_worktree_open_from_repository(&wt, fixture.repo));
+}
diff --git a/tests/worktree/reflog.c b/tests/worktree/reflog.c
new file mode 100644
index 0000000..6152eb3
--- /dev/null
+++ b/tests/worktree/reflog.c
@@ -0,0 +1,65 @@
+#include "clar_libgit2.h"
+#include "worktree_helpers.h"
+
+#include "reflog.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+#define REFLOG "refs/heads/testrepo-worktree"
+#define REFLOG_MESSAGE "reflog message"
+
+static worktree_fixture fixture =
+	WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+void test_worktree_reflog__initialize(void)
+{
+	setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_reflog__cleanup(void)
+{
+	cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_reflog__read(void)
+{
+	git_reflog *reflog;
+	const git_reflog_entry *entry;
+
+	cl_git_pass(git_reflog_read(&reflog, fixture.worktree, REFLOG));
+	cl_assert_equal_i(git_reflog_entrycount(reflog), 1);
+
+	entry = git_reflog_entry_byindex(reflog, 0);
+	cl_assert(entry != NULL);
+	cl_assert_equal_s(git_reflog_entry_message(entry), "branch: Created from HEAD");
+
+	git_reflog_free(reflog);
+}
+
+void test_worktree_reflog__append_then_read(void)
+{
+	git_reflog *reflog, *parent_reflog;
+	const git_reflog_entry *entry;
+	git_reference *head;
+	git_signature *sig;
+	const git_oid *oid;
+
+	cl_git_pass(git_repository_head(&head, fixture.worktree));
+	cl_assert((oid = git_reference_target(head)) != NULL);
+	cl_git_pass(git_signature_now(&sig, "foo", "foo@bar"));
+
+	cl_git_pass(git_reflog_read(&reflog, fixture.worktree, REFLOG));
+	cl_git_pass(git_reflog_append(reflog, oid, sig, REFLOG_MESSAGE));
+	git_reflog_write(reflog);
+
+	cl_git_pass(git_reflog_read(&parent_reflog, fixture.repo, REFLOG));
+	entry = git_reflog_entry_byindex(parent_reflog, 0);
+	cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0);
+	cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0);
+
+	git_reference_free(head);
+	git_signature_free(sig);
+	git_reflog_free(reflog);
+	git_reflog_free(parent_reflog);
+}
diff --git a/tests/worktree/refs.c b/tests/worktree/refs.c
new file mode 100644
index 0000000..b9a0560
--- /dev/null
+++ b/tests/worktree/refs.c
@@ -0,0 +1,156 @@
+#include "clar_libgit2.h"
+#include "path.h"
+#include "refs.h"
+#include "worktree.h"
+#include "worktree_helpers.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+static worktree_fixture fixture =
+	WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+void test_worktree_refs__initialize(void)
+{
+	setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_refs__cleanup(void)
+{
+	cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_refs__list(void)
+{
+	git_strarray refs, wtrefs;
+	unsigned i, j;
+	int error = 0;
+
+	cl_git_pass(git_reference_list(&refs, fixture.repo));
+	cl_git_pass(git_reference_list(&wtrefs, fixture.worktree));
+
+	if (refs.count != wtrefs.count)
+	{
+		error = GIT_ERROR;
+		goto exit;
+	}
+
+	for (i = 0; i < refs.count; i++)
+	{
+		int found = 0;
+
+		for (j = 0; j < wtrefs.count; j++)
+		{
+			if (!strcmp(refs.strings[i], wtrefs.strings[j]))
+			{
+				found = 1;
+				break;
+			}
+		}
+
+		if (!found)
+		{
+			error = GIT_ERROR;
+			goto exit;
+		}
+	}
+
+exit:
+	git_strarray_free(&refs);
+	git_strarray_free(&wtrefs);
+	cl_git_pass(error);
+}
+
+void test_worktree_refs__read_head(void)
+{
+	git_reference *head;
+
+	cl_git_pass(git_repository_head(&head, fixture.worktree));
+
+	git_reference_free(head);
+}
+
+void test_worktree_refs__set_head_fails_when_worktree_wants_linked_repos_HEAD(void)
+{
+	git_reference *head;
+
+	cl_git_pass(git_repository_head(&head, fixture.repo));
+	cl_git_fail(git_repository_set_head(fixture.worktree, git_reference_name(head)));
+
+	git_reference_free(head);
+}
+
+void test_worktree_refs__set_head_fails_when_main_repo_wants_worktree_head(void)
+{
+	git_reference *head;
+
+	cl_git_pass(git_repository_head(&head, fixture.worktree));
+	cl_git_fail(git_repository_set_head(fixture.repo, git_reference_name(head)));
+
+	git_reference_free(head);
+}
+
+void test_worktree_refs__set_head_works_for_current_HEAD(void)
+{
+	git_reference *head;
+
+	cl_git_pass(git_repository_head(&head, fixture.repo));
+	cl_git_pass(git_repository_set_head(fixture.repo, git_reference_name(head)));
+
+	git_reference_free(head);
+}
+
+void test_worktree_refs__set_head_fails_when_already_checked_out(void)
+{
+	cl_git_fail(git_repository_set_head(fixture.repo, "refs/heads/testrepo-worktree"));
+}
+
+void test_worktree_refs__delete_fails_for_checked_out_branch(void)
+{
+       git_reference *branch;
+
+       cl_git_pass(git_branch_lookup(&branch, fixture.repo,
+               "testrepo-worktree", GIT_BRANCH_LOCAL));
+       cl_git_fail(git_branch_delete(branch));
+
+       git_reference_free(branch);
+}
+
+void test_worktree_refs__delete_succeeds_after_pruning_worktree(void)
+{
+       git_reference *branch;
+       git_worktree *worktree;
+
+       cl_git_pass(git_worktree_lookup(&worktree, fixture.repo, fixture.worktreename));
+       cl_git_pass(git_worktree_prune(worktree, GIT_WORKTREE_PRUNE_VALID));
+       git_worktree_free(worktree);
+
+       cl_git_pass(git_branch_lookup(&branch, fixture.repo,
+               "testrepo-worktree", GIT_BRANCH_LOCAL));
+       cl_git_pass(git_branch_delete(branch));
+       git_reference_free(branch);
+}
+
+void test_worktree_refs__creating_refs_uses_commondir(void)
+{
+	   git_reference *head, *branch, *lookup;
+	   git_commit *commit;
+	   git_buf refpath = GIT_BUF_INIT;
+
+	   cl_git_pass(git_buf_joinpath(&refpath,
+		       git_repository_commondir(fixture.worktree), "refs/heads/testbranch"));
+	   cl_assert(!git_path_exists(refpath.ptr));
+
+	   cl_git_pass(git_repository_head(&head, fixture.worktree));
+	   cl_git_pass(git_commit_lookup(&commit, fixture.worktree, git_reference_target(head)));
+	   cl_git_pass(git_branch_create(&branch, fixture.worktree, "testbranch", commit, 0));
+	   cl_git_pass(git_branch_lookup(&lookup, fixture.worktree, "testbranch", GIT_BRANCH_LOCAL));
+	   cl_assert(git_reference_cmp(branch, lookup) == 0);
+	   cl_assert(git_path_exists(refpath.ptr));
+
+	   git_reference_free(lookup);
+	   git_reference_free(branch);
+	   git_reference_free(head);
+	   git_commit_free(commit);
+	   git_buf_free(&refpath);
+}
diff --git a/tests/worktree/repository.c b/tests/worktree/repository.c
new file mode 100644
index 0000000..5c7595c
--- /dev/null
+++ b/tests/worktree/repository.c
@@ -0,0 +1,63 @@
+#include "clar_libgit2.h"
+#include "worktree_helpers.h"
+#include "submodule/submodule_helpers.h"
+
+#include "repository.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+static worktree_fixture fixture =
+	WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+void test_worktree_repository__initialize(void)
+{
+	setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_repository__cleanup(void)
+{
+	cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_repository__head(void)
+{
+	git_reference *ref, *head;
+
+	cl_git_pass(git_reference_lookup(&ref, fixture.repo, "refs/heads/testrepo-worktree"));
+	cl_git_pass(git_repository_head_for_worktree(&head, fixture.repo, "testrepo-worktree"));
+	cl_assert(git_reference_cmp(ref, head) == 0);
+
+	git_reference_free(ref);
+	git_reference_free(head);
+}
+
+void test_worktree_repository__head_fails_for_invalid_worktree(void)
+{
+	git_reference *head = NULL;
+
+	cl_git_fail(git_repository_head_for_worktree(&head, fixture.repo, "invalid"));
+	cl_assert(head == NULL);
+}
+
+void test_worktree_repository__head_detached(void)
+{
+	git_reference *ref, *head;
+
+	cl_git_pass(git_reference_lookup(&ref, fixture.repo, "refs/heads/testrepo-worktree"));
+	cl_git_pass(git_repository_set_head_detached(fixture.worktree, &ref->target.oid));
+
+	cl_assert(git_repository_head_detached(fixture.worktree));
+	cl_assert(git_repository_head_detached_for_worktree(fixture.repo, "testrepo-worktree"));
+	cl_git_fail(git_repository_head_for_worktree(&head, fixture.repo, "testrepo-worktree"));
+
+	git_reference_free(ref);
+}
+
+void test_worktree_repository__head_detached_fails_for_invalid_worktree(void)
+{
+	git_reference *head = NULL;
+
+	cl_git_fail(git_repository_head_detached_for_worktree(fixture.repo, "invalid"));
+	cl_assert(head == NULL);
+}
diff --git a/tests/worktree/submodule.c b/tests/worktree/submodule.c
new file mode 100644
index 0000000..5620775
--- /dev/null
+++ b/tests/worktree/submodule.c
@@ -0,0 +1,92 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "worktree.h"
+#include "worktree_helpers.h"
+
+#define WORKTREE_PARENT "submodules-worktree-parent"
+#define WORKTREE_CHILD "submodules-worktree-child"
+
+static worktree_fixture parent
+    = WORKTREE_FIXTURE_INIT("submodules", WORKTREE_PARENT);
+static worktree_fixture child
+    = WORKTREE_FIXTURE_INIT(NULL, WORKTREE_CHILD);
+
+void test_worktree_submodule__initialize(void)
+{
+	setup_fixture_worktree(&parent);
+
+	cl_git_pass(p_rename(
+		"submodules/testrepo/.gitted",
+		"submodules/testrepo/.git"));
+
+	setup_fixture_worktree(&child);
+}
+
+void test_worktree_submodule__cleanup(void)
+{
+	cleanup_fixture_worktree(&child);
+	cleanup_fixture_worktree(&parent);
+}
+
+void test_worktree_submodule__submodule_worktree_parent(void)
+{
+	cl_assert(git_repository_path(parent.worktree) != NULL);
+	cl_assert(git_repository_workdir(parent.worktree) != NULL);
+
+	cl_assert(!parent.repo->is_worktree);
+	cl_assert(parent.worktree->is_worktree);
+}
+
+void test_worktree_submodule__submodule_worktree_child(void)
+{
+	cl_assert(!parent.repo->is_worktree);
+	cl_assert(parent.worktree->is_worktree);
+	cl_assert(child.worktree->is_worktree);
+}
+
+void test_worktree_submodule__open_discovered_submodule_worktree(void)
+{
+	git_buf path = GIT_BUF_INIT;
+	git_repository *repo;
+
+	cl_git_pass(git_repository_discover(&path,
+		git_repository_workdir(child.worktree), false, NULL));
+	cl_git_pass(git_repository_open(&repo, path.ptr));
+	cl_assert_equal_s(git_repository_workdir(child.worktree),
+		git_repository_workdir(repo));
+
+	git_buf_free(&path);
+	git_repository_free(repo);
+}
+
+void test_worktree_submodule__resolve_relative_url(void)
+{
+	git_buf wt_path = GIT_BUF_INIT;
+	git_buf sm_relative_path = GIT_BUF_INIT, wt_relative_path = GIT_BUF_INIT;
+	git_repository *repo;
+	git_worktree *wt;
+
+	cl_git_pass(git_futils_mkdir("subdir", 0755, GIT_MKDIR_PATH));
+	cl_git_pass(git_path_prettify_dir(&wt_path, "subdir", NULL));
+	cl_git_pass(git_buf_joinpath(&wt_path, wt_path.ptr, "wt"));
+
+	/* Open child repository, which is a submodule */
+	cl_git_pass(git_repository_open(&child.repo, WORKTREE_CHILD));
+
+	/* Create worktree of submodule repository */
+	cl_git_pass(git_worktree_add(&wt, child.repo, "subdir", wt_path.ptr));
+	cl_git_pass(git_repository_open_from_worktree(&repo, wt));
+
+	cl_git_pass(git_submodule_resolve_url(&sm_relative_path, repo,
+		    "../" WORKTREE_CHILD));
+	cl_git_pass(git_submodule_resolve_url(&wt_relative_path, child.repo,
+		    "../" WORKTREE_CHILD));
+
+	cl_assert_equal_s(sm_relative_path.ptr, wt_relative_path.ptr);
+
+	git_worktree_free(wt);
+	git_repository_free(repo);
+	git_buf_free(&wt_path);
+	git_buf_free(&sm_relative_path);
+	git_buf_free(&wt_relative_path);
+}
diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c
new file mode 100644
index 0000000..6e90e6a
--- /dev/null
+++ b/tests/worktree/worktree.c
@@ -0,0 +1,488 @@
+#include "clar_libgit2.h"
+#include "worktree_helpers.h"
+#include "submodule/submodule_helpers.h"
+
+#include "checkout.h"
+#include "repository.h"
+#include "worktree.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+static worktree_fixture fixture =
+	WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+void test_worktree_worktree__initialize(void)
+{
+	setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_worktree__cleanup(void)
+{
+	cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_worktree__list(void)
+{
+	git_strarray wts;
+
+	cl_git_pass(git_worktree_list(&wts, fixture.repo));
+	cl_assert_equal_i(wts.count, 1);
+	cl_assert_equal_s(wts.strings[0], "testrepo-worktree");
+
+	git_strarray_free(&wts);
+}
+
+void test_worktree_worktree__list_with_invalid_worktree_dirs(void)
+{
+	const char *filesets[3][2] = {
+		{ "gitdir", "commondir" },
+		{ "gitdir", "HEAD" },
+		{ "HEAD", "commondir" },
+	};
+	git_buf path = GIT_BUF_INIT;
+	git_strarray wts;
+	unsigned i, j, len;
+
+	cl_git_pass(git_buf_printf(&path, "%s/worktrees/invalid",
+		    fixture.repo->commondir));
+	cl_git_pass(p_mkdir(path.ptr, 0755));
+
+	len = path.size;
+
+	for (i = 0; i < ARRAY_SIZE(filesets); i++) {
+
+		for (j = 0; j < ARRAY_SIZE(filesets[i]); j++) {
+			git_buf_truncate(&path, len);
+			cl_git_pass(git_buf_joinpath(&path, path.ptr, filesets[i][j]));
+			cl_git_pass(p_close(p_creat(path.ptr, 0644)));
+		}
+
+		cl_git_pass(git_worktree_list(&wts, fixture.worktree));
+		cl_assert_equal_i(wts.count, 1);
+		cl_assert_equal_s(wts.strings[0], "testrepo-worktree");
+		git_strarray_free(&wts);
+
+		for (j = 0; j < ARRAY_SIZE(filesets[i]); j++) {
+			git_buf_truncate(&path, len);
+			cl_git_pass(git_buf_joinpath(&path, path.ptr, filesets[i][j]));
+			p_unlink(path.ptr);
+		}
+	}
+
+	git_buf_free(&path);
+}
+
+void test_worktree_worktree__list_in_worktree_repo(void)
+{
+	git_strarray wts;
+
+	cl_git_pass(git_worktree_list(&wts, fixture.worktree));
+	cl_assert_equal_i(wts.count, 1);
+	cl_assert_equal_s(wts.strings[0], "testrepo-worktree");
+
+	git_strarray_free(&wts);
+}
+
+void test_worktree_worktree__list_bare(void)
+{
+	git_repository *repo;
+	git_strarray wts;
+
+	repo = cl_git_sandbox_init("testrepo.git");
+	cl_git_pass(git_worktree_list(&wts, repo));
+	cl_assert_equal_i(wts.count, 0);
+
+	git_repository_free(repo);
+}
+
+void test_worktree_worktree__list_without_worktrees(void)
+{
+	git_repository *repo;
+	git_strarray wts;
+
+	repo = cl_git_sandbox_init("testrepo2");
+	cl_git_pass(git_worktree_list(&wts, repo));
+	cl_assert_equal_i(wts.count, 0);
+
+	git_repository_free(repo);
+}
+
+void test_worktree_worktree__lookup(void)
+{
+	git_worktree *wt;
+	git_buf gitdir_path = GIT_BUF_INIT;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+
+	cl_git_pass(git_buf_joinpath(&gitdir_path, fixture.repo->commondir, "worktrees/testrepo-worktree/"));
+
+	cl_assert_equal_s(wt->gitdir_path, gitdir_path.ptr);
+	cl_assert_equal_s(wt->parent_path, fixture.repo->workdir);
+	cl_assert_equal_s(wt->gitlink_path, fixture.worktree->gitlink);
+	cl_assert_equal_s(wt->commondir_path, fixture.repo->gitdir);
+	cl_assert_equal_s(wt->commondir_path, fixture.repo->commondir);
+
+	git_buf_free(&gitdir_path);
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__lookup_nonexistent_worktree(void)
+{
+	git_worktree *wt;
+
+	cl_git_fail(git_worktree_lookup(&wt, fixture.repo, "nonexistent"));
+	cl_assert_equal_p(wt, NULL);
+}
+
+void test_worktree_worktree__open(void)
+{
+	git_worktree *wt;
+	git_repository *repo;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+
+	cl_git_pass(git_repository_open_from_worktree(&repo, wt));
+	cl_assert_equal_s(git_repository_workdir(repo),
+		git_repository_workdir(fixture.worktree));
+
+	git_repository_free(repo);
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__open_invalid_commondir(void)
+{
+	git_worktree *wt;
+	git_repository *repo;
+	git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
+
+	cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/commondir"));
+	cl_git_pass(git_buf_printf(&path,
+		    "%s/worktrees/testrepo-worktree/commondir",
+		    fixture.repo->commondir));
+	cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644));
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_fail(git_repository_open_from_worktree(&repo, wt));
+
+	git_buf_free(&buf);
+	git_buf_free(&path);
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__open_invalid_gitdir(void)
+{
+	git_worktree *wt;
+	git_repository *repo;
+	git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
+
+	cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/gitdir"));
+	cl_git_pass(git_buf_printf(&path,
+		    "%s/worktrees/testrepo-worktree/gitdir",
+		    fixture.repo->commondir));
+	cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644));
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_fail(git_repository_open_from_worktree(&repo, wt));
+
+	git_buf_free(&buf);
+	git_buf_free(&path);
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__open_invalid_parent(void)
+{
+	git_worktree *wt;
+	git_repository *repo;
+	git_buf buf = GIT_BUF_INIT;
+
+	cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/gitdir"));
+	cl_git_pass(git_futils_writebuffer(&buf,
+		    fixture.worktree->gitlink, O_RDWR, 0644));
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_fail(git_repository_open_from_worktree(&repo, wt));
+
+	git_buf_free(&buf);
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__init(void)
+{
+	git_worktree *wt;
+	git_repository *repo;
+	git_reference *branch;
+	git_buf path = GIT_BUF_INIT;
+
+	cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
+	cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr));
+
+	/* Open and verify created repo */
+	cl_git_pass(git_repository_open(&repo, path.ptr));
+	cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-new/") == 0);
+	cl_git_pass(git_branch_lookup(&branch, repo, "worktree-new", GIT_BRANCH_LOCAL));
+
+	git_buf_free(&path);
+	git_worktree_free(wt);
+	git_reference_free(branch);
+	git_repository_free(repo);
+}
+
+void test_worktree_worktree__init_existing_branch(void)
+{
+	git_reference *head, *branch;
+	git_commit *commit;
+	git_worktree *wt;
+	git_buf path = GIT_BUF_INIT;
+
+	cl_git_pass(git_repository_head(&head, fixture.repo));
+	cl_git_pass(git_commit_lookup(&commit, fixture.repo, &head->target.oid));
+	cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new", commit, false));
+
+	cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
+	cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr));
+
+	git_buf_free(&path);
+	git_commit_free(commit);
+	git_reference_free(head);
+	git_reference_free(branch);
+}
+
+void test_worktree_worktree__init_existing_worktree(void)
+{
+	git_worktree *wt;
+	git_buf path = GIT_BUF_INIT;
+
+	cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
+	cl_git_fail(git_worktree_add(&wt, fixture.repo, "testrepo-worktree", path.ptr));
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_assert_equal_s(wt->gitlink_path, fixture.worktree->gitlink);
+
+	git_buf_free(&path);
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__init_existing_path(void)
+{
+	const char *wtfiles[] = { "HEAD", "commondir", "gitdir", "index" };
+	git_worktree *wt;
+	git_buf path = GIT_BUF_INIT;
+	unsigned i;
+
+	/* Delete files to verify they have not been created by
+	 * the init call */
+	for (i = 0; i < ARRAY_SIZE(wtfiles); i++) {
+		cl_git_pass(git_buf_joinpath(&path,
+			    fixture.worktree->gitdir, wtfiles[i]));
+		cl_git_pass(p_unlink(path.ptr));
+	}
+
+	cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../testrepo-worktree"));
+	cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr));
+
+	/* Verify files have not been re-created */
+	for (i = 0; i < ARRAY_SIZE(wtfiles); i++) {
+		cl_git_pass(git_buf_joinpath(&path,
+			    fixture.worktree->gitdir, wtfiles[i]));
+		cl_assert(!git_path_exists(path.ptr));
+	}
+
+	git_buf_free(&path);
+}
+
+void test_worktree_worktree__init_submodule(void)
+{
+	git_repository *repo, *sm, *wt;
+	git_worktree *worktree;
+	git_buf path = GIT_BUF_INIT;
+
+	cleanup_fixture_worktree(&fixture);
+	repo = setup_fixture_submod2();
+
+	cl_git_pass(git_buf_joinpath(&path, repo->workdir, "sm_unchanged"));
+	cl_git_pass(git_repository_open(&sm, path.ptr));
+	cl_git_pass(git_buf_joinpath(&path, repo->workdir, "../worktree/"));
+	cl_git_pass(git_worktree_add(&worktree, sm, "repo-worktree", path.ptr));
+	cl_git_pass(git_repository_open_from_worktree(&wt, worktree));
+
+	cl_git_pass(git_path_prettify_dir(&path, path.ptr, NULL));
+	cl_assert_equal_s(path.ptr, wt->workdir);
+	cl_git_pass(git_path_prettify_dir(&path, sm->commondir, NULL));
+	cl_assert_equal_s(sm->commondir, wt->commondir);
+
+	cl_git_pass(git_buf_joinpath(&path, sm->gitdir, "worktrees/repo-worktree/"));
+	cl_assert_equal_s(path.ptr, wt->gitdir);
+
+	git_buf_free(&path);
+	git_worktree_free(worktree);
+	git_repository_free(sm);
+	git_repository_free(wt);
+}
+
+void test_worktree_worktree__validate(void)
+{
+	git_worktree *wt;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_pass(git_worktree_validate(wt));
+
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__validate_invalid_commondir(void)
+{
+	git_worktree *wt;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	git__free(wt->commondir_path);
+	wt->commondir_path = "/path/to/invalid/commondir";
+
+	cl_git_fail(git_worktree_validate(wt));
+
+	wt->commondir_path = NULL;
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__validate_invalid_gitdir(void)
+{
+	git_worktree *wt;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	git__free(wt->gitdir_path);
+	wt->gitdir_path = "/path/to/invalid/gitdir";
+	cl_git_fail(git_worktree_validate(wt));
+
+	wt->gitdir_path = NULL;
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__validate_invalid_parent(void)
+{
+	git_worktree *wt;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	git__free(wt->parent_path);
+	wt->parent_path = "/path/to/invalid/parent";
+	cl_git_fail(git_worktree_validate(wt));
+
+	wt->parent_path = NULL;
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__lock_with_reason(void)
+{
+	git_worktree *wt;
+	git_buf reason = GIT_BUF_INIT;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+
+	cl_assert(!git_worktree_is_locked(NULL, wt));
+	cl_git_pass(git_worktree_lock(wt, "because"));
+	cl_assert(git_worktree_is_locked(&reason, wt) > 0);
+	cl_assert_equal_s(reason.ptr, "because");
+	cl_assert(wt->locked);
+
+	git_buf_free(&reason);
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__lock_without_reason(void)
+{
+	git_worktree *wt;
+	git_buf reason = GIT_BUF_INIT;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+
+	cl_assert(!git_worktree_is_locked(NULL, wt));
+	cl_git_pass(git_worktree_lock(wt, NULL));
+	cl_assert(git_worktree_is_locked(&reason, wt) > 0);
+	cl_assert_equal_i(reason.size, 0);
+	cl_assert(wt->locked);
+
+	git_buf_free(&reason);
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__unlock_unlocked_worktree(void)
+{
+	git_worktree *wt;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_assert(!git_worktree_is_locked(NULL, wt));
+	cl_assert(git_worktree_unlock(wt) == 0);
+	cl_assert(!wt->locked);
+
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__unlock_locked_worktree(void)
+{
+	git_worktree *wt;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_pass(git_worktree_lock(wt, NULL));
+	cl_assert(git_worktree_is_locked(NULL, wt));
+	cl_git_pass(git_worktree_unlock(wt));
+	cl_assert(!wt->locked);
+
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__prune_valid(void)
+{
+	git_worktree *wt;
+	git_repository *repo;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_pass(git_worktree_prune(wt, GIT_WORKTREE_PRUNE_VALID));
+
+	/* Assert the repository is not valid anymore */
+	cl_git_fail(git_repository_open_from_worktree(&repo, wt));
+
+	git_worktree_free(wt);
+	git_repository_free(repo);
+}
+
+void test_worktree_worktree__prune_locked(void)
+{
+	git_worktree *wt;
+	git_repository *repo;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_pass(git_worktree_lock(wt, NULL));
+	cl_git_fail(git_worktree_prune(wt, GIT_WORKTREE_PRUNE_VALID));
+	cl_git_fail(git_worktree_prune(wt, ~GIT_WORKTREE_PRUNE_LOCKED));
+
+	/* Assert the repository is still valid */
+	cl_git_pass(git_repository_open_from_worktree(&repo, wt));
+
+	git_worktree_free(wt);
+	git_repository_free(repo);
+}
+
+void test_worktree_worktree__prune_gitdir(void)
+{
+	git_worktree *wt;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_pass(git_worktree_prune(wt, GIT_WORKTREE_PRUNE_VALID));
+
+	cl_assert(!git_path_exists(wt->gitdir_path));
+	cl_assert(git_path_exists(wt->gitlink_path));
+
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__prune_both(void)
+{
+	git_worktree *wt;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_pass(git_worktree_prune(wt, GIT_WORKTREE_PRUNE_WORKING_TREE | GIT_WORKTREE_PRUNE_VALID));
+
+	cl_assert(!git_path_exists(wt->gitdir_path));
+	cl_assert(!git_path_exists(wt->gitlink_path));
+
+	git_worktree_free(wt);
+}
diff --git a/tests/worktree/worktree_helpers.c b/tests/worktree/worktree_helpers.c
new file mode 100644
index 0000000..6d4cdba
--- /dev/null
+++ b/tests/worktree/worktree_helpers.c
@@ -0,0 +1,30 @@
+#include "clar_libgit2.h"
+#include "worktree_helpers.h"
+
+void cleanup_fixture_worktree(worktree_fixture *fixture)
+{
+	if (!fixture)
+		return;
+
+	if (fixture->repo) {
+		git_repository_free(fixture->repo);
+		fixture->repo = NULL;
+	}
+	if (fixture->worktree) {
+		git_repository_free(fixture->worktree);
+		fixture->worktree = NULL;
+	}
+
+	if (fixture->reponame)
+		cl_fixture_cleanup(fixture->reponame);
+	if (fixture->worktreename)
+		cl_fixture_cleanup(fixture->worktreename);
+}
+
+void setup_fixture_worktree(worktree_fixture *fixture)
+{
+	if (fixture->reponame)
+		fixture->repo = cl_git_sandbox_init(fixture->reponame);
+	if (fixture->worktreename)
+		fixture->worktree = cl_git_sandbox_init(fixture->worktreename);
+}
diff --git a/tests/worktree/worktree_helpers.h b/tests/worktree/worktree_helpers.h
new file mode 100644
index 0000000..35ea9ed
--- /dev/null
+++ b/tests/worktree/worktree_helpers.h
@@ -0,0 +1,11 @@
+typedef struct {
+	const char *reponame;
+	const char *worktreename;
+	git_repository *repo;
+	git_repository *worktree;
+} worktree_fixture;
+
+#define WORKTREE_FIXTURE_INIT(repo, worktree) { (repo), (worktree), NULL, NULL }
+
+void cleanup_fixture_worktree(worktree_fixture *fixture);
+void setup_fixture_worktree(worktree_fixture *fixture);