Merge pull request #4088 from chescock/packfile-name-using-complete-hash

Ensure packfiles with different contents have different names
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0ae75e1..bcf8160 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,10 @@
 
 ### API additions
 
+* You can now set the default share mode on Windows for opening files using
+  `GIT_OPT_SET_WINDOWS_SHAREMODE` option with `git_libgit2_opts()`.
+  You can query the current share mode with `GIT_OPT_GET_WINDOWS_SHAREMODE`.
+
 ### API removals
 
 ### Breaking API changes
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4bfd1b4..354ae3e 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,6 +238,7 @@
 
 IF (WIN32 AND WINHTTP)
 	ADD_DEFINITIONS(-DGIT_WINHTTP)
+	ADD_DEFINITIONS(-DGIT_HTTPS)
 
 	# 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
@@ -292,13 +294,19 @@
 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)
+	ADD_DEFINITIONS(-DSHA1DC_NO_STANDARD_INCLUDES=1)
+	ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\"common.h\")
+	ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"common.h\")
+	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()
@@ -464,7 +472,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}")
@@ -539,11 +550,13 @@
 
 IF (SECURITY_FOUND)
   ADD_DEFINITIONS(-DGIT_SECURE_TRANSPORT)
+  ADD_DEFINITIONS(-DGIT_HTTPS)
   INCLUDE_DIRECTORIES(${SECURITY_INCLUDE_DIR})
 ENDIF ()
 
 IF (OPENSSL_FOUND)
   ADD_DEFINITIONS(-DGIT_OPENSSL)
+  ADD_DEFINITIONS(-DGIT_HTTPS)
   INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
   SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES})
 ENDIF()
@@ -696,9 +709,9 @@
 
 	ENABLE_TESTING()
 	IF (WINHTTP OR OPENSSL_FOUND OR SECURITY_FOUND)
-		ADD_TEST(libgit2_clar libgit2_clar -ionline)
+		ADD_TEST(libgit2_clar libgit2_clar -ionline -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
 	ELSE ()
-		ADD_TEST(libgit2_clar libgit2_clar -v)
+		ADD_TEST(libgit2_clar libgit2_clar -v -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
 	ENDIF ()
 
 	# Add a test target which runs the cred callback tests, to be
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/README.md b/README.md
index 19d24de..1bbd371 100644
--- a/README.md
+++ b/README.md
@@ -43,10 +43,9 @@
 What It Can Do
 ==============
 
-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.
+libgit2 provides you with the ability to manage Git repositories in the
+programming language of your choice.  It's used in production to power many
+applications including GitHub.com, Plastic SCM and Visual Studio Team Services.
 
 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
@@ -68,6 +67,16 @@
 * descriptive and detailed error messages
 * ...and more (over 175 different API calls)
 
+As libgit2 is purely a consumer of the Git system, we have to
+adjust to changes made upstream. This has two major consequences:
+
+* Some changes may require us to change provided interfaces. While we try to
+  implement functions in a generic way so that no future changes are required,
+  we cannot promise a completely stable API.
+* As we have to keep up with changes in behavior made upstream, we may lag
+  behind in some areas. We usually to document these incompatibilities in our
+  issue tracker with the label "git change".
+
 Optional dependencies
 =====================
 
@@ -124,6 +133,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
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/network/common.c b/examples/network/common.c
index d123eed..1a81a10 100644
--- a/examples/network/common.c
+++ b/examples/network/common.c
@@ -1,5 +1,7 @@
 #include "common.h"
 #include <stdio.h>
+#include <string.h>
+#include <errno.h>
 
 /* Shamelessly borrowed from http://stackoverflow.com/questions/3417837/
  * with permission of the original author, Martin Pool.
@@ -20,15 +22,27 @@
 		unsigned int UNUSED(allowed_types),
 		void * UNUSED(payload))
 {
-	char username[128] = {0};
-	char password[128] = {0};
+	char *username = NULL, *password = NULL;
+	int error;
 
 	printf("Username: ");
-	scanf("%s", username);
+	if (getline(&username, NULL, stdin) < 0) {
+		fprintf(stderr, "Unable to read username: %s", strerror(errno));
+		return -1;
+	}
 
 	/* Yup. Right there on your terminal. Careful where you copy/paste output. */
 	printf("Password: ");
-	scanf("%s", password);
+	if (getline(&password, NULL, stdin) < 0) {
+		fprintf(stderr, "Unable to read password: %s", strerror(errno));
+		free(username);
+		return -1;
+	}
 
-	return git_cred_userpass_plaintext_new(out, username, password);
+	error = git_cred_userpass_plaintext_new(out, username, password);
+
+	free(username);
+	free(password);
+
+	return error;
 }
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 5be7fb6..f65cfdd 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -178,6 +178,11 @@
 	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_FSYNC_GITDIR,
+	GIT_OPT_GET_WINDOWS_SHAREMODE,
+	GIT_OPT_SET_WINDOWS_SHAREMODE,
+	GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION,
 } git_libgit2_opt_t;
 
 /**
@@ -282,6 +287,17 @@
  *		> - `user_agent` is the value that will be delivered as the
  *		>   User-Agent header on HTTP requests.
  *
+ *	* opts(GIT_OPT_SET_WINDOWS_SHAREMODE, unsigned long value)
+ *
+ *		> Set the share mode used when opening files on Windows.
+ *		> For more information, see the documentation for CreateFile.
+ *		> The default is: FILE_SHARE_READ | FILE_SHARE_WRITE.  This is
+ *		> ignored and unused on non-Windows platforms.
+ *
+ *	* opts(GIT_OPT_GET_WINDOWS_SHAREMODE, unsigned long *value)
+ *
+ *		> Get the share mode used when opening files on Windows.
+ *
  *	* opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, int enabled)
  *
  *		> Enable strict input validation when creating new objects
@@ -305,6 +321,30 @@
  *		>
  *		> - `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_FSYNC_GITDIR, int enabled)
+ *
+ *		> Enable synchronized writes of files in the gitdir using `fsync`
+ *		> (or the platform equivalent) to ensure that new object data
+ *		> is written to permanent storage, not simply cached.  This
+ *		> defaults to disabled.
+ *
+ *	 opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, int enabled)
+ *
+ *		> Enable strict verification of object hashsums when reading
+ *		> objects from disk. This may impact performance due to an
+ *		> additional checksum calculation on each object. This defaults
+ *		> to enabled.
+ *
  * @param option Option key
  * @param ... value to set the option
  * @return 0 on success, <0 on failure
diff --git a/include/git2/errors.h b/include/git2/errors.h
index e959ffd..6f55802 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -53,6 +53,8 @@
 
 	GIT_PASSTHROUGH     = -30,	/**< Internal only */
 	GIT_ITEROVER        = -31,	/**< Signals end of iteration with iterator */
+	GIT_RETRY           = -32,	/**< Internal only */
+	GIT_EMISMATCH       = -33,	/**< Hashsum mismatch in object */
 } git_error_code;
 
 /**
@@ -100,6 +102,8 @@
 	GITERR_REBASE,
 	GITERR_FILESYSTEM,
 	GITERR_PATCH,
+	GITERR_WORKTREE,
+	GITERR_SHA1
 } git_error_t;
 
 /**
diff --git a/include/git2/global.h b/include/git2/global.h
index ce5bdf4..2a87e10 100644
--- a/include/git2/global.h
+++ b/include/git2/global.h
@@ -14,7 +14,7 @@
 /**
  * Init the global state
  *
- * This function must the called before any other libgit2 function in
+ * This function must be called before any other libgit2 function in
  * order to set up global state and threading.
  *
  * This function may be called multiple times - it will return the number
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/odb.h b/include/git2/odb.h
index b3ed270..b7dc0c5 100644
--- a/include/git2/odb.h
+++ b/include/git2/odb.h
@@ -488,7 +488,7 @@
  * The backends are checked in relative ordering, based on the
  * value of the `priority` parameter.
  *
- * Read <odb_backends.h> for more information.
+ * Read <sys/odb_backend.h> for more information.
  *
  * @param odb database to add the backend to
  * @param backend pointer to a git_odb_backend instance
@@ -509,7 +509,7 @@
  *
  * Writing is disabled on alternate backends.
  *
- * Read <odb_backends.h> for more information.
+ * Read <sys/odb_backend.h> for more information.
  *
  * @param odb database to add the backend to
  * @param backend pointer to a git_odb_backend instance
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/remote.h b/include/git2/remote.h
index 6911141..e9e4e5b 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -715,8 +715,8 @@
  * Peform all the steps from a push.
  *
  * @param remote the remote to push to
- * @param refspecs the refspecs to use for pushing. If none are
- * passed, the configured refspecs will be used
+ * @param refspecs the refspecs to use for pushing. If NULL or an empty
+ *                 array, the configured refspecs will be used
  * @param opts options to use for this push
  */
 GIT_EXTERN(int) git_remote_push(git_remote *remote,
@@ -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/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/transport.h b/include/git2/sys/transport.h
index 60e38b2..a395de5 100644
--- a/include/git2/sys/transport.h
+++ b/include/git2/sys/transport.h
@@ -241,6 +241,16 @@
  */
 GIT_EXTERN(int) git_transport_smart_credentials(git_cred **out, git_transport *transport, const char *user, int methods);
 
+/**
+ * Get a copy of the proxy options
+ *
+ * The url is copied and must be freed by the caller.
+ *
+ * @param out options struct to fill
+ * @param transport the transport to extract the data from.
+ */
+GIT_EXTERN(int) git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport);
+
 /*
  *** End of base transport interface ***
  *** Begin interface for subtransports for the smart transport ***
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/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/worktree.h b/include/git2/worktree.h
new file mode 100644
index 0000000..d3fa88e
--- /dev/null
+++ b/include/git2/worktree.h
@@ -0,0 +1,220 @@
+/*
+ * 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);
+
+typedef struct git_worktree_add_options {
+	unsigned int version;
+
+	int lock; /**< lock newly created worktree */
+} git_worktree_add_options;
+
+#define GIT_WORKTREE_ADD_OPTIONS_VERSION 1
+#define GIT_WORKTREE_ADD_OPTIONS_INIT {GIT_WORKTREE_ADD_OPTIONS_VERSION,0}
+
+/**
+ * Initializes a `git_worktree_add_options` with default vaules.
+ * Equivalent to creating an instance with
+ * GIT_WORKTREE_ADD_OPTIONS_INIT.
+ *
+ * @param opts the struct to initialize
+ * @param version Verison of struct; pass `GIT_WORKTREE_ADD_OPTIONS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
+int git_worktree_add_init_options(git_worktree_add_options *opts,
+	unsigned int version);
+
+/**
+ * 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
+ * @param opts Options to modify default behavior. May be NULL
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_worktree_add(git_worktree **out, git_repository *repo,
+	const char *name, const char *path,
+	const git_worktree_add_options *opts);
+
+/**
+ * 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;
+
+typedef struct git_worktree_prune_options {
+	unsigned int version;
+
+	uint32_t flags;
+} git_worktree_prune_options;
+
+#define GIT_WORKTREE_PRUNE_OPTIONS_VERSION 1
+#define GIT_WORKTREE_PRUNE_OPTIONS_INIT {GIT_WORKTREE_PRUNE_OPTIONS_VERSION,0}
+
+/**
+ * Initializes a `git_worktree_prune_options` with default vaules.
+ * Equivalent to creating an instance with
+ * GIT_WORKTREE_PRUNE_OPTIONS_INIT.
+ *
+ * @param opts the struct to initialize
+ * @param version Verison of struct; pass `GIT_WORKTREE_PRUNE_OPTIONS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_worktree_prune_init_options(
+	git_worktree_prune_options *opts,
+	unsigned int version);
+
+/**
+ * Is the worktree prunable with the given options?
+ *
+ * A worktree is not prunable in the following scenarios:
+ *
+ * - the worktree is linking to a valid on-disk worktree. The
+ *   `valid` member will cause this check to be ignored.
+ * - the worktree is locked. The `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,
+	git_worktree_prune_options *opts);
+
+/**
+ * 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 opts Specifies which checks to override. See
+ *        `git_worktree_is_prunable`. May be NULL
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_worktree_prune(git_worktree *wt,
+	git_worktree_prune_options *opts);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/libgit2.pc.in b/libgit2.pc.in
index 329a560..96b9659 100644
--- a/libgit2.pc.in
+++ b/libgit2.pc.in
@@ -1,4 +1,4 @@
-prefix=@PKGCONFIG_PREFIX@
+prefix="@PKGCONFIG_PREFIX@"
 libdir=@PKGCONFIG_LIBDIR@
 includedir=@PKGCONFIG_INCLUDEDIR@
 
@@ -6,7 +6,7 @@
 Description: The git library, take 2
 Version: @LIBGIT2_VERSION_STRING@
 
-Libs: -L"${libdir}" -lgit2
+Libs: -L${libdir} -lgit2
 Libs.private: @LIBGIT2_PC_LIBS@
 Requires.private: @LIBGIT2_PC_REQUIRES@
 
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/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 0bb761d..e30ea5e 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -395,9 +395,13 @@
 	if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) {
 		bool samename;
 
-		/* for attribute checks or root ignore checks, fail match */
+		/*
+		 * for attribute checks or checks at the root of this match's
+		 * containing_dir (or root of the repository if no containing_dir),
+		 * do not match.
+		 */
 		if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) ||
-			path->basename == path->path)
+			path->basename == relpath)
 			return false;
 
 		flags |= FNM_LEADING_DIR;
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 0ade38c..5416189 100644
--- a/src/attrcache.c
+++ b/src/attrcache.c
@@ -5,8 +5,6 @@
 #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 */
@@ -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;
@@ -287,14 +290,16 @@
 		const char *cfgval = entry->value;
 
 		/* expand leading ~/ as needed */
-		if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' &&
-			!git_sysdir_find_global_file(&buf, &cfgval[2]))
-			*out = git_buf_detach(&buf);
-		else if (cfgval)
+		if (cfgval && cfgval[0] == '~' && cfgval[1] == '/') {
+			if (! (error = git_sysdir_expand_global_file(&buf, &cfgval[2])))
+				*out = git_buf_detach(&buf);
+		} else if (cfgval) {
 			*out = git__strdup(cfgval);
+		}
 	}
-	else if (!git_sysdir_find_xdg_file(&buf, fallback))
+	else if (!git_sysdir_find_xdg_file(&buf, fallback)) {
 		*out = git_buf_detach(&buf);
+	}
 
 	git_config_entry_free(entry);
 	git_buf_free(&buf);
@@ -309,7 +314,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 +350,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);
@@ -429,11 +434,11 @@
 	if (macro->assigns.length == 0)
 		return 0;
 
-	if (git_mutex_lock(&cache->lock) < 0) {
+	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..13f5cb4 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)
@@ -514,11 +517,12 @@
 	if (!num_parents) {
 		git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit));
 		goto finish;
-	}
-	else if (num_parents < (int)ARRAY_SIZE(sg_buf))
+	} else if (num_parents < (int)ARRAY_SIZE(sg_buf))
 		memset(sg_buf, 0, sizeof(sg_buf));
-	else
+	else {
 		sg_origin = git__calloc(num_parents, sizeof(*sg_origin));
+		GITERR_CHECK_ALLOC(sg_origin);
+	}
 
 	for (i=0; i<num_parents; i++) {
 		git_commit *p;
@@ -543,7 +547,7 @@
 		}
 		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);
 			origin_decref(porigin);
 			goto finish;
 		}
diff --git a/src/blob.c b/src/blob.c
index cd5df35..19d3039 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -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 7ddcb3d..fe4955a 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"
 
@@ -126,6 +127,31 @@
 		repository, branch_name, commit->commit, commit->description, force);
 }
 
+static int branch_equals(git_repository *repo, const char *path, void *payload)
+{
+	git_reference *branch = (git_reference *) payload;
+	git_reference *head = NULL;
+	int equal = 0;
+
+	if (git_reference__read_head(&head, repo, path) < 0 ||
+		git_reference_type(head) != GIT_REF_SYMBOLIC)
+		goto done;
+
+	equal = !git__strcmp(head->target.symbolic, branch->name);
+
+done:
+	git_reference_free(head);
+	return equal;
+}
+
+int git_branch_is_checked_out(const git_reference *branch)
+{
+	assert(branch && git_reference_is_branch(branch));
+
+	return git_repository_foreach_head(git_reference_owner(branch),
+		branch_equals, (void *) branch) == 1;
+}
+
 int git_branch_delete(git_reference *branch)
 {
 	int is_head;
@@ -149,6 +175,12 @@
 		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;
diff --git a/src/buffer.c b/src/buffer.c
index fdb732d..6dfcbfb 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -18,18 +18,19 @@
 char git_buf__oom[1];
 
 #define ENSURE_SIZE(b, d) \
-	if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\
+	if ((d) > (b)->asize && git_buf_grow((b), (d)) < 0)\
 		return -1;
 
 
-void git_buf_init(git_buf *buf, size_t initial_size)
+int git_buf_init(git_buf *buf, size_t initial_size)
 {
 	buf->asize = 0;
 	buf->size = 0;
 	buf->ptr = git_buf__initbuf;
 
-	if (initial_size)
-		git_buf_grow(buf, initial_size);
+	ENSURE_SIZE(buf, initial_size);
+
+	return 0;
 }
 
 int git_buf_try_grow(
@@ -577,7 +578,7 @@
 	return data;
 }
 
-void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
+int git_buf_attach(git_buf *buf, char *ptr, size_t asize)
 {
 	git_buf_free(buf);
 
@@ -588,9 +589,10 @@
 			buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
 		else /* pass 0 to fall back on strlen + 1 */
 			buf->asize = buf->size + 1;
-	} else {
-		git_buf_grow(buf, asize);
 	}
+
+	ENSURE_SIZE(buf, asize);
+	return 0;
 }
 
 void git_buf_attach_notowned(git_buf *buf, const char *ptr, size_t size)
@@ -724,9 +726,7 @@
 	GITERR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b);
 	GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep);
 	GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
-	if (git_buf_grow(buf, alloc_len) < 0)
-		return -1;
-	assert(buf->ptr);
+	ENSURE_SIZE(buf, alloc_len);
 
 	/* fix up internal pointers */
 	if (offset_a >= 0)
@@ -780,8 +780,7 @@
 	GITERR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b);
 	GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_c);
 	GITERR_CHECK_ALLOC_ADD(&len_total, len_total, 1);
-	if (git_buf_grow(buf, len_total) < 0)
-		return -1;
+	ENSURE_SIZE(buf, len_total);
 
 	tgt = buf->ptr;
 
diff --git a/src/buffer.h b/src/buffer.h
index cdfca6d..b0aece4 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -34,7 +34,7 @@
  * For the cases where GIT_BUF_INIT cannot be used to do static
  * initialization.
  */
-extern void git_buf_init(git_buf *buf, size_t initial_size);
+extern int git_buf_init(git_buf *buf, size_t initial_size);
 
 /**
  * Resize the buffer allocation to make more space.
@@ -66,13 +66,14 @@
  * 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);
 
 extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
 extern char *git_buf_detach(git_buf *buf);
-extern void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
+extern int git_buf_attach(git_buf *buf, char *ptr, size_t asize);
 
 /* Populates a `git_buf` where the contents are not "owned" by the
  * buffer, and calls to `git_buf_free` will not free the given buf.
diff --git a/src/cache.c b/src/cache.c
index 16ae9b3..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),
@@ -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 b70d5ab..25018d2 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 {
@@ -372,10 +370,8 @@
 			 */
 			const git_index_entry *e = git_index_get_byindex(data->index, pos);
 
-			if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0) {
-				notify = GIT_CHECKOUT_NOTIFY_DIRTY;
-				remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0);
-			}
+			if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0)
+				return git_iterator_advance_into(wditem, workdir);
 		}
 	}
 
@@ -2321,8 +2317,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 +2324,7 @@
 	data->index = NULL;
 
 	git_strmap_free(data->mkdir_map);
+	data->mkdir_map = NULL;
 
 	git_attr_session__free(&data->attr_session);
 }
diff --git a/src/cherrypick.c b/src/cherrypick.c
index ab06733..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;
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 87ab2ab..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;
 
@@ -642,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 */
@@ -706,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";
@@ -766,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/config.c b/src/config.c
index 0d73ad2..169a628 100644
--- a/src/config.c
+++ b/src/config.c
@@ -576,22 +576,50 @@
  * Setters
  **************/
 
-static int config_error_nofiles(const char *name)
+typedef enum {
+	BACKEND_USE_SET,
+	BACKEND_USE_DELETE
+} backend_use;
+
+static const char *uses[] = {
+    "set",
+    "delete"
+};
+
+static int get_backend_for_use(git_config_backend **out,
+	git_config *cfg, const char *name, backend_use use)
 {
+	size_t i;
+	file_internal *f;
+
+	*out = NULL;
+
+	if (git_vector_length(&cfg->files) == 0) {
+		giterr_set(GITERR_CONFIG,
+			"cannot %s value for '%s' when no config files exist",
+			uses[use], name);
+		return GIT_ENOTFOUND;
+	}
+
+	git_vector_foreach(&cfg->files, i, f) {
+		if (!f->file->readonly) {
+			*out = f->file;
+			return 0;
+		}
+	}
+
 	giterr_set(GITERR_CONFIG,
-		"cannot set value for '%s' when no config files exist", name);
+		"cannot %s value for '%s' when all config files are readonly",
+		uses[use], name);
 	return GIT_ENOTFOUND;
 }
 
 int git_config_delete_entry(git_config *cfg, const char *name)
 {
 	git_config_backend *file;
-	file_internal *internal;
 
-	internal = git_vector_get(&cfg->files, 0);
-	if (!internal || !internal->file)
-		return config_error_nofiles(name);
-	file = internal->file;
+	if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0)
+		return GIT_ENOTFOUND;
 
 	return file->del(file, name);
 }
@@ -617,17 +645,14 @@
 {
 	int error;
 	git_config_backend *file;
-	file_internal *internal;
 
 	if (!value) {
 		giterr_set(GITERR_CONFIG, "the value to set cannot be NULL");
 		return -1;
 	}
 
-	internal = git_vector_get(&cfg->files, 0);
-	if (!internal || !internal->file)
-		return config_error_nofiles(name);
-	file = internal->file;
+	if (get_backend_for_use(&file, cfg, name, BACKEND_USE_SET) < 0)
+		return GIT_ENOTFOUND;
 
 	error = file->set(file, name, value);
 
@@ -1032,12 +1057,9 @@
 int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
 {
 	git_config_backend *file;
-	file_internal *internal;
 
-	internal = git_vector_get(&cfg->files, 0);
-	if (!internal || !internal->file)
-		return config_error_nofiles(name);
-	file = internal->file;
+	if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0)
+		return GIT_ENOTFOUND;
 
 	return file->set_multivar(file, name, regexp, value);
 }
@@ -1045,12 +1067,9 @@
 int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp)
 {
 	git_config_backend *file;
-	file_internal *internal;
 
-	internal = git_vector_get(&cfg->files, 0);
-	if (!internal || !internal->file)
-		return config_error_nofiles(name);
-	file = internal->file;
+	if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0)
+		return GIT_ENOTFOUND;
 
 	return file->del_multivar(file, name, regexp);
 }
@@ -1339,9 +1358,6 @@
 
 int git_config_parse_path(git_buf *out, const char *value)
 {
-	int error = 0;
-	const git_buf *home;
-
 	assert(out && value);
 
 	git_buf_sanitize(out);
@@ -1352,16 +1368,7 @@
 			return -1;
 		}
 
-		if ((error = git_sysdir_get(&home, GIT_SYSDIR_GLOBAL)) < 0)
-			return error;
-
-		git_buf_sets(out, home->ptr);
-		git_buf_puts(out, value + 1);
-
-		if (git_buf_oom(out))
-			return -1;
-
-		return 0;
+		return git_sysdir_expand_global_file(out, value[1] ? &value[2] : NULL);
 	}
 
 	return git_buf_sets(out, value);
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 2e3d568..e15d57b 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;
@@ -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) {
@@ -1029,7 +1027,7 @@
 	first_quote = strchr(line, '"');
 	if (first_quote == NULL) {
 		set_parse_error(reader, 0, "Missing quotation marks in section header");
-		return -1;
+		goto end_error;
 	}
 
 	last_quote = strrchr(line, '"');
@@ -1037,14 +1035,15 @@
 
 	if (quoted_len == 0) {
 		set_parse_error(reader, 0, "Missing closing quotation mark in section header");
-		return -1;
+		goto end_error;
 	}
 
 	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_error;
 
 	rpos = 0;
 
@@ -1060,8 +1059,7 @@
 		switch (c) {
 		case 0:
 			set_parse_error(reader, 0, "Unexpected end-of-line in section header");
-			git_buf_free(&buf);
-			return -1;
+			goto end_error;
 
 		case '"':
 			goto end_parse;
@@ -1071,8 +1069,7 @@
 
 			if (c == 0) {
 				set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
-				git_buf_free(&buf);
-				return -1;
+				goto end_error;
 			}
 
 		default:
@@ -1084,6 +1081,9 @@
 	} while (line + rpos < last_quote);
 
 end_parse:
+	if (git_buf_oom(&buf))
+		goto end_error;
+
 	if (line[rpos] != '"' || line[rpos + 1] != ']') {
 		set_parse_error(reader, rpos, "Unexpected text after closing quotes");
 		git_buf_free(&buf);
@@ -1092,6 +1092,11 @@
 
 	*section_name = git_buf_detach(&buf);
 	return 0;
+
+end_error:
+	git_buf_free(&buf);
+
+	return -1;
 }
 
 static int parse_section_header(struct reader *reader, char **section_out)
@@ -1251,7 +1256,7 @@
 {
 	/* From the user's home */
 	if (path[0] == '~' && path[1] == '/')
-		return git_sysdir_find_global_file(out, &path[1]);
+		return git_sysdir_expand_global_file(out, &path[1]);
 
 	return git_path_join_unrooted(out, path, dir, NULL);
 }
@@ -1262,7 +1267,7 @@
 /* Escape the values to write them to the file */
 static char *escape_value(const char *ptr)
 {
-	git_buf buf = GIT_BUF_INIT;
+	git_buf buf;
 	size_t len;
 	const char *esc;
 
@@ -1272,7 +1277,8 @@
 	if (!len)
 		return git__calloc(1, sizeof(char));
 
-	git_buf_grow(&buf, len);
+	if (git_buf_init(&buf, len) < 0)
+		return NULL;
 
 	while (*ptr != '\0') {
 		if ((esc = strchr(escaped, *ptr)) != NULL) {
diff --git a/src/config_file.h b/src/config_file.h
index 1c52892..654e6ca 100644
--- a/src/config_file.h
+++ b/src/config_file.h
@@ -7,6 +7,7 @@
 #ifndef INCLUDE_config_file_h__
 #define INCLUDE_config_file_h__
 
+#include "git2/sys/config.h"
 #include "git2/config.h"
 
 GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level)
diff --git a/src/describe.c b/src/describe.c
index 16e1955..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;
 		}
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 0adf704..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,
@@ -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_parse.c b/src/diff_parse.c
index e640063..5e3a7a1 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_tform.c b/src/diff_tform.c
index f2ff147..b004ddd 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -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/fetchhead.c b/src/fetchhead.c
index 0d9ab2c..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) {
@@ -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)
diff --git a/src/filebuf.c b/src/filebuf.c
index ef68b16..80250cc 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -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;
@@ -425,6 +428,11 @@
 
 	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);
 		goto on_error;
@@ -437,6 +445,9 @@
 		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);
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 7a87332..2f3f58d 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(
@@ -68,8 +66,8 @@
 
 int git_futils_creat_locked(const char *path, const mode_t mode)
 {
-	int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC |
-		O_EXCL | O_BINARY | O_CLOEXEC, mode);
+	int fd = p_open(path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC,
+		mode);
 
 	if (fd < 0) {
 		int error = errno;
@@ -198,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;
 
@@ -237,10 +236,16 @@
 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;
 
@@ -255,8 +260,19 @@
 		return error;
 	}
 
-	if ((error = p_close(fd)) < 0)
+	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;
 }
@@ -288,13 +304,19 @@
 	if (fd < 0)
 		return fd;
 
-	len = git_futils_filesize(fd);
+	if ((len = git_futils_filesize(fd)) < 0) {
+		result = -1;
+		goto out;
+	}
+
 	if (!git__is_sizet(len)) {
 		giterr_set(GITERR_OS, "file `%s` too large to mmap", path);
-		return -1;
+		result = -1;
+		goto out;
 	}
 
 	result = git_futils_mmap_ro(out, fd, 0, (size_t)len);
+out:
 	p_close(fd);
 	return result;
 }
@@ -607,7 +629,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;
 		}
@@ -748,6 +770,9 @@
 
 		if (en == ENOENT || en == ENOTDIR) {
 			/* do nothing */
+		} else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0 &&
+			en == EBUSY) {
+			error = git_path_set_error(errno, path, "rmdir");
 		} else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) {
 			error = GIT_ITEROVER;
 		} else {
@@ -1109,3 +1134,37 @@
 		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;
+	int error;
+
+	if ((parent = git_path_dirname(path)) == NULL)
+		return -1;
+
+	error = git_futils_fsync_dir(parent);
+	git__free(parent);
+	return error;
+}
diff --git a/src/fileops.h b/src/fileops.h
index 65c96a6..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);
 
@@ -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 0d8831e..e74cc10 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -911,14 +911,19 @@
 				last_stream);
 
 		if (error < 0)
-			return error;
+			goto out;
 
 		git_vector_insert(streams, filter_stream);
 		last_stream = filter_stream;
 	}
 
-	*out = last_stream;
-	return 0;
+out:
+	if (error)
+		last_stream->close(last_stream);
+	else
+		*out = last_stream;
+
+	return error;
 }
 
 void stream_list_free(git_vector *streams)
@@ -943,12 +948,13 @@
 	git_vector filter_streams = GIT_VECTOR_INIT;
 	git_writestream *stream_start;
 	ssize_t readlen;
-	int fd = -1, error;
+	int fd = -1, error, initialized = 0;
 
 	if ((error = stream_list_init(
 			&stream_start, &filter_streams, filters, target)) < 0 ||
 		(error = git_path_join_unrooted(&abspath, path, base, NULL)) < 0)
 		goto done;
+	initialized = 1;
 
 	if ((fd = git_futils_open_ro(abspath.ptr)) < 0) {
 		error = fd;
@@ -960,13 +966,13 @@
 			goto done;
 	}
 
-	if (!readlen)
-		error = stream_start->close(stream_start);
-	else if (readlen < 0)
+	if (readlen < 0)
 		error = readlen;
 
-
 done:
+	if (initialized)
+		error |= stream_start->close(stream_start);
+
 	if (fd >= 0)
 		p_close(fd);
 	stream_list_free(&filter_streams);
@@ -981,20 +987,24 @@
 {
 	git_vector filter_streams = GIT_VECTOR_INIT;
 	git_writestream *stream_start;
-	int error = 0, close_error;
+	int error, initialized = 0;
 
 	git_buf_sanitize(data);
 
 	if ((error = stream_list_init(&stream_start, &filter_streams, filters, target)) < 0)
 		goto out;
+	initialized = 1;
 
-	error = stream_start->write(stream_start, data->ptr, data->size);
+	if ((error = stream_start->write(
+			stream_start, data->ptr, data->size)) < 0)
+		goto out;
 
 out:
-	close_error = stream_start->close(stream_start);
+	if (initialized)
+		error |= stream_start->close(stream_start);
+
 	stream_list_free(&filter_streams);
-	/* propagate the stream init or write error */
-	return error < 0 ? error : close_error;
+	return error;
 }
 
 int git_filter_list_stream_blob(
diff --git a/src/global.c b/src/global.c
index e2ad8fe..afa57e1 100644
--- a/src/global.c
+++ b/src/global.c
@@ -22,7 +22,7 @@
 
 git_mutex git__mwindow_mutex;
 
-#define MAX_SHUTDOWN_CB 8
+#define MAX_SHUTDOWN_CB 9
 
 static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
 static git_atomic git__n_shutdown_callbacks;
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..5fdae8d
--- /dev/null
+++ b/src/hash/hash_collisiondetect.h
@@ -0,0 +1,47 @@
+/*
+ * 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)
+{
+	assert(ctx);
+	SHA1DCUpdate(&ctx->c, data, 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..facea1b
--- /dev/null
+++ b/src/hash/sha1dc/sha1.c
@@ -0,0 +1,1856 @@
+/***
+* 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
+***/
+
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <string.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+#ifdef SHA1DC_CUSTOM_INCLUDE_SHA1_C
+#include SHA1DC_CUSTOM_INCLUDE_SHA1_C
+#endif
+
+#ifndef SHA1DC_INIT_SAFE_HASH_DEFAULT
+#define SHA1DC_INIT_SAFE_HASH_DEFAULT 1
+#endif
+
+#include "sha1.h"
+#include "ubc_check.h"
+
+
+/*
+   Because Little-Endian architectures are most common,
+   we only set SHA1DC_BIGENDIAN if one of these conditions is met.
+   Note that all MSFT platforms are little endian,
+   so none of these will be defined under the MSC compiler.
+   If you are compiling on a big endian platform and your compiler does not define one of these,
+   you will have to add whatever macros your tool chain defines to indicate Big-Endianness.
+ */
+#ifdef SHA1DC_BIGENDIAN
+#undef SHA1DC_BIGENDIAN
+#endif
+
+#if (defined(_BYTE_ORDER) || defined(__BYTE_ORDER) || defined(__BYTE_ORDER__))
+
+#if ((defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)) || \
+     (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || \
+     (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__)) )
+#define SHA1DC_BIGENDIAN
+#endif
+
+#else
+
+#if (defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN) || defined(__BIG_ENDIAN__) || \
+     defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \
+     defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || \
+     defined(__sparc))
+#define SHA1DC_BIGENDIAN
+#endif
+
+#endif
+
+#if (defined(SHA1DC_FORCE_LITTLEENDIAN) && defined(SHA1DC_BIGENDIAN))
+#undef SHA1DC_BIGENDIAN
+#endif
+#if (defined(SHA1DC_FORCE_BIGENDIAN) && !defined(SHA1DC_BIGENDIAN))
+#define SHA1DC_BIGENDIAN
+#endif
+/*ENDIANNESS SELECTION*/
+
+#if (defined SHA1DC_FORCE_UNALIGNED_ACCESS || \
+     defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || \
+     defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__)  || \
+     defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || \
+     defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || defined(__INTEL__) || \
+     defined(__386) || defined(_M_X64) || defined(_M_AMD64))
+
+#define SHA1DC_ALLOW_UNALIGNED_ACCESS
+
+#endif /*UNALIGNMENT DETECTION*/
+
+
+#define rotate_right(x,n) (((x)>>(n))|((x)<<(32-(n))))
+#define rotate_left(x,n)  (((x)<<(n))|((x)>>(32-(n))))
+
+#define sha1_bswap32(x) \
+	{x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0xFF00FF); x = (x << 16) | (x >> 16);}
+
+#define sha1_mix(W, t)  (rotate_left(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1))
+
+#ifdef SHA1DC_BIGENDIAN
+	#define sha1_load(m, t, temp)  { temp = m[t]; }
+#else
+	#define sha1_load(m, t, temp)  { temp = m[t]; sha1_bswap32(temp); }
+#endif
+
+#define sha1_store(W, t, x)	*(volatile uint32_t *)&W[t] = x
+
+#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)&((b)^(c))))
+#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 SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, t, temp) \
+	{sha1_load(m, t, temp); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999; b = rotate_left(b, 30);}
+
+#define SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(a, b, c, d, e, W, t, temp) \
+	{temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999; b = rotate_left(b, 30); }
+
+#define SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, t, temp) \
+	{temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1; b = rotate_left(b, 30); }
+
+#define SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, t, temp) \
+	{temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC; b = rotate_left(b, 30); }
+
+#define SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, t, temp) \
+	{temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6; b = rotate_left(b, 30); }
+
+
+#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;
+
+#ifdef BUILDNOCOLLDETECTSHA1COMPRESSION
+void sha1_compression(uint32_t ihv[5], const uint32_t m[16])
+{
+	uint32_t W[80];
+	uint32_t a,b,c,d,e;
+	unsigned i;
+
+	memcpy(W, m, 16 * 4);
+	for (i = 16; i < 80; ++i)
+		W[i] = sha1_mix(W, i);
+
+	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;
+}
+#endif /*BUILDNOCOLLDETECTSHA1COMPRESSION*/
+
+
+static 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 m[16], 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];
+	uint32_t temp;
+
+#ifdef DOSTORESTATE00
+	SHA1_STORE_STATE(0)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 0, temp);
+
+#ifdef DOSTORESTATE01
+	SHA1_STORE_STATE(1)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 1, temp);
+
+#ifdef DOSTORESTATE02
+	SHA1_STORE_STATE(2)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 2, temp);
+
+#ifdef DOSTORESTATE03
+	SHA1_STORE_STATE(3)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 3, temp);
+
+#ifdef DOSTORESTATE04
+	SHA1_STORE_STATE(4)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 4, temp);
+
+#ifdef DOSTORESTATE05
+	SHA1_STORE_STATE(5)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 5, temp);
+
+#ifdef DOSTORESTATE06
+	SHA1_STORE_STATE(6)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 6, temp);
+
+#ifdef DOSTORESTATE07
+	SHA1_STORE_STATE(7)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 7, temp);
+
+#ifdef DOSTORESTATE08
+	SHA1_STORE_STATE(8)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 8, temp);
+
+#ifdef DOSTORESTATE09
+	SHA1_STORE_STATE(9)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 9, temp);
+
+#ifdef DOSTORESTATE10
+	SHA1_STORE_STATE(10)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 10, temp);
+
+#ifdef DOSTORESTATE11
+	SHA1_STORE_STATE(11)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 11, temp);
+
+#ifdef DOSTORESTATE12
+	SHA1_STORE_STATE(12)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 12, temp);
+
+#ifdef DOSTORESTATE13
+	SHA1_STORE_STATE(13)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 13, temp);
+
+#ifdef DOSTORESTATE14
+	SHA1_STORE_STATE(14)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 14, temp);
+
+#ifdef DOSTORESTATE15
+	SHA1_STORE_STATE(15)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 15, temp);
+
+#ifdef DOSTORESTATE16
+	SHA1_STORE_STATE(16)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(e, a, b, c, d, W, 16, temp);
+
+#ifdef DOSTORESTATE17
+	SHA1_STORE_STATE(17)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(d, e, a, b, c, W, 17, temp);
+
+#ifdef DOSTORESTATE18
+	SHA1_STORE_STATE(18)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(c, d, e, a, b, W, 18, temp);
+
+#ifdef DOSTORESTATE19
+	SHA1_STORE_STATE(19)
+#endif
+	SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(b, c, d, e, a, W, 19, temp);
+
+
+
+#ifdef DOSTORESTATE20
+	SHA1_STORE_STATE(20)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 20, temp);
+
+#ifdef DOSTORESTATE21
+	SHA1_STORE_STATE(21)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 21, temp);
+
+#ifdef DOSTORESTATE22
+	SHA1_STORE_STATE(22)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 22, temp);
+
+#ifdef DOSTORESTATE23
+	SHA1_STORE_STATE(23)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 23, temp);
+
+#ifdef DOSTORESTATE24
+	SHA1_STORE_STATE(24)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 24, temp);
+
+#ifdef DOSTORESTATE25
+	SHA1_STORE_STATE(25)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 25, temp);
+
+#ifdef DOSTORESTATE26
+	SHA1_STORE_STATE(26)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 26, temp);
+
+#ifdef DOSTORESTATE27
+	SHA1_STORE_STATE(27)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 27, temp);
+
+#ifdef DOSTORESTATE28
+	SHA1_STORE_STATE(28)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 28, temp);
+
+#ifdef DOSTORESTATE29
+	SHA1_STORE_STATE(29)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 29, temp);
+
+#ifdef DOSTORESTATE30
+	SHA1_STORE_STATE(30)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 30, temp);
+
+#ifdef DOSTORESTATE31
+	SHA1_STORE_STATE(31)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 31, temp);
+
+#ifdef DOSTORESTATE32
+	SHA1_STORE_STATE(32)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 32, temp);
+
+#ifdef DOSTORESTATE33
+	SHA1_STORE_STATE(33)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 33, temp);
+
+#ifdef DOSTORESTATE34
+	SHA1_STORE_STATE(34)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 34, temp);
+
+#ifdef DOSTORESTATE35
+	SHA1_STORE_STATE(35)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 35, temp);
+
+#ifdef DOSTORESTATE36
+	SHA1_STORE_STATE(36)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 36, temp);
+
+#ifdef DOSTORESTATE37
+	SHA1_STORE_STATE(37)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 37, temp);
+
+#ifdef DOSTORESTATE38
+	SHA1_STORE_STATE(38)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 38, temp);
+
+#ifdef DOSTORESTATE39
+	SHA1_STORE_STATE(39)
+#endif
+	SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 39, temp);
+
+
+
+#ifdef DOSTORESTATE40
+	SHA1_STORE_STATE(40)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 40, temp);
+
+#ifdef DOSTORESTATE41
+	SHA1_STORE_STATE(41)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 41, temp);
+
+#ifdef DOSTORESTATE42
+	SHA1_STORE_STATE(42)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 42, temp);
+
+#ifdef DOSTORESTATE43
+	SHA1_STORE_STATE(43)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 43, temp);
+
+#ifdef DOSTORESTATE44
+	SHA1_STORE_STATE(44)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 44, temp);
+
+#ifdef DOSTORESTATE45
+	SHA1_STORE_STATE(45)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 45, temp);
+
+#ifdef DOSTORESTATE46
+	SHA1_STORE_STATE(46)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 46, temp);
+
+#ifdef DOSTORESTATE47
+	SHA1_STORE_STATE(47)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 47, temp);
+
+#ifdef DOSTORESTATE48
+	SHA1_STORE_STATE(48)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 48, temp);
+
+#ifdef DOSTORESTATE49
+	SHA1_STORE_STATE(49)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 49, temp);
+
+#ifdef DOSTORESTATE50
+	SHA1_STORE_STATE(50)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 50, temp);
+
+#ifdef DOSTORESTATE51
+	SHA1_STORE_STATE(51)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 51, temp);
+
+#ifdef DOSTORESTATE52
+	SHA1_STORE_STATE(52)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 52, temp);
+
+#ifdef DOSTORESTATE53
+	SHA1_STORE_STATE(53)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 53, temp);
+
+#ifdef DOSTORESTATE54
+	SHA1_STORE_STATE(54)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 54, temp);
+
+#ifdef DOSTORESTATE55
+	SHA1_STORE_STATE(55)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 55, temp);
+
+#ifdef DOSTORESTATE56
+	SHA1_STORE_STATE(56)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 56, temp);
+
+#ifdef DOSTORESTATE57
+	SHA1_STORE_STATE(57)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 57, temp);
+
+#ifdef DOSTORESTATE58
+	SHA1_STORE_STATE(58)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 58, temp);
+
+#ifdef DOSTORESTATE59
+	SHA1_STORE_STATE(59)
+#endif
+	SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 59, temp);
+
+
+
+
+#ifdef DOSTORESTATE60
+	SHA1_STORE_STATE(60)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 60, temp);
+
+#ifdef DOSTORESTATE61
+	SHA1_STORE_STATE(61)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 61, temp);
+
+#ifdef DOSTORESTATE62
+	SHA1_STORE_STATE(62)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 62, temp);
+
+#ifdef DOSTORESTATE63
+	SHA1_STORE_STATE(63)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 63, temp);
+
+#ifdef DOSTORESTATE64
+	SHA1_STORE_STATE(64)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 64, temp);
+
+#ifdef DOSTORESTATE65
+	SHA1_STORE_STATE(65)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 65, temp);
+
+#ifdef DOSTORESTATE66
+	SHA1_STORE_STATE(66)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 66, temp);
+
+#ifdef DOSTORESTATE67
+	SHA1_STORE_STATE(67)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 67, temp);
+
+#ifdef DOSTORESTATE68
+	SHA1_STORE_STATE(68)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 68, temp);
+
+#ifdef DOSTORESTATE69
+	SHA1_STORE_STATE(69)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 69, temp);
+
+#ifdef DOSTORESTATE70
+	SHA1_STORE_STATE(70)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 70, temp);
+
+#ifdef DOSTORESTATE71
+	SHA1_STORE_STATE(71)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 71, temp);
+
+#ifdef DOSTORESTATE72
+	SHA1_STORE_STATE(72)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 72, temp);
+
+#ifdef DOSTORESTATE73
+	SHA1_STORE_STATE(73)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 73, temp);
+
+#ifdef DOSTORESTATE74
+	SHA1_STORE_STATE(74)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 74, temp);
+
+#ifdef DOSTORESTATE75
+	SHA1_STORE_STATE(75)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 75, temp);
+
+#ifdef DOSTORESTATE76
+	SHA1_STORE_STATE(76)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 76, temp);
+
+#ifdef DOSTORESTATE77
+	SHA1_STORE_STATE(77)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 77, temp);
+
+#ifdef DOSTORESTATE78
+	SHA1_STORE_STATE(78)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 78, temp);
+
+#ifdef DOSTORESTATE79
+	SHA1_STORE_STATE(79)
+#endif
+	SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 79, temp);
+
+
+
+	ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e;
+}
+
+
+
+
+#define SHA1_RECOMPRESS(t) \
+static 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; \
+}
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4127)  /* Complier complains about the checks in the above macro being constant. */
+#endif
+
+#ifdef DOSTORESTATE0
+SHA1_RECOMPRESS(0)
+#endif
+
+#ifdef DOSTORESTATE1
+SHA1_RECOMPRESS(1)
+#endif
+
+#ifdef DOSTORESTATE2
+SHA1_RECOMPRESS(2)
+#endif
+
+#ifdef DOSTORESTATE3
+SHA1_RECOMPRESS(3)
+#endif
+
+#ifdef DOSTORESTATE4
+SHA1_RECOMPRESS(4)
+#endif
+
+#ifdef DOSTORESTATE5
+SHA1_RECOMPRESS(5)
+#endif
+
+#ifdef DOSTORESTATE6
+SHA1_RECOMPRESS(6)
+#endif
+
+#ifdef DOSTORESTATE7
+SHA1_RECOMPRESS(7)
+#endif
+
+#ifdef DOSTORESTATE8
+SHA1_RECOMPRESS(8)
+#endif
+
+#ifdef DOSTORESTATE9
+SHA1_RECOMPRESS(9)
+#endif
+
+#ifdef DOSTORESTATE10
+SHA1_RECOMPRESS(10)
+#endif
+
+#ifdef DOSTORESTATE11
+SHA1_RECOMPRESS(11)
+#endif
+
+#ifdef DOSTORESTATE12
+SHA1_RECOMPRESS(12)
+#endif
+
+#ifdef DOSTORESTATE13
+SHA1_RECOMPRESS(13)
+#endif
+
+#ifdef DOSTORESTATE14
+SHA1_RECOMPRESS(14)
+#endif
+
+#ifdef DOSTORESTATE15
+SHA1_RECOMPRESS(15)
+#endif
+
+#ifdef DOSTORESTATE16
+SHA1_RECOMPRESS(16)
+#endif
+
+#ifdef DOSTORESTATE17
+SHA1_RECOMPRESS(17)
+#endif
+
+#ifdef DOSTORESTATE18
+SHA1_RECOMPRESS(18)
+#endif
+
+#ifdef DOSTORESTATE19
+SHA1_RECOMPRESS(19)
+#endif
+
+#ifdef DOSTORESTATE20
+SHA1_RECOMPRESS(20)
+#endif
+
+#ifdef DOSTORESTATE21
+SHA1_RECOMPRESS(21)
+#endif
+
+#ifdef DOSTORESTATE22
+SHA1_RECOMPRESS(22)
+#endif
+
+#ifdef DOSTORESTATE23
+SHA1_RECOMPRESS(23)
+#endif
+
+#ifdef DOSTORESTATE24
+SHA1_RECOMPRESS(24)
+#endif
+
+#ifdef DOSTORESTATE25
+SHA1_RECOMPRESS(25)
+#endif
+
+#ifdef DOSTORESTATE26
+SHA1_RECOMPRESS(26)
+#endif
+
+#ifdef DOSTORESTATE27
+SHA1_RECOMPRESS(27)
+#endif
+
+#ifdef DOSTORESTATE28
+SHA1_RECOMPRESS(28)
+#endif
+
+#ifdef DOSTORESTATE29
+SHA1_RECOMPRESS(29)
+#endif
+
+#ifdef DOSTORESTATE30
+SHA1_RECOMPRESS(30)
+#endif
+
+#ifdef DOSTORESTATE31
+SHA1_RECOMPRESS(31)
+#endif
+
+#ifdef DOSTORESTATE32
+SHA1_RECOMPRESS(32)
+#endif
+
+#ifdef DOSTORESTATE33
+SHA1_RECOMPRESS(33)
+#endif
+
+#ifdef DOSTORESTATE34
+SHA1_RECOMPRESS(34)
+#endif
+
+#ifdef DOSTORESTATE35
+SHA1_RECOMPRESS(35)
+#endif
+
+#ifdef DOSTORESTATE36
+SHA1_RECOMPRESS(36)
+#endif
+
+#ifdef DOSTORESTATE37
+SHA1_RECOMPRESS(37)
+#endif
+
+#ifdef DOSTORESTATE38
+SHA1_RECOMPRESS(38)
+#endif
+
+#ifdef DOSTORESTATE39
+SHA1_RECOMPRESS(39)
+#endif
+
+#ifdef DOSTORESTATE40
+SHA1_RECOMPRESS(40)
+#endif
+
+#ifdef DOSTORESTATE41
+SHA1_RECOMPRESS(41)
+#endif
+
+#ifdef DOSTORESTATE42
+SHA1_RECOMPRESS(42)
+#endif
+
+#ifdef DOSTORESTATE43
+SHA1_RECOMPRESS(43)
+#endif
+
+#ifdef DOSTORESTATE44
+SHA1_RECOMPRESS(44)
+#endif
+
+#ifdef DOSTORESTATE45
+SHA1_RECOMPRESS(45)
+#endif
+
+#ifdef DOSTORESTATE46
+SHA1_RECOMPRESS(46)
+#endif
+
+#ifdef DOSTORESTATE47
+SHA1_RECOMPRESS(47)
+#endif
+
+#ifdef DOSTORESTATE48
+SHA1_RECOMPRESS(48)
+#endif
+
+#ifdef DOSTORESTATE49
+SHA1_RECOMPRESS(49)
+#endif
+
+#ifdef DOSTORESTATE50
+SHA1_RECOMPRESS(50)
+#endif
+
+#ifdef DOSTORESTATE51
+SHA1_RECOMPRESS(51)
+#endif
+
+#ifdef DOSTORESTATE52
+SHA1_RECOMPRESS(52)
+#endif
+
+#ifdef DOSTORESTATE53
+SHA1_RECOMPRESS(53)
+#endif
+
+#ifdef DOSTORESTATE54
+SHA1_RECOMPRESS(54)
+#endif
+
+#ifdef DOSTORESTATE55
+SHA1_RECOMPRESS(55)
+#endif
+
+#ifdef DOSTORESTATE56
+SHA1_RECOMPRESS(56)
+#endif
+
+#ifdef DOSTORESTATE57
+SHA1_RECOMPRESS(57)
+#endif
+
+#ifdef DOSTORESTATE58
+SHA1_RECOMPRESS(58)
+#endif
+
+#ifdef DOSTORESTATE59
+SHA1_RECOMPRESS(59)
+#endif
+
+#ifdef DOSTORESTATE60
+SHA1_RECOMPRESS(60)
+#endif
+
+#ifdef DOSTORESTATE61
+SHA1_RECOMPRESS(61)
+#endif
+
+#ifdef DOSTORESTATE62
+SHA1_RECOMPRESS(62)
+#endif
+
+#ifdef DOSTORESTATE63
+SHA1_RECOMPRESS(63)
+#endif
+
+#ifdef DOSTORESTATE64
+SHA1_RECOMPRESS(64)
+#endif
+
+#ifdef DOSTORESTATE65
+SHA1_RECOMPRESS(65)
+#endif
+
+#ifdef DOSTORESTATE66
+SHA1_RECOMPRESS(66)
+#endif
+
+#ifdef DOSTORESTATE67
+SHA1_RECOMPRESS(67)
+#endif
+
+#ifdef DOSTORESTATE68
+SHA1_RECOMPRESS(68)
+#endif
+
+#ifdef DOSTORESTATE69
+SHA1_RECOMPRESS(69)
+#endif
+
+#ifdef DOSTORESTATE70
+SHA1_RECOMPRESS(70)
+#endif
+
+#ifdef DOSTORESTATE71
+SHA1_RECOMPRESS(71)
+#endif
+
+#ifdef DOSTORESTATE72
+SHA1_RECOMPRESS(72)
+#endif
+
+#ifdef DOSTORESTATE73
+SHA1_RECOMPRESS(73)
+#endif
+
+#ifdef DOSTORESTATE74
+SHA1_RECOMPRESS(74)
+#endif
+
+#ifdef DOSTORESTATE75
+SHA1_RECOMPRESS(75)
+#endif
+
+#ifdef DOSTORESTATE76
+SHA1_RECOMPRESS(76)
+#endif
+
+#ifdef DOSTORESTATE77
+SHA1_RECOMPRESS(77)
+#endif
+
+#ifdef DOSTORESTATE78
+SHA1_RECOMPRESS(78)
+#endif
+
+#ifdef DOSTORESTATE79
+SHA1_RECOMPRESS(79)
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+static void sha1_recompression_step(uint32_t step, uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5])
+{
+	switch (step)
+	{
+#ifdef DOSTORESTATE0
+	case 0:
+		sha1recompress_fast_0(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE1
+	case 1:
+		sha1recompress_fast_1(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE2
+	case 2:
+		sha1recompress_fast_2(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE3
+	case 3:
+		sha1recompress_fast_3(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE4
+	case 4:
+		sha1recompress_fast_4(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE5
+	case 5:
+		sha1recompress_fast_5(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE6
+	case 6:
+		sha1recompress_fast_6(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE7
+	case 7:
+		sha1recompress_fast_7(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE8
+	case 8:
+		sha1recompress_fast_8(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE9
+	case 9:
+		sha1recompress_fast_9(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE10
+	case 10:
+		sha1recompress_fast_10(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE11
+	case 11:
+		sha1recompress_fast_11(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE12
+	case 12:
+		sha1recompress_fast_12(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE13
+	case 13:
+		sha1recompress_fast_13(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE14
+	case 14:
+		sha1recompress_fast_14(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE15
+	case 15:
+		sha1recompress_fast_15(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE16
+	case 16:
+		sha1recompress_fast_16(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE17
+	case 17:
+		sha1recompress_fast_17(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE18
+	case 18:
+		sha1recompress_fast_18(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE19
+	case 19:
+		sha1recompress_fast_19(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE20
+	case 20:
+		sha1recompress_fast_20(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE21
+	case 21:
+		sha1recompress_fast_21(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE22
+	case 22:
+		sha1recompress_fast_22(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE23
+	case 23:
+		sha1recompress_fast_23(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE24
+	case 24:
+		sha1recompress_fast_24(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE25
+	case 25:
+		sha1recompress_fast_25(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE26
+	case 26:
+		sha1recompress_fast_26(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE27
+	case 27:
+		sha1recompress_fast_27(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE28
+	case 28:
+		sha1recompress_fast_28(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE29
+	case 29:
+		sha1recompress_fast_29(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE30
+	case 30:
+		sha1recompress_fast_30(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE31
+	case 31:
+		sha1recompress_fast_31(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE32
+	case 32:
+		sha1recompress_fast_32(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE33
+	case 33:
+		sha1recompress_fast_33(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE34
+	case 34:
+		sha1recompress_fast_34(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE35
+	case 35:
+		sha1recompress_fast_35(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE36
+	case 36:
+		sha1recompress_fast_36(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE37
+	case 37:
+		sha1recompress_fast_37(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE38
+	case 38:
+		sha1recompress_fast_38(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE39
+	case 39:
+		sha1recompress_fast_39(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE40
+	case 40:
+		sha1recompress_fast_40(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE41
+	case 41:
+		sha1recompress_fast_41(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE42
+	case 42:
+		sha1recompress_fast_42(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE43
+	case 43:
+		sha1recompress_fast_43(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE44
+	case 44:
+		sha1recompress_fast_44(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE45
+	case 45:
+		sha1recompress_fast_45(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE46
+	case 46:
+		sha1recompress_fast_46(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE47
+	case 47:
+		sha1recompress_fast_47(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE48
+	case 48:
+		sha1recompress_fast_48(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE49
+	case 49:
+		sha1recompress_fast_49(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE50
+	case 50:
+		sha1recompress_fast_50(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE51
+	case 51:
+		sha1recompress_fast_51(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE52
+	case 52:
+		sha1recompress_fast_52(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE53
+	case 53:
+		sha1recompress_fast_53(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE54
+	case 54:
+		sha1recompress_fast_54(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE55
+	case 55:
+		sha1recompress_fast_55(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE56
+	case 56:
+		sha1recompress_fast_56(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE57
+	case 57:
+		sha1recompress_fast_57(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE58
+	case 58:
+		sha1recompress_fast_58(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE59
+	case 59:
+		sha1recompress_fast_59(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE60
+	case 60:
+		sha1recompress_fast_60(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE61
+	case 61:
+		sha1recompress_fast_61(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE62
+	case 62:
+		sha1recompress_fast_62(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE63
+	case 63:
+		sha1recompress_fast_63(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE64
+	case 64:
+		sha1recompress_fast_64(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE65
+	case 65:
+		sha1recompress_fast_65(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE66
+	case 66:
+		sha1recompress_fast_66(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE67
+	case 67:
+		sha1recompress_fast_67(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE68
+	case 68:
+		sha1recompress_fast_68(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE69
+	case 69:
+		sha1recompress_fast_69(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE70
+	case 70:
+		sha1recompress_fast_70(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE71
+	case 71:
+		sha1recompress_fast_71(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE72
+	case 72:
+		sha1recompress_fast_72(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE73
+	case 73:
+		sha1recompress_fast_73(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE74
+	case 74:
+		sha1recompress_fast_74(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE75
+	case 75:
+		sha1recompress_fast_75(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE76
+	case 76:
+		sha1recompress_fast_76(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE77
+	case 77:
+		sha1recompress_fast_77(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE78
+	case 78:
+		sha1recompress_fast_78(ihvin, ihvout, me2, state);
+		break;
+#endif
+#ifdef DOSTORESTATE79
+	case 79:
+		sha1recompress_fast_79(ihvin, ihvout, me2, state);
+		break;
+#endif
+	default:
+		abort();
+	}
+
+}
+
+
+
+static void sha1_process(SHA1_CTX* ctx, const uint32_t block[16])
+{
+	unsigned i, j;
+	uint32_t ubc_dv_mask[DVMASKSIZE] = { 0xFFFFFFFF };
+	uint32_t ihvtmp[5];
+
+	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];
+
+	sha1_compression_states(ctx->ihv, block, ctx->m1, ctx->states);
+
+	if (ctx->detect_coll)
+	{
+		if (ctx->ubc_check)
+		{
+			ubc_check(ctx->m1, ubc_dv_mask);
+		}
+
+		if (ubc_dv_mask[0] != 0)
+		{
+			for (i = 0; sha1_dvs[i].dvType != 0; ++i)
+			{
+				if (ubc_dv_mask[0] & ((uint32_t)(1) << sha1_dvs[i].maskb))
+				{
+					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 ((0 == ((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 && 0==((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;
+
+						if (ctx->safe_hash)
+						{
+							sha1_compression_W(ctx->ihv, ctx->m1);
+							sha1_compression_W(ctx->ihv, ctx->m1);
+						}
+
+						break;
+					}
+				}
+			}
+		}
+	}
+}
+
+void SHA1DCInit(SHA1_CTX* ctx)
+{
+	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 = SHA1DC_INIT_SAFE_HASH_DEFAULT;
+	ctx->ubc_check = 1;
+	ctx->detect_coll = 1;
+	ctx->reduced_round_coll = 0;
+	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, size_t 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);
+		sha1_process(ctx, (uint32_t*)(ctx->buffer));
+		buf += fill;
+		len -= fill;
+		left = 0;
+	}
+	while (len >= 64)
+	{
+		ctx->total += 64;
+
+#if defined(SHA1DC_ALLOW_UNALIGNED_ACCESS)
+		sha1_process(ctx, (uint32_t*)(buf));
+#else
+		memcpy(ctx->buffer, buf, 64);
+		sha1_process(ctx, (uint32_t*)(ctx->buffer));
+#endif /* defined(SHA1DC_ALLOW_UNALIGNED_ACCESS) */
+		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);
+	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;
+}
+
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C
+#endif
diff --git a/src/hash/sha1dc/sha1.h b/src/hash/sha1dc/sha1.h
new file mode 100644
index 0000000..1e4e94b
--- /dev/null
+++ b/src/hash/sha1dc/sha1.h
@@ -0,0 +1,110 @@
+/***
+* 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
+***/
+
+#ifndef SHA1DC_SHA1_H
+#define SHA1DC_SHA1_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <stdint.h>
+#endif
+
+/* sha-1 compression function that takes an already expanded message, and 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[5], const uint32_t[16], uint32_t[80], uint32_t[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*);
+
+/* 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 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 be 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*, int);
+
+/* 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*, size_t);
+
+/* 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*);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H
+#endif
+
+#endif
diff --git a/src/hash/sha1dc/ubc_check.c b/src/hash/sha1dc/ubc_check.c
new file mode 100644
index 0000000..b3beff2
--- /dev/null
+++ b/src/hash/sha1dc/ubc_check.c
@@ -0,0 +1,372 @@
+/***
+* 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
+*/
+
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <stdint.h>
+#endif
+#ifdef SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C
+#include SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C
+#endif
+#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;
+}
+
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C
+#endif
diff --git a/src/hash/sha1dc/ubc_check.h b/src/hash/sha1dc/ubc_check.h
new file mode 100644
index 0000000..d7e17dc
--- /dev/null
+++ b/src/hash/sha1dc/ubc_check.h
@@ -0,0 +1,52 @@
+/***
+* 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 SHA1DC_UBC_CHECK_H
+#define SHA1DC_UBC_CHECK_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <stdint.h>
+#endif
+
+#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
+
+#define CHECK_DVMASK(_DVMASK) (0 != _DVMASK[0])
+
+#if defined(__cplusplus)
+}
+#endif
+
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H
+#endif
+
+#endif
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 cc9e08e..c324d4d 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -277,6 +277,7 @@
 {
 	int error = 0;
 	const char *workdir = git_repository_workdir(repo);
+	git_buf infopath = GIT_BUF_INIT;
 
 	assert(repo && ignores && path);
 
@@ -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);
 
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 f27fa16..c29e90f 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)); \
@@ -57,10 +54,6 @@
 				  unsigned int flags,
 				  git_index_matched_path_cb cb, void *payload);
 
-#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7)
-#define short_entry_size(len) entry_size(struct entry_short, len)
-#define long_entry_size(len) entry_size(struct entry_long, len)
-
 #define minimal_entry_size (offsetof(struct entry_short, path))
 
 static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ;
@@ -1365,7 +1358,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);
 		}
 	}
 
@@ -1592,7 +1585,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;
 	}
@@ -2285,12 +2278,29 @@
 	return 0;
 }
 
+static size_t index_entry_size(size_t path_len, size_t varint_len, uint32_t flags)
+{
+	if (varint_len) {
+		if (flags & GIT_IDXENTRY_EXTENDED)
+			return offsetof(struct entry_long, path) + path_len + 1 + varint_len;
+		else
+			return offsetof(struct entry_short, path) + path_len + 1 + varint_len;
+	} else {
+#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7)
+		if (flags & GIT_IDXENTRY_EXTENDED)
+			return entry_size(struct entry_long, path_len);
+		else
+			return entry_size(struct entry_short, path_len);
+#undef entry_size
+	}
+}
+
 static size_t read_entry(
 	git_index_entry **out,
 	git_index *index,
 	const void *buffer,
 	size_t buffer_size,
-	const char **last)
+	const char *last)
 {
 	size_t path_length, entry_size;
 	const char *path_ptr;
@@ -2347,35 +2357,34 @@
 			path_length = path_end - path_ptr;
 		}
 
-		if (entry.flags & GIT_IDXENTRY_EXTENDED)
-			entry_size = long_entry_size(path_length);
-		else
-			entry_size = short_entry_size(path_length);
-
-		if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
-			return 0;
-
+		entry_size = index_entry_size(path_length, 0, entry.flags);
 		entry.path = (char *)path_ptr;
 	} else {
 		size_t varint_len;
-		size_t shared = git_decode_varint((const unsigned char *)path_ptr, 
-						  &varint_len);
-		size_t len = strlen(path_ptr + varint_len);
-		size_t last_len = strlen(*last);
-		size_t tmp_path_len;
+		size_t strip_len = git_decode_varint((const unsigned char *)path_ptr,
+						     &varint_len);
+		size_t last_len = strlen(last);
+		size_t prefix_len = last_len - strip_len;
+		size_t suffix_len = strlen(path_ptr + varint_len);
+		size_t path_len;
 
 		if (varint_len == 0)
 			return index_error_invalid("incorrect prefix length");
 
-		GITERR_CHECK_ALLOC_ADD(&tmp_path_len, shared, len + 1);
-		tmp_path = git__malloc(tmp_path_len);
+		GITERR_CHECK_ALLOC_ADD(&path_len, prefix_len, suffix_len);
+		GITERR_CHECK_ALLOC_ADD(&path_len, path_len, 1);
+		tmp_path = git__malloc(path_len);
 		GITERR_CHECK_ALLOC(tmp_path);
-		memcpy(tmp_path, last, last_len);
-		memcpy(tmp_path + last_len, path_ptr + varint_len, len);
-		entry_size = long_entry_size(shared + len);
+
+		memcpy(tmp_path, last, prefix_len);
+		memcpy(tmp_path + prefix_len, path_ptr + varint_len, suffix_len + 1);
+		entry_size = index_entry_size(suffix_len, varint_len, entry.flags);
 		entry.path = tmp_path;
 	}
 
+	if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
+		return 0;
+
 	if (index_entry_dup(out, index, &entry) < 0) {
 		git__free(tmp_path);
 		return 0;
@@ -2448,7 +2457,7 @@
 	unsigned int i;
 	struct index_header header = { 0 };
 	git_oid checksum_calculated, checksum_expected;
-	const char **last = NULL;
+	const char *last = NULL;
 	const char *empty = "";
 
 #define seek_forward(_increase) { \
@@ -2472,16 +2481,16 @@
 
 	index->version = header.version;
 	if (index->version >= INDEX_VERSION_NUMBER_COMP)
-		last = &empty;
+		last = empty;
 
 	seek_forward(INDEX_HEADER_SIZE);
 
 	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 +2508,7 @@
 			goto done;
 		}
 
-		INSERT_IN_MAP(index, entry, error);
+		INSERT_IN_MAP(index, entry, &error);
 
 		if (error < 0) {
 			index_entry_free(entry);
@@ -2507,6 +2516,9 @@
 		}
 		error = 0;
 
+		if (index->version >= INDEX_VERSION_NUMBER_COMP)
+			last = entry->path;
+
 		seek_forward(entry_size);
 	}
 
@@ -2577,11 +2589,12 @@
 	return (extended > 0);
 }
 
-static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const char **last)
+static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const char *last)
 {
 	void *mem = NULL;
 	struct entry_short *ondisk;
 	size_t path_len, disk_size;
+	int varint_len = 0;
 	char *path;
 	const char *path_start = entry->path;
 	size_t same_len = 0;
@@ -2589,7 +2602,7 @@
 	path_len = ((struct entry_internal *)entry)->pathlen;
 
 	if (last) {
-		const char *last_c = *last;
+		const char *last_c = last;
 
 		while (*path_start == *last_c) {
 			if (!*path_start || !*last_c)
@@ -2599,13 +2612,10 @@
 			++same_len;
 		}
 		path_len -= same_len;
-		*last = entry->path;
+		varint_len = git_encode_varint(NULL, 0, same_len);
 	}
 
-	if (entry->flags & GIT_IDXENTRY_EXTENDED)
-		disk_size = long_entry_size(path_len);
-	else
-		disk_size = short_entry_size(path_len);
+	disk_size = index_entry_size(path_len, varint_len, entry->flags);
 
 	if (git_filebuf_reserve(file, &mem, disk_size) < 0)
 		return -1;
@@ -2645,16 +2655,34 @@
 		ondisk_ext->flags_extended = htons(entry->flags_extended &
 			GIT_IDXENTRY_EXTENDED_FLAGS);
 		path = ondisk_ext->path;
-	}
-	else
+		disk_size -= offsetof(struct entry_long, path);
+	} else {
 		path = ondisk->path;
+		disk_size -= offsetof(struct entry_short, path);
+	}
 
 	if (last) {
-		path += git_encode_varint((unsigned char *) path,
-					  disk_size,
-					  path_len - same_len);
+		varint_len = git_encode_varint((unsigned char *) path,
+					  disk_size, same_len);
+		assert(varint_len > 0);
+		path += varint_len;
+		disk_size -= varint_len;
+
+		/*
+		 * If using path compression, we are not allowed
+		 * to have additional trailing NULs.
+		 */
+		assert(disk_size == path_len + 1);
+	} else {
+		/*
+		 * If no path compression is used, we do have
+		 * NULs as padding. As such, simply assert that
+		 * we have enough space left to write the path.
+		 */
+		assert(disk_size > path_len);
 	}
-	memcpy(path, path_start, path_len);
+
+	memcpy(path, path_start, path_len + 1);
 
 	return 0;
 }
@@ -2665,8 +2693,7 @@
 	size_t i;
 	git_vector case_sorted, *entries;
 	git_index_entry *entry;
-	const char **last = NULL;
-	const char *empty = "";
+	const char *last = NULL;
 
 	/* If index->entries is sorted case-insensitively, then we need
 	 * to re-sort it case-sensitively before writing */
@@ -2679,11 +2706,14 @@
 	}
 
 	if (index->version >= INDEX_VERSION_NUMBER_COMP)
-		last = &empty;
+		last = "";
 
-	git_vector_foreach(entries, i, entry)
+	git_vector_foreach(entries, i, entry) {
 		if ((error = write_disk_entry(file, entry, last)) < 0)
 			break;
+		if (index->version >= INDEX_VERSION_NUMBER_COMP)
+			last = entry->path;
+	}
 
 	if (index->ignore_case)
 		git_vector_free(&case_sorted);
@@ -2979,12 +3009,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 +3067,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 +3133,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)
diff --git a/src/indexer.c b/src/indexer.c
index 0ad7869..15f6cc2 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;
 
@@ -35,7 +34,8 @@
 	unsigned int parsed_header :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;
@@ -125,6 +125,9 @@
 	git_hash_ctx_init(&idx->hash_ctx);
 	git_hash_ctx_init(&idx->trailer);
 
+	if (git_repository__fsync_gitdir)
+		idx->do_fsync = 1;
+
 	error = git_buf_joinpath(&path, prefix, suff);
 	if (error < 0)
 		goto cleanup;
@@ -163,6 +166,11 @@
 	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)
 {
@@ -294,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();
@@ -308,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);
 
@@ -333,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)
@@ -351,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)
@@ -993,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 */
@@ -1068,6 +1076,11 @@
 		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");
@@ -1080,7 +1093,14 @@
 		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);
@@ -1102,8 +1122,9 @@
 
 	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);
 	}
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/merge.c b/src/merge.c
index 087178a..6e00b5a 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -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;
 
@@ -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 */
@@ -2018,6 +2018,26 @@
 	git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
 	int error;
 
+	assert(out && repo);
+
+	/* if one side is treesame to the ancestor, take the other side */
+	if (ancestor_tree && merge_opts && (merge_opts->flags & GIT_MERGE_SKIP_REUC)) {
+		const git_tree *result = NULL;
+		const git_oid *ancestor_tree_id = git_tree_id(ancestor_tree);
+
+		if (our_tree && !git_oid_cmp(ancestor_tree_id, git_tree_id(our_tree)))
+			result = their_tree;
+		else if (their_tree && !git_oid_cmp(ancestor_tree_id, git_tree_id(their_tree)))
+			result = our_tree;
+
+		if (result) {
+			if ((error = git_index_new(out)) == 0)
+    			error = git_index_read_tree(*out, result);
+
+			return error;
+		}
+	}
+
 	iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
 
 	if ((error = git_iterator_for_tree(
@@ -2277,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;
 
@@ -2305,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;
 
@@ -2536,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;
@@ -2914,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;
 
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/mwindow.c b/src/mwindow.c
index 520e768..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) {
diff --git a/src/odb.c b/src/odb.c
index dc98a6f..b66324f 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -31,6 +31,8 @@
 
 #define GIT_ALTERNATES_MAX_DEPTH 5
 
+bool git_odb__strict_hash_verification = true;
+
 typedef struct
 {
 	git_odb_backend *backend;
@@ -496,7 +498,7 @@
 	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)
 {
@@ -531,7 +533,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 +588,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 +600,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 +614,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 +623,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 +715,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);
 
@@ -980,7 +1000,9 @@
 	size_t i;
 	git_rawobj raw;
 	git_odb_object *object;
+	git_oid hashed;
 	bool found = false;
+	int error = 0;
 
 	if (!only_refreshed && odb_read_hardcoded(&raw, id) == 0)
 		found = true;
@@ -993,7 +1015,7 @@
 			continue;
 
 		if (b->read != NULL) {
-			int error = b->read(&raw.data, &raw.len, &raw.type, b, id);
+			error = b->read(&raw.data, &raw.len, &raw.type, b, id);
 			if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND)
 				continue;
 
@@ -1007,12 +1029,26 @@
 	if (!found)
 		return GIT_ENOTFOUND;
 
+	if (git_odb__strict_hash_verification) {
+		if ((error = git_odb_hash(&hashed, raw.data, raw.len, raw.type)) < 0)
+			goto out;
+
+		if (!git_oid_equal(id, &hashed)) {
+			error = git_odb__error_mismatch(id, &hashed);
+			goto out;
+		}
+	}
+
 	giterr_clear();
 	if ((object = odb_object__alloc(id, &raw)) == NULL)
-		return -1;
+		goto out;
 
 	*out = git_cache_store_raw(odb_cache(db), object);
-	return 0;
+
+out:
+	if (error)
+		git__free(raw.data);
+	return error;
 }
 
 int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
@@ -1063,9 +1099,9 @@
 		const git_oid *key, size_t len, bool only_refreshed)
 {
 	size_t i;
-	int error = GIT_ENOTFOUND;
+	int error = 0;
 	git_oid found_full_oid = {{0}};
-	git_rawobj raw;
+	git_rawobj raw = {0};
 	void *data = NULL;
 	bool found = false;
 	git_odb_object *object;
@@ -1084,14 +1120,22 @@
 				continue;
 
 			if (error)
-				return error;
+				goto out;
 
 			git__free(data);
 			data = raw.data;
 
 			if (found && git_oid__cmp(&full_oid, &found_full_oid)) {
-				git__free(raw.data);
-				return git_odb__error_ambiguous("multiple matches for prefix");
+				git_buf buf = GIT_BUF_INIT;
+
+				git_buf_printf(&buf, "multiple matches for prefix: %s",
+					git_oid_tostr_s(&full_oid));
+				git_buf_printf(&buf, " %s",
+					git_oid_tostr_s(&found_full_oid));
+
+				error = git_odb__error_ambiguous(buf.ptr);
+				git_buf_free(&buf);
+				goto out;
 			}
 
 			found_full_oid = full_oid;
@@ -1102,11 +1146,28 @@
 	if (!found)
 		return GIT_ENOTFOUND;
 
+	if (git_odb__strict_hash_verification) {
+		git_oid hash;
+
+		if ((error = git_odb_hash(&hash, raw.data, raw.len, raw.type)) < 0)
+			goto out;
+
+		if (!git_oid_equal(&found_full_oid, &hash)) {
+			error = git_odb__error_mismatch(&found_full_oid, &hash);
+			goto out;
+		}
+	}
+
 	if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL)
-		return -1;
+		goto out;
 
 	*out = git_cache_store_raw(odb_cache(db), object);
-	return 0;
+
+out:
+	if (error)
+		git__free(raw.data);
+
+	return error;
 }
 
 int git_odb_read_prefix(
@@ -1167,7 +1228,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) {
@@ -1265,9 +1326,9 @@
 {
 	giterr_set(GITERR_ODB,
 		"cannot %s - "
-		"Invalid length. %"PRIuZ" was expected. The "
-		"total size of the received chunks amounts to %"PRIuZ".",
-		action, stream->declared_size, stream->received_bytes);		
+		"Invalid length. %"PRIdZ" was expected. The "
+		"total size of the received chunks amounts to %"PRIdZ".",
+		action, stream->declared_size, stream->received_bytes);
 
 	return -1;
 }
@@ -1293,7 +1354,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);
@@ -1393,6 +1454,19 @@
 	return 0;
 }
 
+int git_odb__error_mismatch(const git_oid *expected, const git_oid *actual)
+{
+	char expected_oid[GIT_OID_HEXSZ + 1], actual_oid[GIT_OID_HEXSZ + 1];
+
+	git_oid_tostr(expected_oid, sizeof(expected_oid), expected);
+	git_oid_tostr(actual_oid, sizeof(actual_oid), actual);
+
+	giterr_set(GITERR_ODB, "object hash mismatch - expected %s but got %s",
+		expected_oid, actual_oid);
+
+	return GIT_EMISMATCH;
+}
+
 int git_odb__error_notfound(
 	const char *message, const git_oid *oid, size_t oid_len)
 {
diff --git a/src/odb.h b/src/odb.h
index 31a9fd1..61d687a 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -20,6 +20,8 @@
 #define GIT_OBJECT_DIR_MODE 0777
 #define GIT_OBJECT_FILE_MODE 0444
 
+extern bool git_odb__strict_hash_verification;
+
 /* DO NOT EXPORT */
 typedef struct {
 	void *data;			/**< Raw, decompressed object data. */
@@ -38,8 +40,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
@@ -79,6 +98,12 @@
  */
 int git_odb__hashlink(git_oid *out, const char *path);
 
+/**
+ * Generate a GIT_EMISMATCH error for the ODB.
+ */
+int git_odb__error_mismatch(
+	const git_oid *expected, const git_oid *actual);
+
 /*
  * Generate a GIT_ENOTFOUND error for the ODB.
  */
@@ -98,6 +123,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 b2e2f1f..99fdcb4 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"
@@ -204,6 +205,11 @@
 	return inflate(s, 0);
 }
 
+static void abort_inflate(z_stream *s)
+{
+	inflateEnd(s);
+}
+
 static int finish_inflate(z_stream *s)
 {
 	int status = Z_OK;
@@ -366,6 +372,7 @@
 		(used = get_object_header(&hdr, head)) == 0 ||
 		!git_object_typeisloose(hdr.type))
 	{
+		abort_inflate(&zs);
 		giterr_set(GITERR_ODB, "failed to inflate disk object");
 		return -1;
 	}
@@ -838,6 +845,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_repository__fsync_gitdir)
+		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 +882,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 +910,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 84ed9c1..d6f2fb4 100644
--- a/src/odb_mempack.c
+++ b/src/odb_mempack.c
@@ -18,8 +18,6 @@
 #include "git2/types.h"
 #include "git2/pack.h"
 
-GIT__USE_OIDMAP
-
 struct memobject {
 	git_oid oid;
 	size_t len;
@@ -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);
 	});
 
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/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 bb9b32c..759c501 100644
--- a/src/openssl_stream.c
+++ b/src/openssl_stream.c
@@ -37,7 +37,7 @@
 
 #define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
 
-#ifdef GIT_THREADS
+#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
 
 static git_mutex *openssl_locks;
 
@@ -66,11 +66,11 @@
 	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 */
+#endif /* GIT_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L */
 
 static BIO_METHOD *git_stream_bio_method;
 static int init_bio_method(void);
@@ -103,8 +103,13 @@
 	ssl_opts |= SSL_OP_NO_COMPRESSION;
 #endif
 
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	SSL_load_error_strings();
 	OpenSSL_add_ssl_algorithms();
+#else
+	OPENSSL_init_ssl(0, NULL);
+#endif
+
 	/*
 	 * Load SSLv{2,3} and TLSv1 so that we can talk with servers
 	 * which use the SSL hellos, which are often used for
@@ -146,7 +151,7 @@
 
 int git_openssl_set_locking(void)
 {
-#ifdef GIT_THREADS
+#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
 	int num_locks, i;
 
 	num_locks = CRYPTO_num_locks();
@@ -163,6 +168,8 @@
 	CRYPTO_set_locking_callback(openssl_locking_function);
 	git__on_shutdown(shutdown_ssl_locking);
 	return 0;
+#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
+	return 0;
 #else
 	giterr_set(GITERR_THREAD, "libgit2 was not built with threads");
 	return -1;
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 2e5de98..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 { \
@@ -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) {
@@ -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;
 
@@ -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 */
@@ -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;
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 243719d..f8d0dc9 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(
@@ -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;
@@ -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;
@@ -321,7 +312,7 @@
 {
 	int error = 0;
 	size_t name_len;
-	git_buf idx_name = GIT_BUF_INIT;
+	git_buf idx_name;
 
 	if (p->index_version > -1)
 		return 0;
@@ -329,11 +320,13 @@
 	name_len = strlen(p->pack_name);
 	assert(name_len > strlen(".pack")); /* checked by git_pack_file alloc */
 
-	git_buf_grow(&idx_name, name_len);
+	if (git_buf_init(&idx_name, name_len) < 0)
+		return -1;
+
 	git_buf_put(&idx_name, p->pack_name, name_len - strlen(".pack"));
 	git_buf_puts(&idx_name, ".idx");
 	if (git_buf_oom(&idx_name)) {
-		giterr_set_oom();
+		git_buf_free(&idx_name);
 		return -1;
 	}
 
@@ -962,10 +955,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
diff --git a/src/patch_generate.c b/src/patch_generate.c
index ab68f58..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
@@ -421,56 +400,6 @@
 	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) {
-				git_patch_free(&patch.base);
-				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;
diff --git a/src/patch_parse.c b/src/patch_parse.c
index f527594..0a9edcd 100644
--- a/src/patch_parse.c
+++ b/src/patch_parse.c
@@ -444,9 +444,9 @@
 				goto done;
 
 			parse_advance_ws(ctx);
-			parse_advance_expected_str(ctx, "\n");
 
-			if (ctx->line_len > 0) {
+			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;
 			}
@@ -562,8 +562,9 @@
 	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;
diff --git a/src/path.c b/src/path.c
index bffde93..5fc7a05 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;
 }
 
 
@@ -679,7 +700,8 @@
 		return false;
 
 	/* save excursion */
-	git_buf_joinpath(dir, dir->ptr, sub);
+	if (git_buf_joinpath(dir, dir->ptr, sub) < 0)
+		return false;
 
 	result = predicate(dir->ptr);
 
@@ -804,8 +826,8 @@
 
 int git_path_apply_relative(git_buf *target, const char *relpath)
 {
-	git_buf_joinpath(target, git_buf_cstr(target), relpath);
-	return git_path_resolve_relative(target, 0);
+	return git_buf_joinpath(target, git_buf_cstr(target), relpath) ||
+	    git_path_resolve_relative(target, 0);
 }
 
 int git_path_cmp(
@@ -1686,6 +1708,7 @@
 	unsigned int flags)
 {
 	int protectHFS = 0, protectNTFS = 0;
+	int error = 0;
 
 	flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL;
 
@@ -1698,13 +1721,13 @@
 #endif
 
 	if (repo && !protectHFS)
-		git_repository__cvar(&protectHFS, repo, GIT_CVAR_PROTECTHFS);
-	if (protectHFS)
+		error = git_repository__cvar(&protectHFS, repo, GIT_CVAR_PROTECTHFS);
+	if (!error && protectHFS)
 		flags |= GIT_PATH_REJECT_DOT_GIT_HFS;
 
 	if (repo && !protectNTFS)
-		git_repository__cvar(&protectNTFS, repo, GIT_CVAR_PROTECTNTFS);
-	if (protectNTFS)
+		error = git_repository__cvar(&protectNTFS, repo, GIT_CVAR_PROTECTNTFS);
+	if (!error && protectNTFS)
 		flags |= GIT_PATH_REJECT_DOT_GIT_NTFS;
 
 	return flags;
diff --git a/src/posix.c b/src/posix.c
index e68f324..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
diff --git a/src/posix.h b/src/posix.h
index f204751..d26371b 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -24,6 +24,10 @@
 #define _S_IFLNK S_IFLNK
 #endif
 
+#ifndef S_IWUSR
+#define S_IWUSR 00200
+#endif
+
 #ifndef S_IXUSR
 #define S_IXUSR 00100
 #endif
@@ -111,6 +115,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/rebase.c b/src/rebase.c
index b2024a4..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))) {
@@ -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;
 	}
@@ -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);
@@ -546,7 +546,9 @@
 	}
 
 	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) {
@@ -624,7 +626,7 @@
 
 	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);
@@ -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 ||
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index e40f48b..eb135dc 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);
@@ -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;
 
 	/*
@@ -331,7 +333,7 @@
 	assert(backend);
 
 	if ((error = packed_reload(backend)) < 0 ||
-		(error = git_buf_joinpath(&ref_path, backend->path, ref_name)) < 0)
+		(error = git_buf_joinpath(&ref_path, backend->gitpath, ref_name)) < 0)
 		return error;
 
 	*exists = git_path_isfile(ref_path.ptr) ||
@@ -362,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,
@@ -369,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;
@@ -484,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;
@@ -716,8 +737,9 @@
 
 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);
 
@@ -726,16 +748,25 @@
 		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 ((error = 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, backend->path, name) < 0)
+	if (git_buf_joinpath(&ref_path, basedir, name) < 0)
 		return -1;
 
-	error = git_filebuf_open(file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE);
+	filebuf_flags = GIT_FILEBUF_FORCE;
+	if (backend->fsync)
+		filebuf_flags |= GIT_FILEBUF_FSYNC;
+
+	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);
@@ -970,15 +1001,18 @@
 {
 	git_sortedcache *refcache = backend->refcache;
 	git_filebuf pack_file = GIT_FILEBUF_INIT;
-	int error;
+	int error, open_flags = 0;
 	size_t i;
 
 	/* lock the cache to updates while we do this */
 	if ((error = git_sortedcache_wlock(refcache)) < 0)
 		return error;
 
+	if (backend->fsync)
+		open_flags = GIT_FILEBUF_FSYNC;
+
 	/* Open the file! */
-	if ((error = 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
@@ -1106,7 +1140,7 @@
 static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message)
 {
 	int error;
-	git_oid old_id = {{0}};
+	git_oid old_id;
 	git_reference *tmp = NULL, *head = NULL, *peeled = NULL;
 	const char *name;
 
@@ -1114,7 +1148,8 @@
 		return 0;
 
 	/* if we can't resolve, we use {0}*40 as old id */
-	git_reference_name_to_id(&old_id, backend->repo, ref->name);
+	if (git_reference_name_to_id(&old_id, backend->repo, ref->name) < 0)
+		memset(&old_id, 0, sizeof(old_id));
 
 	if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0)
 		return error;
@@ -1283,7 +1318,7 @@
 	}
 
 	/* 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;
 
 
@@ -1408,28 +1443,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`:
@@ -1437,21 +1474,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)
@@ -1562,7 +1602,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)
@@ -1761,7 +1801,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;
@@ -1829,7 +1869,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);
@@ -1857,7 +1902,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)
@@ -1948,7 +1993,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));
@@ -1956,18 +2001,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;
@@ -1977,6 +2031,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_repository__fsync_gitdir)
+		backend->fsync = 1;
 
 	backend->parent.exists = &refdb_fs_backend__exists;
 	backend->parent.lookup = &refdb_fs_backend__lookup;
@@ -1999,8 +2056,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/refs.c b/src/refs.c
index 140b175..f7120d9 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -26,8 +26,6 @@
 
 bool git_reference__enable_symbolic_ref_target_validation = true;
 
-GIT__USE_STRMAP
-
 #define DEFAULT_NESTING_LEVEL	5
 #define MAX_NESTING_LEVEL		10
 
@@ -251,6 +249,40 @@
 	return 0;
 }
 
+int git_reference__read_head(
+	git_reference **out,
+	git_repository *repo,
+	const char *path)
+{
+	git_buf reference = GIT_BUF_INIT;
+	char *name = NULL;
+	int error;
+
+	if ((error = git_futils_readbuffer(&reference, path)) < 0)
+		goto out;
+	git_buf_rtrim(&reference);
+
+	if (git__strncmp(reference.ptr, GIT_SYMREF, strlen(GIT_SYMREF)) == 0) {
+		git_buf_consume(&reference, reference.ptr + strlen(GIT_SYMREF));
+
+		name = git_path_basename(path);
+
+		if ((*out = git_reference__alloc_symbolic(name, reference.ptr)) == NULL) {
+			error = -1;
+			goto out;
+		}
+	} else {
+		if ((error = git_reference_lookup(out, repo, reference.ptr)) < 0)
+			goto out;
+	}
+
+out:
+	git__free(name);
+	git_buf_free(&reference);
+
+	return error;
+}
+
 int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname)
 {
 	int error = 0, i;
@@ -582,19 +614,62 @@
 		out, ref->db->repo, ref->name, target, 1, ref->target.symbolic, log_message);
 }
 
+typedef struct {
+    const char *old_name;
+    git_refname_t new_name;
+} rename_cb_data;
+
+static int update_wt_heads(git_repository *repo, const char *path, void *payload)
+{
+	rename_cb_data *data = (rename_cb_data *) payload;
+	git_reference *head = NULL;
+	char *gitdir = NULL;
+	int error;
+
+	if ((error = git_reference__read_head(&head, repo, path)) < 0) {
+		giterr_set(GITERR_REFERENCE, "could not read HEAD when renaming references");
+		goto out;
+	}
+
+	if ((gitdir = git_path_dirname(path)) == NULL) {
+		error = -1;
+		goto out;
+	}
+
+	if (git_reference_type(head) != GIT_REF_SYMBOLIC ||
+	    git__strcmp(head->target.symbolic, data->old_name) != 0) {
+		error = 0;
+		goto out;
+	}
+
+	/* Update HEAD it was pointing to the reference being renamed */
+	if ((error = git_repository_create_head(gitdir, data->new_name)) < 0) {
+		giterr_set(GITERR_REFERENCE, "failed to update HEAD after renaming reference");
+		goto out;
+	}
+
+out:
+	git_reference_free(head);
+	git__free(gitdir);
+
+	return error;
+}
+
 static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
 				 const git_signature *signature, const char *message)
 {
+	git_repository *repo;
 	git_refname_t normalized;
 	bool should_head_be_updated = false;
 	int error = 0;
 
 	assert(ref && new_name && signature);
 
-	if ((error = reference_normalize_for_repo(
-		normalized, git_reference_owner(ref), new_name, true)) < 0)
-		return error;
+	repo = git_reference_owner(ref);
 
+	if ((error = reference_normalize_for_repo(
+		normalized, repo, new_name, true)) < 0)
+		return error;
 
 	/* Check if we have to update HEAD. */
 	if ((error = git_branch_is_head(ref)) < 0)
@@ -605,14 +680,18 @@
 	if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0)
 		return error;
 
-	/* 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");
-		return error;
+	/* Update HEAD if it was pointing to the reference being renamed */
+	if (should_head_be_updated) {
+		error = git_repository_set_head(ref->db->repo, normalized);
+	} else {
+		rename_cb_data payload;
+		payload.old_name = ref->name;
+		memcpy(&payload.new_name, &normalized, sizeof(normalized));
+
+		error = git_repository_foreach_head(repo, update_wt_heads, &payload);
 	}
 
-	return 0;
+	return error;
 }
 
 
@@ -1132,6 +1211,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,
@@ -1148,7 +1239,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;
 
diff --git a/src/refs.h b/src/refs.h
index 80e655a..0c90db3 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -107,6 +107,20 @@
 	const char *name,
 	int max_deref);
 
+/**
+ * Read reference from a file.
+ *
+ * This function will read in the file at `path`. If it is a
+ * symref, it will return a new unresolved symbolic reference
+ * with the given name pointing to the reference pointed to by
+ * the file. If it is not a symbolic reference, it will return
+ * the resolved reference.
+ */
+int git_reference__read_head(
+	git_reference **out,
+	git_repository *repo,
+	const char *path);
+
 int git_reference__log_signature(git_signature **out, git_repository *repo);
 
 /** Update a reference after a commit. */
diff --git a/src/remote.c b/src/remote.c
index 8da7346..bd8b3cf 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -192,7 +192,7 @@
 static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
 {
 	git_remote *remote;
-	git_config *config = NULL;
+	git_config *config_ro = NULL, *config_rw;
 	git_buf canonical_url = GIT_BUF_INIT;
 	git_buf var = GIT_BUF_INIT;
 	int error = -1;
@@ -200,7 +200,7 @@
 	/* name is optional */
 	assert(out && repo && url);
 
-	if ((error = git_repository_config__weakptr(&config, repo)) < 0)
+	if ((error = git_repository_config_snapshot(&config_ro, repo)) < 0)
 		return error;
 
 	remote = git__calloc(1, sizeof(git_remote));
@@ -212,7 +212,8 @@
 		(error = canonicalize_url(&canonical_url, url)) < 0)
 		goto on_error;
 
-	remote->url = apply_insteadof(repo->_config, canonical_url.ptr, GIT_DIRECTION_FETCH);
+	remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH);
+	GITERR_CHECK_ALLOC(remote->url);
 
 	if (name != NULL) {
 		remote->name = git__strdup(name);
@@ -221,7 +222,8 @@
 		if ((error = git_buf_printf(&var, CONFIG_URL_FMT, name)) < 0)
 			goto on_error;
 
-		if ((error = git_config_set_string(config, var.ptr, canonical_url.ptr)) < 0)
+		if ((error = git_repository_config__weakptr(&config_rw, repo)) < 0 ||
+			(error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0)
 			goto on_error;
 	}
 
@@ -233,10 +235,7 @@
 		if (name && (error = write_add_refspec(repo, name, fetch, true)) < 0)
 			goto on_error;
 
-		if ((error = git_repository_config_snapshot(&config, repo)) < 0)
-			goto on_error;
-
-		if ((error = lookup_remote_prune_config(remote, config, name)) < 0)
+		if ((error = lookup_remote_prune_config(remote, config_ro, name)) < 0)
 			goto on_error;
 
 		/* Move the data over to where the matching functions can find them */
@@ -260,7 +259,7 @@
 	if (error)
 		git_remote_free(remote);
 
-	git_config_free(config);
+	git_config_free(config_ro);
 	git_buf_free(&canonical_url);
 	git_buf_free(&var);
 	return error;
@@ -770,8 +769,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) {
@@ -2408,7 +2411,7 @@
 		proxy = &opts->proxy_opts;
 	}
 
-	assert(remote && refspecs);
+	assert(remote);
 
 	if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
 		return error;
diff --git a/src/repository.c b/src/repository.c
index 2185632..c7b40fd 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -28,16 +28,42 @@
 #include "diff_driver.h"
 #include "annotated_commit.h"
 #include "submodule.h"
+#include "worktree.h"
 
-GIT__USE_STRMAP
 #include "strmap.h"
 
 #ifdef GIT_WIN32
 # include "win32/w32_util.h"
 #endif
 
+bool git_repository__fsync_gitdir = false;
+
+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"
@@ -141,8 +167,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);
@@ -157,17 +184,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;
@@ -206,6 +257,7 @@
 	GITERR_CHECK_ALLOC(repo);
 
 	repo->is_bare = 1;
+	repo->is_worktree = 0;
 
 	return 0;
 }
@@ -225,9 +277,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;
@@ -236,9 +289,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);
@@ -246,7 +316,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;
@@ -257,6 +327,7 @@
 
 	GITERR_CHECK_ALLOC(repo->workdir);
 cleanup:
+	git_buf_free(&path);
 	git_config_entry_free(ce);
 	return error;
 }
@@ -353,9 +424,10 @@
 }
 
 static int find_repo(
-	git_buf *repo_path,
-	git_buf *parent_path,
-	git_buf *link_path,
+	git_buf *gitdir_path,
+	git_buf *workdir_path,
+	git_buf *gitlink_path,
+	git_buf *commondir_path,
 	const char *start_path,
 	uint32_t flags,
 	const char *ceiling_dirs)
@@ -363,13 +435,14 @@
 	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;
 	bool in_dot_git;
 	size_t ceiling_offset = 0;
 
-	git_buf_free(repo_path);
+	git_buf_clear(gitdir_path);
 
 	error = git_path_prettify(&path, start_path, NULL);
 	if (error < 0)
@@ -409,9 +482,16 @@
 				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);
+					git_buf_set(gitdir_path, path.ptr, path.size);
+
+					if (gitlink_path)
+						git_buf_attach(gitlink_path,
+							git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0);
+					if (commondir_path)
+						git_buf_swap(&common_link, commondir_path);
+
 					break;
 				}
 			}
@@ -419,11 +499,13 @@
 				error = read_gitfile(&repo_link, path.ptr);
 				if (error < 0)
 					break;
-				if (valid_repository_path(&repo_link)) {
-					git_buf_swap(repo_path, &repo_link);
+				if (valid_repository_path(&repo_link, &common_link)) {
+					git_buf_swap(gitdir_path, &repo_link);
 
-					if (link_path)
-						error = git_buf_put(link_path, path.ptr, path.size);
+					if (gitlink_path)
+						error = git_buf_put(gitlink_path, path.ptr, path.size);
+					if (commondir_path)
+						git_buf_swap(&common_link, commondir_path);
 				}
 				break;
 			}
@@ -449,20 +531,20 @@
 			break;
 	}
 
-	if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
-		if (!git_buf_len(repo_path))
-			git_buf_clear(parent_path);
+	if (!error && workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
+		if (!git_buf_len(gitdir_path))
+			git_buf_clear(workdir_path);
 		else {
-			git_path_dirname_r(parent_path, path.ptr);
-			git_path_to_dir(parent_path);
+			git_path_dirname_r(workdir_path, path.ptr);
+			git_path_to_dir(workdir_path);
 		}
-		if (git_buf_oom(parent_path))
+		if (git_buf_oom(workdir_path))
 			return -1;
 	}
 
 	/* If we didn't find the repository, and we don't have any other error
 	 * to report, report that. */
-	if (!git_buf_len(repo_path) && !error) {
+	if (!git_buf_len(gitdir_path) && !error) {
 		giterr_set(GITERR_REPOSITORY,
 			"could not find repository from '%s'", start_path);
 		error = GIT_ENOTFOUND;
@@ -470,6 +552,7 @@
 
 	git_buf_free(&path);
 	git_buf_free(&repo_link);
+	git_buf_free(&common_link);
 	return error;
 }
 
@@ -478,14 +561,15 @@
 	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);
+		git_buf_free(&common_path);
 		giterr_set(GITERR_REPOSITORY, "path is not a repository: %s", bare_path);
 		return GIT_ENOTFOUND;
 	}
@@ -493,11 +577,14 @@
 	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;
@@ -673,6 +760,29 @@
 	return error;
 }
 
+static int repo_is_worktree(unsigned *out, const git_repository *repo)
+{
+	git_buf gitdir_link = GIT_BUF_INIT;
+	int error;
+
+	/* Worktrees cannot have the same commondir and gitdir */
+	if (repo->commondir && repo->gitdir
+	    && !strcmp(repo->commondir, repo->gitdir)) {
+		*out = 0;
+		return 0;
+	}
+
+	if ((error = git_buf_joinpath(&gitdir_link, repo->gitdir, "gitdir")) < 0)
+		return -1;
+
+	/* A 'gitdir' file inside a git directory is currently
+	 * only used when the repository is a working tree. */
+	*out = !!git_path_exists(gitdir_link.ptr);
+
+	git_buf_free(&gitdir_link);
+	return error;
+}
+
 int git_repository_open_ext(
 	git_repository **repo_ptr,
 	const char *start_path,
@@ -680,8 +790,9 @@
 	const char *ceiling_dirs)
 {
 	int error;
-	git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT,
-		link_path = GIT_BUF_INIT;
+	unsigned is_worktree;
+	git_buf gitdir = GIT_BUF_INIT, workdir = GIT_BUF_INIT,
+		gitlink = GIT_BUF_INIT, commondir = GIT_BUF_INIT;
 	git_repository *repo;
 	git_config *config = NULL;
 
@@ -692,7 +803,7 @@
 		*repo_ptr = NULL;
 
 	error = find_repo(
-		&path, &parent, &link_path, start_path, flags, ceiling_dirs);
+		&gitdir, &workdir, &gitlink, &commondir, start_path, flags, ceiling_dirs);
 
 	if (error < 0 || !repo_ptr)
 		return error;
@@ -700,13 +811,21 @@
 	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(&gitdir);
+	GITERR_CHECK_ALLOC(repo->gitdir);
 
-	if (link_path.size) {
-		repo->path_gitlink = git_buf_detach(&link_path);
-		GITERR_CHECK_ALLOC(repo->path_gitlink);
+	if (gitlink.size) {
+		repo->gitlink = git_buf_detach(&gitlink);
+		GITERR_CHECK_ALLOC(repo->gitlink);
 	}
+	if (commondir.size) {
+		repo->commondir = git_buf_detach(&commondir);
+		GITERR_CHECK_ALLOC(repo->commondir);
+	}
+
+	if ((error = repo_is_worktree(&is_worktree, repo)) < 0)
+		goto cleanup;
+	repo->is_worktree = is_worktree;
 
 	/*
 	 * We'd like to have the config, but git doesn't particularly
@@ -726,12 +845,13 @@
 
 		if (config &&
 		    ((error = load_config_data(repo, config)) < 0 ||
-		     (error = load_workdir(repo, config, &parent)) < 0))
+		     (error = load_workdir(repo, config, &workdir)) < 0))
 			goto cleanup;
 	}
 
 cleanup:
-	git_buf_free(&parent);
+	git_buf_free(&gitdir);
+	git_buf_free(&workdir);
 	git_config_free(config);
 
 	if (error < 0)
@@ -748,6 +868,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;
@@ -773,7 +923,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(
@@ -793,8 +943,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;
 
@@ -928,18 +1077,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);
@@ -1014,7 +1168,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);
@@ -1130,13 +1284,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;
 		}
 	}
@@ -1193,7 +1347,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;
@@ -1856,7 +2010,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);
@@ -1868,7 +2023,7 @@
 		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,
@@ -1889,7 +2044,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)
@@ -1901,6 +2056,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);
 
@@ -1930,6 +2086,29 @@
 	return exists;
 }
 
+static int get_worktree_file_path(git_buf *out, git_repository *repo, const char *worktree, const char *file)
+{
+	git_buf_clear(out);
+	return git_buf_printf(out, "%s/worktrees/%s/%s", repo->commondir, worktree, file);
+}
+
+int git_repository_head_detached_for_worktree(git_repository *repo, const char *name)
+{
+	git_reference *ref = NULL;
+	int error;
+
+	assert(repo && name);
+
+	if ((error = git_repository_head_for_worktree(&ref, repo, name)) < 0)
+		goto out;
+
+	error = (git_reference_type(ref) != GIT_REF_SYMBOLIC);
+out:
+	git_reference_free(ref);
+
+	return error;
+}
+
 int git_repository_head(git_reference **head_out, git_repository *repo)
 {
 	git_reference *head;
@@ -1949,6 +2128,71 @@
 	return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error;
 }
 
+int git_repository_head_for_worktree(git_reference **out, git_repository *repo, const char *name)
+{
+	git_buf path = GIT_BUF_INIT;
+	git_reference *head = NULL;
+	int error;
+
+	assert(out && repo && name);
+
+	*out = NULL;
+
+	if ((error = get_worktree_file_path(&path, repo, name, GIT_HEAD_FILE)) < 0 ||
+	    (error = git_reference__read_head(&head, repo, path.ptr)) < 0)
+		goto out;
+
+	if (git_reference_type(head) != GIT_REF_OID) {
+		git_reference *resolved;
+
+		error = git_reference_lookup_resolved(&resolved, repo, git_reference_symbolic_target(head), -1);
+		git_reference_free(head);
+		head = resolved;
+	}
+
+	*out = head;
+
+out:
+	if (error)
+		git_reference_free(head);
+
+	git_buf_free(&path);
+
+	return error;
+}
+
+int git_repository_foreach_head(git_repository *repo, git_repository_foreach_head_cb cb, void *payload)
+{
+	git_strarray worktrees = GIT_VECTOR_INIT;
+	git_buf path = GIT_BUF_INIT;
+	int error;
+	size_t i;
+
+	/* Execute callback for HEAD of commondir */
+	if ((error = git_buf_joinpath(&path, repo->commondir, GIT_HEAD_FILE)) < 0 ||
+	    (error = cb(repo, path.ptr, payload) != 0))
+		goto out;
+
+	if ((error = git_worktree_list(&worktrees, repo)) < 0) {
+		error = 0;
+		goto out;
+	}
+
+	/* Execute callback for all worktree HEADs */
+	for (i = 0; i < worktrees.count; i++) {
+		if (get_worktree_file_path(&path, repo, worktrees.strings[i], GIT_HEAD_FILE) < 0)
+			continue;
+
+		if ((error = cb(repo, path.ptr, payload)) != 0)
+			goto out;
+	}
+
+out:
+	git_buf_free(&path);
+	git_strarray_free(&worktrees);
+	return error;
+}
+
 int git_repository_head_unborn(git_repository *repo)
 {
 	git_reference *ref = NULL;
@@ -2007,10 +2251,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)
@@ -2023,6 +2307,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)
 {
@@ -2073,6 +2363,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;
@@ -2127,7 +2423,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);
@@ -2148,7 +2444,7 @@
 
 	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) {
@@ -2169,7 +2465,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));
@@ -2259,7 +2555,9 @@
 
 	git_buf_puts(out, " to ");
 
-	if (git_reference__is_branch(new))
+	if (git_reference__is_branch(new) ||
+		git_reference__is_tag(new) ||
+		git_reference__is_remote(new))
 		git_buf_puts(out, git_reference__shorthand(new));
 	else
 		git_buf_puts(out, new);
@@ -2270,6 +2568,41 @@
 	return 0;
 }
 
+static int detach(git_repository *repo, const git_oid *id, const char *new)
+{
+	int error;
+	git_buf log_message = GIT_BUF_INIT;
+	git_object *object = NULL, *peeled = NULL;
+	git_reference *new_head = NULL, *current = NULL;
+
+	assert(repo && id);
+
+	if ((error = git_reference_lookup(&current, repo, GIT_HEAD_FILE)) < 0)
+		return error;
+
+	if ((error = git_object_lookup(&object, repo, id, GIT_OBJ_ANY)) < 0)
+		goto cleanup;
+
+	if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0)
+		goto cleanup;
+
+	if (new == NULL)
+		new = git_oid_tostr_s(git_object_id(peeled));
+
+	if ((error = checkout_message(&log_message, current, new)) < 0)
+		goto cleanup;
+
+	error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, git_buf_cstr(&log_message));
+
+cleanup:
+	git_buf_free(&log_message);
+	git_object_free(object);
+	git_object_free(peeled);
+	git_reference_free(current);
+	git_reference_free(new_head);
+	return error;
+}
+
 int git_repository_set_head(
 	git_repository* repo,
 	const char* refname)
@@ -2290,12 +2623,21 @@
 	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)) {
+		giterr_set(GITERR_REPOSITORY, "cannot set HEAD to reference '%s' as it is the current HEAD "
+			"of a linked repository.", git_reference_name(ref));
+		error = -1;
+		goto cleanup;
+	}
+
 	if (!error) {
 		if (git_reference_is_branch(ref)) {
 			error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE,
 					git_reference_name(ref), true, git_buf_cstr(&log_message));
 		} else {
-			error = git_repository_set_head_detached(repo, git_reference_target(ref));
+			error = detach(repo, git_reference_target(ref),
+				git_reference_is_tag(ref) || git_reference_is_remote(ref) ? refname : NULL);
 		}
 	} else if (git_reference__is_branch(refname)) {
 		error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname,
@@ -2310,41 +2652,6 @@
 	return error;
 }
 
-static int detach(git_repository *repo, const git_oid *id, const char *from)
-{
-	int error;
-	git_buf log_message = GIT_BUF_INIT;
-	git_object *object = NULL, *peeled = NULL;
-	git_reference *new_head = NULL, *current = NULL;
-
-	assert(repo && id);
-
-	if ((error = git_reference_lookup(&current, repo, GIT_HEAD_FILE)) < 0)
-		return error;
-
-	if ((error = git_object_lookup(&object, repo, id, GIT_OBJ_ANY)) < 0)
-		goto cleanup;
-
-	if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0)
-		goto cleanup;
-
-	if (from == NULL)
-		from = git_oid_tostr_s(git_object_id(peeled));
-
-	if ((error = checkout_message(&log_message, current, from)) < 0)
-		goto cleanup;
-
-	error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, git_buf_cstr(&log_message));
-
-cleanup:
-	git_buf_free(&log_message);
-	git_object_free(object);
-	git_object_free(peeled);
-	git_reference_free(current);
-	git_reference_free(new_head);
-	return error;
-}
-
 int git_repository_set_head_detached(
 	git_repository* repo,
 	const git_oid* commitish)
@@ -2405,7 +2712,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))
@@ -2447,7 +2754,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);
@@ -2491,7 +2798,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);
diff --git a/src/repository.h b/src/repository.h
index 9d276f3..52f9ec2 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -31,6 +31,8 @@
 /* Default DOS-compatible 8.3 "short name" for a git repository, "GIT~1" */
 #define GIT_DIR_SHORTNAME "GIT~1"
 
+extern bool git_repository__fsync_gitdir;
+
 /** Cvar cache identifiers */
 typedef enum {
 	GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */
@@ -46,6 +48,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 +109,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 +131,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,6 +143,7 @@
 	git_array_t(git_buf) reserved_names;
 
 	unsigned is_bare:1;
+	unsigned is_worktree:1;
 
 	unsigned int lru_counter;
 
@@ -152,6 +159,27 @@
 }
 
 int git_repository_head_tree(git_tree **tree, git_repository *repo);
+int git_repository_create_head(const char *git_dir, const char *ref_name);
+
+/*
+ * Called for each HEAD.
+ *
+ * Can return either 0, causing the iteration over HEADs to
+ * continue, or a non-0 value causing the iteration to abort. The
+ * return value is passed back to the caller of
+ * `git_repository_foreach_head`
+ */
+typedef int (*git_repository_foreach_head_cb)(git_repository *repo, const char *path, void *payload);
+
+/*
+ * Iterate over repository and all worktree HEADs.
+ *
+ * This function will be called for the repository HEAD and for
+ * all HEADS of linked worktrees. For each HEAD, the callback is
+ * executed with the given payload. The return value equals the
+ * return value of the last executed callback function.
+ */
+int git_repository_foreach_head(git_repository *repo, git_repository_foreach_head_cb cb, void *payload);
 
 /*
  * Weak pointers to repository internals.
diff --git a/src/revert.c b/src/revert.c
index b255245..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)
diff --git a/src/revparse.c b/src/revparse.c
index d5511b4..fd6bd1e 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -892,6 +892,17 @@
 		const char *rstr;
 		revspec->flags = GIT_REVPARSE_RANGE;
 
+		/*
+		 * Following git.git, don't allow '..' because it makes command line
+		 * arguments which can be either paths or revisions ambiguous when the
+		 * path is almost certainly intended. The empty range '...' is still
+		 * allowed.
+		 */
+		if (!git__strcmp(spec, "..")) {
+			giterr_set(GITERR_INVALID, "Invalid pattern '..'");
+			return GIT_EINVALIDSPEC;
+		}
+
 		lstr = git__substrdup(spec, dotdot - spec);
 		rstr = dotdot + 2;
 		if (dotdot[2] == '.') {
@@ -899,9 +910,17 @@
 			rstr++;
 		}
 
-		error = git_revparse_single(&revspec->from, repo, lstr);
-		if (!error)
-			error = git_revparse_single(&revspec->to, repo, rstr);
+		error = git_revparse_single(
+			&revspec->from,
+			repo,
+			*lstr == '\0' ? "HEAD" : lstr);
+
+		if (!error) {
+			error = git_revparse_single(
+				&revspec->to,
+				repo,
+				*rstr == '\0' ? "HEAD" : rstr);
+		}
 
 		git__free((void*)lstr);
 	} else {
diff --git a/src/revwalk.c b/src/revwalk.c
index 8c370bc..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;
 }
@@ -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();
@@ -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;
diff --git a/src/settings.c b/src/settings.c
index 222bd6b..52b861b 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -15,7 +15,9 @@
 #include "cache.h"
 #include "global.h"
 #include "object.h"
+#include "odb.h"
 #include "refs.h"
+#include "transports/smart.h"
 
 void git_libgit2_version(int *major, int *minor, int *rev)
 {
@@ -30,7 +32,7 @@
 #ifdef GIT_THREADS
 		| GIT_FEATURE_THREADS
 #endif
-#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
+#ifdef GIT_HTTPS
 		| GIT_FEATURE_HTTPS
 #endif
 #if defined(GIT_SSH)
@@ -222,6 +224,30 @@
 		}
 		break;
 
+	case GIT_OPT_ENABLE_OFS_DELTA:
+		git_smart__ofs_delta_enabled = (va_arg(ap, int) != 0);
+		break;
+
+	case GIT_OPT_ENABLE_FSYNC_GITDIR:
+		git_repository__fsync_gitdir = (va_arg(ap, int) != 0);
+		break;
+
+	case GIT_OPT_GET_WINDOWS_SHAREMODE:
+#ifdef GIT_WIN32
+		*(va_arg(ap, unsigned long *)) = git_win32__createfile_sharemode;
+#endif
+		break;
+
+	case GIT_OPT_SET_WINDOWS_SHAREMODE:
+#ifdef GIT_WIN32
+		git_win32__createfile_sharemode = va_arg(ap, unsigned long);
+#endif
+		break;
+
+	case GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION:
+		git_odb__strict_hash_verification = (va_arg(ap, int) != 0);
+		break;
+
 	default:
 		giterr_set(GITERR_INVALID, "invalid option key");
 		error = -1;
diff --git a/src/signature.c b/src/signature.c
index e792a52..a56b8a2 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -228,8 +228,11 @@
 		const char *time_start = email_end + 2;
 		const char *time_end;
 
-		if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0)
+		if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0) {
+			git__free(sig->name);
+			git__free(sig->email);
 			return signature_error("invalid Unix timestamp");
+		}
 
 		/* do we have a timezone? */
 		if (time_end + 1 < buffer_end) {
diff --git a/src/socket_stream.c b/src/socket_stream.c
index fca4117..c0a1684 100644
--- a/src/socket_stream.c
+++ b/src/socket_stream.c
@@ -106,10 +106,8 @@
 	for (p = info; p != NULL; p = p->ai_next) {
 		s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
 
-		if (s == INVALID_SOCKET) {
-			net_set_error("error creating socket");
-			break;
-		}
+		if (s == INVALID_SOCKET)
+			continue;
 
 		if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
 			break;
diff --git a/src/sortedcache.c b/src/sortedcache.c
index c5e338f..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,
@@ -294,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)
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 e1f59b8..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"
 
@@ -186,10 +187,9 @@
 		ldot = strrchr(entry->name, '.');
 
 		git_buf_put(&buf, fdot + 1, ldot - fdot - 1);
-		git_strmap_insert(out, entry->value, git_buf_detach(&buf), rval);
+		git_strmap_insert(out, entry->value, git_buf_detach(&buf), &rval);
 		if (rval < 0) {
-			giterr_set(GITERR_NOMEMORY, "Error inserting submodule into hash table");
-			free_submodule_names(out);
+			giterr_set(GITERR_NOMEMORY, "error inserting submodule into hash table");
 			return -1;
 		}
 	}
@@ -330,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) {
@@ -402,7 +402,7 @@
 static int submodules_from_head(git_strmap *map, git_tree *head, git_config *cfg)
 {
 	int error;
-	git_iterator *i;
+	git_iterator *i = NULL;
 	const git_index_entry *entry;
 	git_strmap *names = 0;
 	git_strmap_alloc(&names);
@@ -513,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;
 	}
@@ -556,7 +556,7 @@
 		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)
@@ -617,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;
 
@@ -1085,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;
 
@@ -1209,6 +1213,8 @@
 			(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
@@ -1217,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 ||
@@ -1863,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;
@@ -2027,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/sysdir.c b/src/sysdir.c
index ed11221..9312a7e 100644
--- a/src/sysdir.c
+++ b/src/sysdir.c
@@ -275,3 +275,14 @@
 		path, NULL, GIT_SYSDIR_TEMPLATE, "template");
 }
 
+int git_sysdir_expand_global_file(git_buf *path, const char *filename)
+{
+	int error;
+
+	if ((error = git_sysdir_find_global_file(path, NULL)) == 0) {
+		if (filename)
+			error = git_buf_joinpath(path, path->ptr, filename);
+	}
+
+	return error;
+}
diff --git a/src/sysdir.h b/src/sysdir.h
index 1187898..79f2381 100644
--- a/src/sysdir.h
+++ b/src/sysdir.h
@@ -55,6 +55,18 @@
  */
 extern int git_sysdir_find_template_dir(git_buf *path);
 
+/**
+ * Expand the name of a "global" file (i.e. one in a user's home
+ * directory).  Unlike `find_global_file` (above), this makes no
+ * attempt to check for the existence of the file, and is useful if
+ * you want the full path regardless of existence.
+ *
+ * @param path buffer to write the full path into
+ * @param filename name of file in the home directory
+ * @return 0 on success or -1 on error
+ */
+extern int git_sysdir_expand_global_file(git_buf *path, const char *filename);
+
 typedef enum {
 	GIT_SYSDIR_SYSTEM = 0,
 	GIT_SYSDIR_GLOBAL = 1,
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/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/transports/http.c b/src/transports/http.c
index 949e857..cb4a6d0 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -575,6 +575,9 @@
 		if ((error = git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &url)) < 0)
 			return error;
 
+		opts.credentials = t->owner->proxy.credentials;
+		opts.certificate_check = t->owner->proxy.certificate_check;
+		opts.payload = t->owner->proxy.payload;
 		opts.type = GIT_PROXY_SPECIFIED;
 		opts.url = url;
 		error = git_stream_set_proxy(t->io, &opts);
diff --git a/src/transports/local.c b/src/transports/local.c
index 87745ad..e24e998 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -375,7 +375,8 @@
 		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 e4aa26d..a96fdf6 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -472,6 +472,12 @@
 	return t->cred_acquire_cb(out, t->url, user, methods, t->cred_acquire_payload);
 }
 
+int git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport)
+{
+	transport_smart *t = (transport_smart *) transport;
+	return git_proxy_options_dup(out, &t->proxy);
+}
+
 int git_transport_smart(git_transport **out, git_remote *owner, void *param)
 {
 	transport_smart *t;
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_protocol.c b/src/transports/smart_protocol.c
index 7a1d4dc..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;
@@ -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;
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 44d02e5..4c55e3f 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -9,6 +9,7 @@
 #include <libssh2.h>
 #endif
 
+#include "global.h"
 #include "git2.h"
 #include "buffer.h"
 #include "netops.h"
@@ -893,11 +894,22 @@
 #endif
 }
 
+#ifdef GIT_SSH
+static void shutdown_ssh(void)
+{
+    libssh2_exit();
+}
+#endif
+
 int git_transport_ssh_global_init(void)
 {
 #ifdef GIT_SSH
+	if (libssh2_init(0) < 0) {
+		giterr_set(GITERR_SSH, "unable to initialize libssh2");
+		return -1;
+	}
 
-	libssh2_init(0);
+	git__on_shutdown(shutdown_ssh);
 	return 0;
 
 #else
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 79b3ac6..fb504c9 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -70,6 +70,7 @@
 	GIT_WINHTTP_AUTH_BASIC = 1,
 	GIT_WINHTTP_AUTH_NTLM = 2,
 	GIT_WINHTTP_AUTH_NEGOTIATE = 4,
+	GIT_WINHTTP_AUTH_DIGEST = 8,
 } winhttp_authmechanism_t;
 
 typedef struct {
@@ -131,8 +132,13 @@
 	return error;
 }
 
-static int apply_userpass_credential_proxy(HINTERNET request, git_cred *cred)
+static int apply_userpass_credential_proxy(HINTERNET request, git_cred *cred, int mechanisms)
 {
+	if (GIT_WINHTTP_AUTH_DIGEST & mechanisms) {
+		return _apply_userpass_credential(request, WINHTTP_AUTH_TARGET_PROXY,
+			WINHTTP_AUTH_SCHEME_DIGEST, cred);
+	}
+
 	return _apply_userpass_credential(request, WINHTTP_AUTH_TARGET_PROXY,
 		WINHTTP_AUTH_SCHEME_BASIC, cred);
 }
@@ -242,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;
@@ -419,7 +429,6 @@
 			git_buf_printf(&processed_url, ":%s", t->proxy_connection_data.port);
 
 		if (git_buf_oom(&processed_url)) {
-			giterr_set_oom();
 			error = -1;
 			goto on_error;
 		}
@@ -447,7 +456,7 @@
 
 		if (t->proxy_cred) {
 			if (t->proxy_cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT) {
-				if ((error = apply_userpass_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;
 			}
 		}
@@ -584,11 +593,11 @@
 	*allowed_types = 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;
 	}
 
@@ -608,6 +617,11 @@
 		*allowed_mechanisms |= GIT_WINHTTP_AUTH_BASIC;
 	}
 
+	if (WINHTTP_AUTH_SCHEME_DIGEST & supported) {
+		*allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
+		*allowed_mechanisms |= GIT_WINHTTP_AUTH_DIGEST;
+	}
+
 	return 0;
 }
 
@@ -691,6 +705,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)
 {
@@ -747,7 +793,7 @@
 		goto on_error;
 	}
 
-	
+
 	/* Establish connection */
 	t->connection = WinHttpConnect(
 		t->session,
@@ -760,6 +806,11 @@
 		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;
+	}
+
 	error = 0;
 
 on_error:
@@ -798,16 +849,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();
@@ -823,14 +873,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;
 }
@@ -1032,7 +1082,7 @@
 			/* 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;
diff --git a/src/tree.c b/src/tree.c
index 26da296..6b1d1b2 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
@@ -505,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);
@@ -754,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);
@@ -820,7 +818,7 @@
 	size_t i, entrycount;
 	git_odb *odb;
 	git_tree_entry *entry;
-	git_vector entries;
+	git_vector entries = GIT_VECTOR_INIT;
 
 	assert(bld);
 	assert(tree);
@@ -828,35 +826,37 @@
 	git_buf_clear(tree);
 
 	entrycount = git_strmap_num_entries(bld->map);
-	if (git_vector_init(&entries, entrycount, entry_sort_cmp) < 0)
-		return -1;
+	if ((error = git_vector_init(&entries, entrycount, entry_sort_cmp)) < 0)
+		goto out;
 
 	if (tree->asize == 0 &&
-		(error = git_buf_grow(tree, entrycount * 72)) < 0)
-		return error;
+	    (error = git_buf_grow(tree, entrycount * 72)) < 0)
+		goto out;
 
 	git_strmap_foreach_value(bld->map, entry, {
-		if (git_vector_insert(&entries, entry) < 0)
-			return -1;
+		if ((error = git_vector_insert(&entries, entry)) < 0)
+			goto out;
 	});
 
 	git_vector_sort(&entries);
 
 	for (i = 0; i < entries.length && !error; ++i) {
-		git_tree_entry *entry = git_vector_get(&entries, i);
+		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);
 
-		if (git_buf_oom(tree))
+		if (git_buf_oom(tree)) {
 			error = -1;
+			goto out;
+		}
 	}
 
-	if (!error &&
-		!(error = git_repository_odb__weakptr(&odb, bld->repo)))
+	if ((error = git_repository_odb__weakptr(&odb, bld->repo)) == 0)
 		error = git_odb_write(oid, odb, tree->ptr, tree->size, GIT_OBJ_TREE);
 
+out:
 	git_vector_free(&entries);
 
 	return error;
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/varint.c b/src/varint.c
index 2f86860..beac8c7 100644
--- a/src/varint.c
+++ b/src/varint.c
@@ -36,7 +36,7 @@
 	while (value >>= 7)
 		varint[--pos] = 128 | (--value & 127);
 	if (buf) {
-		if (bufsize < pos)
+		if (bufsize < (sizeof(varint) - pos))
 			return -1;
 		memcpy(buf, varint + pos, sizeof(varint) - pos);
 	}
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/posix.h b/src/win32/posix.h
index 73705fb..64769ec 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -14,6 +14,9 @@
 #include "utf-conv.h"
 #include "dir.h"
 
+extern unsigned long git_win32__createfile_sharemode;
+extern int git_win32__retries;
+
 typedef SOCKET GIT_SOCKET;
 
 #define p_lseek(f,n,w) _lseeki64(f, n, w)
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index fea634b..e4fe414 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -26,15 +26,6 @@
 #define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
 #endif
 
-/* Options which we always provide to _wopen.
- *
- * _O_BINARY - Raw access; no translation of CR or LF characters
- * _O_NOINHERIT - Do not mark the created handle as inheritable by child processes.
- *    The Windows default is 'not inheritable', but the CRT's default (following
- *    POSIX convention) is 'inheritable'. We have no desire for our handles to be
- *    inheritable on Windows, so specify the flag to get default behavior back. */
-#define STANDARD_OPEN_FLAGS (_O_BINARY | _O_NOINHERIT)
-
 /* Allowable mode bits on Win32.  Using mode bits that are not supported on
  * Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it
  * so we simply remove them.
@@ -44,6 +35,164 @@
 /* GetFinalPathNameByHandleW signature */
 typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
 
+unsigned long git_win32__createfile_sharemode =
+ FILE_SHARE_READ | FILE_SHARE_WRITE;
+int git_win32__retries = 10;
+
+GIT_INLINE(void) set_errno(void)
+{
+	switch (GetLastError()) {
+	case ERROR_FILE_NOT_FOUND:
+	case ERROR_PATH_NOT_FOUND:
+	case ERROR_INVALID_DRIVE:
+	case ERROR_NO_MORE_FILES:
+	case ERROR_BAD_NETPATH:
+	case ERROR_BAD_NET_NAME:
+	case ERROR_BAD_PATHNAME:
+	case ERROR_FILENAME_EXCED_RANGE:
+		errno = ENOENT;
+		break;
+	case ERROR_BAD_ENVIRONMENT:
+		errno = E2BIG;
+		break;
+	case ERROR_BAD_FORMAT:
+	case ERROR_INVALID_STARTING_CODESEG:
+	case ERROR_INVALID_STACKSEG:
+	case ERROR_INVALID_MODULETYPE:
+	case ERROR_INVALID_EXE_SIGNATURE:
+	case ERROR_EXE_MARKED_INVALID:
+	case ERROR_BAD_EXE_FORMAT:
+	case ERROR_ITERATED_DATA_EXCEEDS_64k:
+	case ERROR_INVALID_MINALLOCSIZE:
+	case ERROR_DYNLINK_FROM_INVALID_RING:
+	case ERROR_IOPL_NOT_ENABLED:
+	case ERROR_INVALID_SEGDPL:
+	case ERROR_AUTODATASEG_EXCEEDS_64k:
+	case ERROR_RING2SEG_MUST_BE_MOVABLE:
+	case ERROR_RELOC_CHAIN_XEEDS_SEGLIM:
+	case ERROR_INFLOOP_IN_RELOC_CHAIN:
+		errno = ENOEXEC;
+		break;
+	case ERROR_INVALID_HANDLE:
+	case ERROR_INVALID_TARGET_HANDLE:
+	case ERROR_DIRECT_ACCESS_HANDLE:
+		errno = EBADF;
+		break;
+	case ERROR_WAIT_NO_CHILDREN:
+	case ERROR_CHILD_NOT_COMPLETE:
+		errno = ECHILD;
+		break;
+	case ERROR_NO_PROC_SLOTS:
+	case ERROR_MAX_THRDS_REACHED:
+	case ERROR_NESTING_NOT_ALLOWED:
+		errno = EAGAIN;
+		break;
+	case ERROR_ARENA_TRASHED:
+	case ERROR_NOT_ENOUGH_MEMORY:
+	case ERROR_INVALID_BLOCK:
+	case ERROR_NOT_ENOUGH_QUOTA:
+		errno = ENOMEM;
+		break;
+	case ERROR_ACCESS_DENIED:
+	case ERROR_CURRENT_DIRECTORY:
+	case ERROR_WRITE_PROTECT:
+	case ERROR_BAD_UNIT:
+	case ERROR_NOT_READY:
+	case ERROR_BAD_COMMAND:
+	case ERROR_CRC:
+	case ERROR_BAD_LENGTH:
+	case ERROR_SEEK:
+	case ERROR_NOT_DOS_DISK:
+	case ERROR_SECTOR_NOT_FOUND:
+	case ERROR_OUT_OF_PAPER:
+	case ERROR_WRITE_FAULT:
+	case ERROR_READ_FAULT:
+	case ERROR_GEN_FAILURE:
+	case ERROR_SHARING_VIOLATION:
+	case ERROR_LOCK_VIOLATION:
+	case ERROR_WRONG_DISK:
+	case ERROR_SHARING_BUFFER_EXCEEDED:
+	case ERROR_NETWORK_ACCESS_DENIED:
+	case ERROR_CANNOT_MAKE:
+	case ERROR_FAIL_I24:
+	case ERROR_DRIVE_LOCKED:
+	case ERROR_SEEK_ON_DEVICE:
+	case ERROR_NOT_LOCKED:
+	case ERROR_LOCK_FAILED:
+		errno = EACCES;
+		break;
+	case ERROR_FILE_EXISTS:
+	case ERROR_ALREADY_EXISTS:
+		errno = EEXIST;
+		break;
+	case ERROR_NOT_SAME_DEVICE:
+		errno = EXDEV;
+		break;
+	case ERROR_INVALID_FUNCTION:
+	case ERROR_INVALID_ACCESS:
+	case ERROR_INVALID_DATA:
+	case ERROR_INVALID_PARAMETER:
+	case ERROR_NEGATIVE_SEEK:
+		errno = EINVAL;
+		break;
+	case ERROR_TOO_MANY_OPEN_FILES:
+		errno = EMFILE;
+		break;
+	case ERROR_DISK_FULL:
+		errno = ENOSPC;
+		break;
+	case ERROR_BROKEN_PIPE:
+		errno = EPIPE;
+		break;
+	case ERROR_DIR_NOT_EMPTY:
+		errno = ENOTEMPTY;
+		break;
+	default:
+		errno = EINVAL;
+	}
+}
+
+GIT_INLINE(bool) last_error_retryable(void)
+{
+	int os_error = GetLastError();
+
+	return (os_error == ERROR_SHARING_VIOLATION ||
+		os_error == ERROR_ACCESS_DENIED);
+}
+
+#define do_with_retries(fn, remediation) \
+	do {                                                             \
+		int __tries, __ret;                                          \
+		for (__tries = 0; __tries < git_win32__retries; __tries++) { \
+			if (__tries && (__ret = (remediation)) != 0)             \
+				return __ret;                                        \
+			if ((__ret = (fn)) != GIT_RETRY)                         \
+				return __ret;                                        \
+			Sleep(5);                                                \
+		}                                                            \
+		return -1;                                                   \
+	} while (0)                                                      \
+
+static int ensure_writable(wchar_t *path)
+{
+	DWORD attrs;
+
+	if ((attrs = GetFileAttributesW(path)) == INVALID_FILE_ATTRIBUTES)
+		goto on_error;
+
+	if ((attrs & FILE_ATTRIBUTE_READONLY) == 0)
+		return 0;
+
+	if (!SetFileAttributesW(path, (attrs & ~FILE_ATTRIBUTE_READONLY)))
+		goto on_error;
+
+	return 0;
+
+on_error:
+	set_errno();
+	return -1;
+}
+
 /**
  * Truncate or extend file.
  *
@@ -89,30 +238,34 @@
 	return -1;
 }
 
+GIT_INLINE(int) unlink_once(const wchar_t *path)
+{
+	if (DeleteFileW(path))
+		return 0;
+
+	if (last_error_retryable())
+		return GIT_RETRY;
+
+	set_errno();
+	return -1;
+}
+
 int p_unlink(const char *path)
 {
-	git_win32_path buf;
-	int error;
+	git_win32_path wpath;
 
-	if (git_win32_path_from_utf8(buf, path) < 0)
+	if (git_win32_path_from_utf8(wpath, path) < 0)
 		return -1;
 
-	error = _wunlink(buf);
-
-	/* If the file could not be deleted because it was
-	 * read-only, clear the bit and try again */
-	if (error == -1 && errno == EACCES) {
-		_wchmod(buf, 0666);
-		error = _wunlink(buf);
-	}
-
-	return error;
+	do_with_retries(unlink_once(wpath), ensure_writable(wpath));
 }
 
 int p_fsync(int fd)
 {
 	HANDLE fh = (HANDLE)_get_osfhandle(fd);
 
+	p_fsync__cnt++;
+
 	if (fh == INVALID_HANDLE_VALUE) {
 		errno = EBADF;
 		return -1;
@@ -210,44 +363,6 @@
 	return do_lstat(filename, buf, true);
 }
 
-int p_utimes(const char *filename, const struct p_timeval times[2])
-{
-	int fd, error;
-
-	if ((fd = p_open(filename, O_RDWR)) < 0)
-		return fd;
-
-	error = p_futimes(fd, times);
-
-	close(fd);
-	return error;
-}
-
-int p_futimes(int fd, const struct p_timeval times[2])
-{
-	HANDLE handle;
-	FILETIME atime = {0}, mtime = {0};
-
-	if (times == NULL) {
-		SYSTEMTIME st;
-
-		GetSystemTime(&st);
-		SystemTimeToFileTime(&st, &atime);
-		SystemTimeToFileTime(&st, &mtime);
-	} else {
-		git_win32__timeval_to_filetime(&atime, times[0]);
-		git_win32__timeval_to_filetime(&mtime, times[1]);
-	}
-
-	if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
-		return -1;
-
-	if (SetFileTime(handle, NULL, &atime, &mtime) == 0)
-		return -1;
-
-	return 0;
-}
-
 int p_readlink(const char *path, char *buf, size_t bufsiz)
 {
 	git_win32_path path_w, target_w;
@@ -280,12 +395,91 @@
 	return git_futils_fake_symlink(old, new);
 }
 
+struct open_opts {
+	DWORD access;
+	DWORD sharing;
+	SECURITY_ATTRIBUTES security;
+	DWORD creation_disposition;
+	DWORD attributes;
+	int osf_flags;
+};
+
+GIT_INLINE(void) open_opts_from_posix(struct open_opts *opts, int flags, mode_t mode)
+{
+	memset(opts, 0, sizeof(struct open_opts));
+
+	switch (flags & (O_WRONLY | O_RDWR)) {
+	case O_WRONLY:
+		opts->access = GENERIC_WRITE;
+		break;
+	case O_RDWR:
+		opts->access = GENERIC_READ | GENERIC_WRITE;
+		break;
+	default:
+		opts->access = GENERIC_READ;
+		break;
+	}
+
+	opts->sharing = (DWORD)git_win32__createfile_sharemode;
+
+	switch (flags & (O_CREAT | O_TRUNC | O_EXCL)) {
+	case O_CREAT | O_EXCL:
+	case O_CREAT | O_TRUNC | O_EXCL:
+		opts->creation_disposition = CREATE_NEW;
+		break;
+	case O_CREAT | O_TRUNC:
+		opts->creation_disposition = CREATE_ALWAYS;
+		break;
+	case O_TRUNC:
+		opts->creation_disposition = TRUNCATE_EXISTING;
+		break;
+	case O_CREAT:
+		opts->creation_disposition = OPEN_ALWAYS;
+		break;
+	default:
+		opts->creation_disposition = OPEN_EXISTING;
+		break;
+	}
+
+	opts->attributes = ((flags & O_CREAT) && !(mode & S_IWRITE)) ?
+		FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL;
+	opts->osf_flags = flags & (O_RDONLY | O_APPEND);
+
+	opts->security.nLength = sizeof(SECURITY_ATTRIBUTES);
+	opts->security.lpSecurityDescriptor = NULL;
+	opts->security.bInheritHandle = 0;
+}
+
+GIT_INLINE(int) open_once(
+	const wchar_t *path,
+	struct open_opts *opts)
+{
+	int fd;
+
+	HANDLE handle = CreateFileW(path, opts->access, opts->sharing,
+		&opts->security, opts->creation_disposition, opts->attributes, 0);
+
+	if (handle == INVALID_HANDLE_VALUE) {
+		if (last_error_retryable())
+			return GIT_RETRY;
+
+		set_errno();
+		return -1;
+	}
+
+	if ((fd = _open_osfhandle((intptr_t)handle, opts->osf_flags)) < 0)
+		CloseHandle(handle);
+
+	return fd;
+}
+
 int p_open(const char *path, int flags, ...)
 {
-	git_win32_path buf;
+	git_win32_path wpath;
 	mode_t mode = 0;
+	struct open_opts opts = {0};
 
-	if (git_win32_path_from_utf8(buf, path) < 0)
+	if (git_win32_path_from_utf8(wpath, path) < 0)
 		return -1;
 
 	if (flags & O_CREAT) {
@@ -296,19 +490,83 @@
 		va_end(arg_list);
 	}
 
-	return _wopen(buf, flags | STANDARD_OPEN_FLAGS, mode & WIN32_MODE_MASK);
+	open_opts_from_posix(&opts, flags, mode);
+
+	do_with_retries(
+		open_once(wpath, &opts),
+		0);
 }
 
 int p_creat(const char *path, mode_t mode)
 {
-	git_win32_path buf;
+	return p_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+}
 
-	if (git_win32_path_from_utf8(buf, path) < 0)
+int p_utimes(const char *path, const struct p_timeval times[2])
+{
+	git_win32_path wpath;
+	int fd, error;
+	DWORD attrs_orig, attrs_new = 0;
+	struct open_opts opts = { 0 };
+
+	if (git_win32_path_from_utf8(wpath, path) < 0)
 		return -1;
 
-	return _wopen(buf,
-		_O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS,
-		mode & WIN32_MODE_MASK);
+	attrs_orig = GetFileAttributesW(wpath);
+
+	if (attrs_orig & FILE_ATTRIBUTE_READONLY) {
+		attrs_new = attrs_orig & ~FILE_ATTRIBUTE_READONLY;
+
+		if (!SetFileAttributesW(wpath, attrs_new)) {
+			giterr_set(GITERR_OS, "failed to set attributes");
+			return -1;
+		}
+	}
+
+	open_opts_from_posix(&opts, O_RDWR, 0);
+
+	if ((fd = open_once(wpath, &opts)) < 0) {
+		error = -1;
+		goto done;
+	}
+
+	error = p_futimes(fd, times);
+	close(fd);
+
+done:
+	if (attrs_orig != attrs_new) {
+		DWORD os_error = GetLastError();
+		SetFileAttributesW(wpath, attrs_orig);
+		SetLastError(os_error);
+	}
+
+	return error;
+}
+
+int p_futimes(int fd, const struct p_timeval times[2])
+{
+	HANDLE handle;
+	FILETIME atime = { 0 }, mtime = { 0 };
+
+	if (times == NULL) {
+		SYSTEMTIME st;
+
+		GetSystemTime(&st);
+		SystemTimeToFileTime(&st, &atime);
+		SystemTimeToFileTime(&st, &mtime);
+	}
+	else {
+		git_win32__timeval_to_filetime(&atime, times[0]);
+		git_win32__timeval_to_filetime(&mtime, times[1]);
+	}
+
+	if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
+		return -1;
+
+	if (SetFileTime(handle, NULL, &atime, &mtime) == 0)
+		return -1;
+
+	return 0;
 }
 
 int p_getcwd(char *buffer_out, size_t size)
@@ -581,62 +839,27 @@
 	return _waccess(buf, mode & WIN32_MODE_MASK);
 }
 
-static int ensure_writable(wchar_t *fpath)
+GIT_INLINE(int) rename_once(const wchar_t *from, const wchar_t *to)
 {
-	DWORD attrs;
-
-	attrs = GetFileAttributesW(fpath);
-	if (attrs == INVALID_FILE_ATTRIBUTES) {
-		if (GetLastError() == ERROR_FILE_NOT_FOUND)
-			return 0;
-
-		giterr_set(GITERR_OS, "failed to get attributes");
-		return -1;
-	}
-
-	if (!(attrs & FILE_ATTRIBUTE_READONLY))
+	if (MoveFileExW(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
 		return 0;
 
-	attrs &= ~FILE_ATTRIBUTE_READONLY;
-	if (!SetFileAttributesW(fpath, attrs)) {
-		giterr_set(GITERR_OS, "failed to set attributes");
-		return -1;
-	}
+	if (last_error_retryable())
+		return GIT_RETRY;
 
-	return 0;
+	set_errno();
+	return -1;
 }
 
 int p_rename(const char *from, const char *to)
 {
-	git_win32_path wfrom;
-	git_win32_path wto;
-	int rename_tries;
-	int rename_succeeded;
-	int error;
+	git_win32_path wfrom, wto;
 
 	if (git_win32_path_from_utf8(wfrom, from) < 0 ||
 		git_win32_path_from_utf8(wto, to) < 0)
 		return -1;
 
-	/* wait up to 50ms if file is locked by another thread or process */
-	rename_tries = 0;
-	rename_succeeded = 0;
-	while (rename_tries < 10) {
-		if (ensure_writable(wto) == 0 &&
-		    MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) {
-			rename_succeeded = 1;
-			break;
-		}
-		
-		error = GetLastError();
-		if (error == ERROR_SHARING_VIOLATION || error == ERROR_ACCESS_DENIED) {
-			Sleep(5);
-			rename_tries++;
-		} else
-			break;
-	}
-	
-	return rename_succeeded ? 0 : -1;
+	do_with_retries(rename_once(wfrom, wto), ensure_writable(wto));
 }
 
 int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
diff --git a/src/worktree.c b/src/worktree.c
new file mode 100644
index 0000000..ede155b
--- /dev/null
+++ b/src/worktree.c
@@ -0,0 +1,558 @@
+/*
+ * 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:
+	git__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_init_options(git_worktree_add_options *opts,
+	unsigned int version)
+{
+	GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version,
+		git_worktree_add_options, GIT_WORKTREE_ADD_OPTIONS_INIT);
+	return 0;
+}
+
+int git_worktree_add(git_worktree **out, git_repository *repo,
+	const char *name, const char *worktree,
+	const git_worktree_add_options *opts)
+{
+	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;
+	git_worktree_add_options wtopts = GIT_WORKTREE_ADD_OPTIONS_INIT;
+	int err;
+
+	GITERR_CHECK_VERSION(
+		opts, GIT_WORKTREE_ADD_OPTIONS_VERSION, "git_worktree_add_options");
+
+	if (opts)
+		memcpy(&wtopts, opts, sizeof(wtopts));
+
+	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;
+
+	if (wtopts.lock) {
+		int fd;
+
+		if ((err = git_buf_joinpath(&buf, gitdir.ptr, "locked")) < 0)
+			goto out;
+
+		if ((fd = p_creat(buf.ptr, 0644)) < 0) {
+			err = fd;
+			goto out;
+		}
+
+		p_close(fd);
+		git_buf_clear(&buf);
+	}
+
+	/* 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_prune_init_options(
+	git_worktree_prune_options *opts,
+	unsigned int version)
+{
+	GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version,
+		git_worktree_prune_options, GIT_WORKTREE_PRUNE_OPTIONS_INIT);
+	return 0;
+}
+
+int git_worktree_is_prunable(git_worktree *wt,
+	git_worktree_prune_options *opts)
+{
+	git_buf reason = GIT_BUF_INIT;
+	git_worktree_prune_options popts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+
+	GITERR_CHECK_VERSION(
+		opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION,
+		"git_worktree_prune_options");
+
+	if (opts)
+		memcpy(&popts, opts, sizeof(popts));
+
+	if ((popts.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 ((popts.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,
+	git_worktree_prune_options *opts)
+{
+	git_worktree_prune_options popts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+	git_buf path = GIT_BUF_INIT;
+	char *wtpath;
+	int err;
+
+	GITERR_CHECK_VERSION(
+		opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION,
+		"git_worktree_prune_options");
+
+	if (opts)
+		memcpy(&popts, opts, sizeof(popts));
+
+	if (!git_worktree_is_prunable(wt, &popts)) {
+		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 ((popts.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/tests/attr/ignore.c b/tests/attr/ignore.c
index f1fe1c7..a089ee4 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",
@@ -291,3 +291,15 @@
 	assert_is_ignored(true, "symlink");
 	assert_is_ignored(true, "lala/../symlink");
 }
+
+void test_attr_ignore__test(void)
+{
+	cl_git_rewritefile("attr/.gitignore",
+		"/*/\n"
+		"!/src\n");
+	assert_is_ignored(false, "src/foo.c");
+	assert_is_ignored(false, "src/foo/foo.c");
+	assert_is_ignored(false, "README.md");
+	assert_is_ignored(true, "dist/foo.o");
+	assert_is_ignored(true, "bin/foo");
+}
diff --git a/tests/checkout/head.c b/tests/checkout/head.c
index 07cc1d2..ded86df 100644
--- a/tests/checkout/head.c
+++ b/tests/checkout/head.c
@@ -38,7 +38,7 @@
 	cl_git_pass(git_repository_index(&index, g_repo));
 
 	p_mkdir("testrepo/newdir", 0777);
-    cl_git_mkfile("testrepo/newdir/newfile.txt", "new file\n");
+	cl_git_mkfile("testrepo/newdir/newfile.txt", "new file\n");
 
 	cl_git_pass(git_index_add_bypath(index, "newdir/newfile.txt"));
 	cl_git_pass(git_index_write(index));
@@ -60,3 +60,79 @@
 
 	git_index_free(index);
 }
+
+void test_checkout_head__do_not_remove_untracked_file(void)
+{
+	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+	git_index *index;
+
+	cl_git_pass(p_mkdir("testrepo/tracked", 0755));
+	cl_git_mkfile("testrepo/tracked/tracked", "tracked\n");
+	cl_git_mkfile("testrepo/tracked/untracked", "untracked\n");
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+	cl_git_pass(git_index_add_bypath(index, "tracked/tracked"));
+	cl_git_pass(git_index_write(index));
+
+	git_index_free(index);
+
+	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+	cl_git_pass(git_checkout_head(g_repo, &opts));
+
+	cl_assert(!git_path_isfile("testrepo/tracked/tracked"));
+	cl_assert(git_path_isfile("testrepo/tracked/untracked"));
+}
+
+void test_checkout_head__do_not_remove_untracked_file_in_subdir(void)
+{
+	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+	git_index *index;
+
+	cl_git_pass(p_mkdir("testrepo/tracked", 0755));
+	cl_git_pass(p_mkdir("testrepo/tracked/subdir", 0755));
+	cl_git_mkfile("testrepo/tracked/tracked", "tracked\n");
+	cl_git_mkfile("testrepo/tracked/subdir/tracked", "tracked\n");
+	cl_git_mkfile("testrepo/tracked/subdir/untracked", "untracked\n");
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+	cl_git_pass(git_index_add_bypath(index, "tracked/tracked"));
+	cl_git_pass(git_index_add_bypath(index, "tracked/subdir/tracked"));
+	cl_git_pass(git_index_write(index));
+
+	git_index_free(index);
+
+	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+	cl_git_pass(git_checkout_head(g_repo, &opts));
+
+	cl_assert(!git_path_isfile("testrepo/tracked/tracked"));
+	cl_assert(!git_path_isfile("testrepo/tracked/subdir/tracked"));
+	cl_assert(git_path_isfile("testrepo/tracked/subdir/untracked"));
+}
+
+void test_checkout_head__do_remove_tracked_subdir(void)
+{
+	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+	git_index *index;
+
+	cl_git_pass(p_mkdir("testrepo/subdir", 0755));
+	cl_git_pass(p_mkdir("testrepo/subdir/tracked", 0755));
+	cl_git_mkfile("testrepo/subdir/tracked-file", "tracked\n");
+	cl_git_mkfile("testrepo/subdir/untracked-file", "untracked\n");
+	cl_git_mkfile("testrepo/subdir/tracked/tracked1", "tracked\n");
+	cl_git_mkfile("testrepo/subdir/tracked/tracked2", "tracked\n");
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+	cl_git_pass(git_index_add_bypath(index, "subdir/tracked-file"));
+	cl_git_pass(git_index_add_bypath(index, "subdir/tracked/tracked1"));
+	cl_git_pass(git_index_add_bypath(index, "subdir/tracked/tracked2"));
+	cl_git_pass(git_index_write(index));
+
+	git_index_free(index);
+
+	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+	cl_git_pass(git_checkout_head(g_repo, &opts));
+
+	cl_assert(!git_path_isdir("testrepo/subdir/tracked"));
+	cl_assert(!git_path_isfile("testrepo/subdir/tracked-file"));
+	cl_assert(git_path_isfile("testrepo/subdir/untracked-file"));
+}
diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c
index c9581fd..b3b860c 100644
--- a/tests/checkout/tree.c
+++ b/tests/checkout/tree.c
@@ -441,6 +441,7 @@
 	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 =
@@ -454,10 +455,6 @@
 	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);
-	g_object = NULL;
-
 }
 
 void test_checkout_tree__can_disable_pattern_match(void)
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 fc08bbf..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,7 +41,7 @@
 	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)
 
@@ -86,7 +90,7 @@
 		clar__assert(0, threaderr->file, threaderr->line, threaderr->expr, threaderr->error_msg, 1);
 }
 
-void cl_git_report_failure(int, const char *, int, const char *);
+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/clone/local.c b/tests/clone/local.c
index 91a0a1c..7f54d05 100644
--- a/tests/clone/local.c
+++ b/tests/clone/local.c
@@ -16,6 +16,7 @@
 	return git_buf_printf(buf, "file://%s/%s", host, path);
 }
 
+#ifdef GIT_WIN32
 static int git_style_unc_path(git_buf *buf, const char *host, const char *path)
 {
 	git_buf_clear(buf);
@@ -49,6 +50,7 @@
 
 	return 0;
 }
+#endif
 
 void test_clone_local__should_clone_local(void)
 {
diff --git a/tests/config/include.c b/tests/config/include.c
index 882b89b..0a07c9b 100644
--- a/tests/config/include.c
+++ b/tests/config/include.c
@@ -108,6 +108,26 @@
 	git_config_free(cfg);
 }
 
+void test_config_include__missing_homedir(void)
+{
+	git_config *cfg;
+	git_buf buf = GIT_BUF_INIT;
+
+	cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config")));
+	cl_git_mkfile("including", "[include]\npath = ~/.nonexistentfile\n[foo]\nbar = baz");
+
+	giterr_clear();
+	cl_git_pass(git_config_open_ondisk(&cfg, "including"));
+	cl_assert(giterr_last() == NULL);
+	cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+	cl_assert_equal_s("baz", git_buf_cstr(&buf));
+
+	git_buf_free(&buf);
+	git_config_free(cfg);
+
+	cl_sandbox_set_search_path_defaults();
+}
+
 #define replicate10(s) s s s s s s s s s s
 void test_config_include__depth2(void)
 {
diff --git a/tests/config/readonly.c b/tests/config/readonly.c
new file mode 100644
index 0000000..f45abdd
--- /dev/null
+++ b/tests/config/readonly.c
@@ -0,0 +1,64 @@
+#include "clar_libgit2.h"
+#include "config_file.h"
+#include "config.h"
+
+static git_config *cfg;
+
+void test_config_readonly__initialize(void)
+{
+	cl_git_pass(git_config_new(&cfg));
+}
+
+void test_config_readonly__cleanup(void)
+{
+	git_config_free(cfg);
+	cfg = NULL;
+}
+
+void test_config_readonly__writing_to_readonly_fails(void)
+{
+	git_config_backend *backend;
+
+	cl_git_pass(git_config_file__ondisk(&backend, "global"));
+	backend->readonly = 1;
+	cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+	cl_git_fail_with(GIT_ENOTFOUND, git_config_set_string(cfg, "foo.bar", "baz"));
+	cl_assert(!git_path_exists("global"));
+}
+
+void test_config_readonly__writing_to_cfg_with_rw_precedence_succeeds(void)
+{
+	git_config_backend *backend;
+
+	cl_git_pass(git_config_file__ondisk(&backend, "global"));
+	backend->readonly = 1;
+	cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+	cl_git_pass(git_config_file__ondisk(&backend, "local"));
+	cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0));
+
+	cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
+
+	cl_assert(git_path_exists("local"));
+	cl_assert(!git_path_exists("global"));
+	cl_git_pass(p_unlink("local"));
+}
+
+void test_config_readonly__writing_to_cfg_with_ro_precedence_succeeds(void)
+{
+	git_config_backend *backend;
+
+	cl_git_pass(git_config_file__ondisk(&backend, "local"));
+	backend->readonly = 1;
+	cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0));
+
+	cl_git_pass(git_config_file__ondisk(&backend, "global"));
+	cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+	cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
+
+	cl_assert(!git_path_exists("local"));
+	cl_assert(git_path_exists("global"));
+	cl_git_pass(p_unlink("global"));
+}
diff --git a/tests/core/encoding.c b/tests/core/encoding.c
index 7d91720..a677afe 100644
--- a/tests/core/encoding.c
+++ b/tests/core/encoding.c
@@ -29,6 +29,9 @@
 	cl_assert(git_encode_varint(buf, 100, 65) == 1);
 	cl_assert(buf[0] == 'A');
 
+	cl_assert(git_encode_varint(buf, 1, 1) == 1);
+	cl_assert(!memcmp(buf, "\x01", 1));
+
 	cl_assert(git_encode_varint(buf, 100, 267869656) == 4);
 	cl_assert(!memcmp(buf, "\xfe\xdc\xbaX", 4));
 
diff --git a/tests/core/features.c b/tests/core/features.c
index cf5e190..7b28cc0 100644
--- a/tests/core/features.c
+++ b/tests/core/features.c
@@ -17,7 +17,9 @@
 	cl_assert((caps & GIT_FEATURE_THREADS) == 0);
 #endif
 
+#ifdef GIT_HTTPS
 	cl_assert((caps & GIT_FEATURE_HTTPS) != 0);
+#endif
 
 #if defined(GIT_SSH)
 	cl_assert((caps & GIT_FEATURE_SSH) != 0);
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/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/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/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/filter/custom.c b/tests/filter/custom.c
index fd1cd27..799beef 100644
--- a/tests/filter/custom.c
+++ b/tests/filter/custom.c
@@ -55,6 +55,7 @@
 		"hero* bitflip reverse\n"
 		"herofile text\n"
 		"heroflip -reverse binary\n"
+		"villain erroneous\n"
 		"*.bin binary\n");
 }
 
@@ -82,6 +83,11 @@
 			create_reverse_filter("+prereverse"),
 			GIT_FILTER_DRIVER_PRIORITY));
 
+		cl_git_pass(git_filter_register(
+			"erroneous",
+			create_erroneous_filter("+erroneous"),
+			GIT_FILTER_DRIVER_PRIORITY));
+
 		filters_registered = 1;
 	}
 }
@@ -235,3 +241,18 @@
 	cl_git_fail(git_filter_unregister(GIT_FILTER_IDENT));
 	cl_assert_equal_i(GIT_ENOTFOUND, git_filter_unregister("not-a-filter"));
 }
+
+void test_filter_custom__erroneous_filter_fails(void)
+{
+	git_filter_list *filters;
+	git_buf out = GIT_BUF_INIT;
+	git_buf in = GIT_BUF_INIT_CONST(workdir_data, strlen(workdir_data));
+
+	cl_git_pass(git_filter_list_load(
+		&filters, g_repo, NULL, "villain", GIT_FILTER_TO_WORKTREE, 0));
+
+	cl_git_fail(git_filter_list_apply_to_data(&out, filters, &in));
+
+	git_filter_list_free(filters);
+	git_buf_free(&out);
+}
diff --git a/tests/filter/custom_helpers.c b/tests/filter/custom_helpers.c
index 2c80212..d7f2afe 100644
--- a/tests/filter/custom_helpers.c
+++ b/tests/filter/custom_helpers.c
@@ -106,3 +106,36 @@
 
 	return filter;
 }
+
+int erroneous_filter_stream(
+	git_writestream **out,
+	git_filter *self,
+	void **payload,
+	const git_filter_source *src,
+	git_writestream *next)
+{
+	GIT_UNUSED(out);
+	GIT_UNUSED(self);
+	GIT_UNUSED(payload);
+	GIT_UNUSED(src);
+	GIT_UNUSED(next);
+	return -1;
+}
+
+static void erroneous_filter_free(git_filter *f)
+{
+	git__free(f);
+}
+
+git_filter *create_erroneous_filter(const char *attrs)
+{
+	git_filter *filter = git__calloc(1, sizeof(git_filter));
+	cl_assert(filter);
+
+	filter->version = GIT_FILTER_VERSION;
+	filter->attributes = attrs;
+	filter->stream = erroneous_filter_stream;
+	filter->shutdown = erroneous_filter_free;
+
+	return filter;
+}
diff --git a/tests/filter/custom_helpers.h b/tests/filter/custom_helpers.h
index 13cfb23..537a51d 100644
--- a/tests/filter/custom_helpers.h
+++ b/tests/filter/custom_helpers.h
@@ -2,6 +2,7 @@
 
 extern git_filter *create_bitflip_filter(void);
 extern git_filter *create_reverse_filter(const char *attr);
+extern git_filter *create_erroneous_filter(const char *attr);
 
 extern int bitflip_filter_apply(
 	git_filter     *self,
diff --git a/tests/index/version.c b/tests/index/version.c
index 3fd240d..7ada302 100644
--- a/tests/index/version.c
+++ b/tests/index/version.c
@@ -3,39 +3,135 @@
 
 static git_repository *g_repo = NULL;
 
+void test_index_version__cleanup(void)
+{
+        cl_git_sandbox_cleanup();
+        g_repo = NULL;
+}
+
+void test_index_version__can_read_v4(void)
+{
+	const char *paths[] = {
+	    "file.tx", "file.txt", "file.txz", "foo", "zzz",
+	};
+	git_index *index;
+	size_t i;
+
+	g_repo = cl_git_sandbox_init("indexv4");
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+	cl_assert_equal_sz(git_index_entrycount(index), 5);
+
+	for (i = 0; i < ARRAY_SIZE(paths); i++) {
+		const git_index_entry *entry =
+		    git_index_get_bypath(index, paths[i], GIT_INDEX_STAGE_NORMAL);
+
+		cl_assert(entry != NULL);
+	}
+
+	git_index_free(index);
+}
+
 void test_index_version__can_write_v4(void)
 {
+	const char *paths[] = {
+	    "foo",
+	    "foox",
+	    "foobar",
+	    "foobal",
+	    "x",
+	    "xz",
+	    "xyzzyx"
+	};
+	git_index_entry entry;
 	git_index *index;
-	const git_index_entry *entry;
+	size_t i;
 
-	g_repo = cl_git_sandbox_init("filemodes");
+	g_repo = cl_git_sandbox_init("empty_standard_repo");
 	cl_git_pass(git_repository_index(&index, g_repo));
-
-	cl_assert(index->on_disk);
-	cl_assert(git_index_version(index) == 2);
-
-	cl_assert(git_index_entrycount(index) == 6);
-
 	cl_git_pass(git_index_set_version(index, 4));
 
+	for (i = 0; i < ARRAY_SIZE(paths); i++) {
+		memset(&entry, 0, sizeof(entry));
+		entry.path = paths[i];
+		entry.mode = GIT_FILEMODE_BLOB;
+		cl_git_pass(git_index_add_frombuffer(index, &entry, paths[i],
+						     strlen(paths[i]) + 1));
+	}
+	cl_assert_equal_sz(git_index_entrycount(index), ARRAY_SIZE(paths));
+
 	cl_git_pass(git_index_write(index));
 	git_index_free(index);
 
 	cl_git_pass(git_repository_index(&index, g_repo));
 	cl_assert(git_index_version(index) == 4);
 
-	entry = git_index_get_bypath(index, "exec_off", 0);
-	cl_assert(entry);
-	entry = git_index_get_bypath(index, "exec_off2on_staged", 0);
-	cl_assert(entry);
-	entry = git_index_get_bypath(index, "exec_on", 0);
-	cl_assert(entry);
+	for (i = 0; i < ARRAY_SIZE(paths); i++) {
+		const git_index_entry *e;
+
+		cl_assert(e = git_index_get_bypath(index, paths[i], 0));
+		cl_assert_equal_s(paths[i], e->path);
+	}
 
 	git_index_free(index);
 }
 
-void test_index_version__cleanup(void)
+void test_index_version__v4_uses_path_compression(void)
 {
-        cl_git_sandbox_cleanup();
-        g_repo = NULL;
+	git_index_entry entry;
+	git_index *index;
+	char path[250], buf[1];
+	struct stat st;
+	char i, j;
+
+	memset(path, 'a', sizeof(path));
+	memset(buf, 'a', sizeof(buf));
+
+	memset(&entry, 0, sizeof(entry));
+	entry.path = path;
+	entry.mode = GIT_FILEMODE_BLOB;
+
+	g_repo = cl_git_sandbox_init("indexv4");
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	/* write 676 paths of 250 bytes length */
+	for (i = 'a'; i <= 'z'; i++) {
+		for (j = 'a'; j < 'z'; j++) {
+			path[ARRAY_SIZE(path) - 3] = i;
+			path[ARRAY_SIZE(path) - 2] = j;
+			path[ARRAY_SIZE(path) - 1] = '\0';
+			cl_git_pass(git_index_add_frombuffer(index, &entry, buf, sizeof(buf)));
+		}
+	}
+
+	cl_git_pass(git_index_write(index));
+	cl_git_pass(p_stat(git_index_path(index), &st));
+
+	/*
+	 * Without path compression, the written paths would at
+	 * least take
+	 *
+	 *    (entries * pathlen) = len
+	 *    (676 * 250) = 169000
+	 *
+	 *  bytes. As index v4 uses suffix-compression and our
+	 *  written paths only differ in the last two entries,
+	 *  this number will be much smaller, e.g.
+	 *
+	 *    (1 * pathlen) + (675 * 2) = len
+	 *    676 + 1350 = 2026
+	 *
+	 *    bytes.
+	 *
+	 *    Note that the above calculations do not include
+	 *    additional metadata of the index, e.g. OIDs or
+	 *    index extensions. Including those we get an index
+	 *    of approx. 200kB without compression and 40kB with
+	 *    compression. As this is a lot smaller than without
+	 *    compression, we can verify that path compression is
+	 *    used.
+	 */
+	cl_assert_(st.st_size < 75000, "path compression not enabled");
+
+	git_index_free(index);
 }
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 6f5c36b..853bf2f 100644
--- a/tests/merge/trees/renames.c
+++ b/tests/merge/trees/renames.c
@@ -252,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/lookup.c b/tests/object/lookup.c
index cfa6d46..544f32b 100644
--- a/tests/object/lookup.c
+++ b/tests/object/lookup.c
@@ -6,13 +6,12 @@
 
 void test_object_lookup__initialize(void)
 {
-   cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+	g_repo = cl_git_sandbox_init("testrepo.git");
 }
 
 void test_object_lookup__cleanup(void)
 {
-	git_repository_free(g_repo);
-	g_repo = NULL;
+	cl_git_sandbox_cleanup();
 }
 
 void test_object_lookup__lookup_wrong_type_returns_enotfound(void)
@@ -63,3 +62,61 @@
 		GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJ_TAG));
 }
 
+void test_object_lookup__lookup_corrupt_object_returns_error(void)
+{
+	const char *commit = "8e73b769e97678d684b809b163bebdae2911720f",
+	      *file = "objects/8e/73b769e97678d684b809b163bebdae2911720f";
+	git_buf path = GIT_BUF_INIT, contents = GIT_BUF_INIT;
+	git_oid oid;
+	git_object *object;
+	size_t i;
+
+	cl_git_pass(git_oid_fromstr(&oid, commit));
+	cl_git_pass(git_buf_joinpath(&path, git_repository_path(g_repo), file));
+	cl_git_pass(git_futils_readbuffer(&contents, path.ptr));
+
+	/* Corrupt and try to read the object */
+	for (i = 0; i < contents.size; i++) {
+		contents.ptr[i] ^= 0x1;
+		cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644));
+		cl_git_fail(git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT));
+		contents.ptr[i] ^= 0x1;
+	}
+
+	/* Restore original content and assert we can read the object */
+	cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644));
+	cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT));
+
+	git_object_free(object);
+	git_buf_free(&path);
+	git_buf_free(&contents);
+}
+
+void test_object_lookup__lookup_object_with_wrong_hash_returns_error(void)
+{
+	const char *oldloose = "objects/8e/73b769e97678d684b809b163bebdae2911720f",
+	      *newloose = "objects/8e/73b769e97678d684b809b163bebdae2911720e",
+	      *commit = "8e73b769e97678d684b809b163bebdae2911720e";
+	git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT;
+	git_object *object;
+	git_oid oid;
+
+	cl_git_pass(git_oid_fromstr(&oid, commit));
+
+	/* Copy object to another location with wrong hash */
+	cl_git_pass(git_buf_joinpath(&oldpath, git_repository_path(g_repo), oldloose));
+	cl_git_pass(git_buf_joinpath(&newpath, git_repository_path(g_repo), newloose));
+	cl_git_pass(git_futils_cp(oldpath.ptr, newpath.ptr, 0644));
+
+	/* Verify that lookup fails due to a hashsum mismatch */
+	cl_git_fail_with(GIT_EMISMATCH, git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT));
+
+	/* Disable verification and try again */
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0));
+	cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT));
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1));
+
+	git_object_free(object);
+	git_buf_free(&oldpath);
+	git_buf_free(&newpath);
+}
diff --git a/tests/odb/backend/nonrefreshing.c b/tests/odb/backend/nonrefreshing.c
index b435294..f12ac74 100644
--- a/tests/odb/backend/nonrefreshing.c
+++ b/tests/odb/backend/nonrefreshing.c
@@ -17,6 +17,9 @@
 static fake_backend *_fake;
 static git_oid _oid;
 
+#define HASH "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+#define EMPTY_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
+
 static int fake_backend__exists(git_odb_backend *backend, const git_oid *oid)
 {
 	fake_backend *fake;
@@ -78,7 +81,6 @@
 {
 	fake_backend *fake;
 
-	GIT_UNUSED(out_oid);
 	GIT_UNUSED(buffer_p);
 	GIT_UNUSED(len_p);
 	GIT_UNUSED(type_p);
@@ -89,6 +91,7 @@
 
 	fake->read_prefix_calls++;
 
+	git_oid_cpy(out_oid, &_oid);
 	*len_p = 0;
 	*buffer_p = NULL;
 	*type_p = GIT_OBJ_BLOB;
@@ -130,7 +133,7 @@
 	return 0;
 }
 
-static void setup_repository_and_backend(git_error_code error_code)
+static void setup_repository_and_backend(git_error_code error_code, const char *hash)
 {
 	git_odb *odb = NULL;
 	git_odb_backend *backend = NULL;
@@ -144,7 +147,7 @@
 
 	_fake = (fake_backend *)backend;
 
-	cl_git_pass(git_oid_fromstr(&_oid, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+	cl_git_pass(git_oid_fromstr(&_oid, hash));
 }
 
 void test_odb_backend_nonrefreshing__cleanup(void)
@@ -156,7 +159,7 @@
 {
 	git_odb *odb;
 
-	setup_repository_and_backend(GIT_ENOTFOUND);
+	setup_repository_and_backend(GIT_ENOTFOUND, HASH);
 
 	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
 	cl_assert_equal_b(false, git_odb_exists(odb, &_oid));
@@ -168,7 +171,7 @@
 {
 	git_object *obj;
 
-	setup_repository_and_backend(GIT_ENOTFOUND);
+	setup_repository_and_backend(GIT_ENOTFOUND, HASH);
 
 	cl_git_fail_with(
 		git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY),
@@ -181,7 +184,7 @@
 {
 	git_object *obj;
 
-	setup_repository_and_backend(GIT_ENOTFOUND);
+	setup_repository_and_backend(GIT_ENOTFOUND, HASH);
 
 	cl_git_fail_with(
 		git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY),
@@ -196,7 +199,7 @@
 	size_t len;
 	git_otype type;
 
-	setup_repository_and_backend(GIT_ENOTFOUND);
+	setup_repository_and_backend(GIT_ENOTFOUND, HASH);
 
 	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
 
@@ -211,7 +214,7 @@
 {
 	git_odb *odb;
 
-	setup_repository_and_backend(GIT_OK);
+	setup_repository_and_backend(GIT_OK, HASH);
 
 	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
 	cl_assert_equal_b(true, git_odb_exists(odb, &_oid));
@@ -223,7 +226,7 @@
 {
 	git_object *obj;
 
-	setup_repository_and_backend(GIT_OK);
+	setup_repository_and_backend(GIT_OK, EMPTY_HASH);
 
 	cl_git_pass(git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY));
 
@@ -236,7 +239,7 @@
 {
 	git_object *obj;
 
-	setup_repository_and_backend(GIT_OK);
+	setup_repository_and_backend(GIT_OK, EMPTY_HASH);
 
 	cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY));
 
@@ -251,7 +254,7 @@
 	size_t len;
 	git_otype type;
 
-	setup_repository_and_backend(GIT_OK);
+	setup_repository_and_backend(GIT_OK, HASH);
 
 	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
 
@@ -264,7 +267,7 @@
 {
 	git_object *obj;
 
-	setup_repository_and_backend(GIT_ENOTFOUND);
+	setup_repository_and_backend(GIT_ENOTFOUND, HASH);
 
 	cl_git_fail_with(
 		git_revparse_single(&obj, _repo, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
diff --git a/tests/odb/freshen.c b/tests/odb/freshen.c
index d8d6c02..9d3cf51 100644
--- a/tests/odb/freshen.c
+++ b/tests/odb/freshen.c
@@ -17,36 +17,126 @@
 	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 UNIQUE_STR     "doesnt exist in the odb yet\n"
+#define UNIQUE_BLOB_ID "78a87d0b8878c5953b9a63015ff4e22a3d898826"
+#define UNIQUE_BLOB_FN "78/a87d0b8878c5953b9a63015ff4e22a3d898826"
+
+void test_odb_freshen__readonly_object(void)
+{
+	git_oid expected_id, id;
+	struct stat before, after;
+
+	cl_git_pass(git_oid_fromstr(&expected_id, UNIQUE_BLOB_ID));
+
+	cl_git_pass(git_blob_create_frombuffer(&id, repo, UNIQUE_STR, CONST_STRLEN(UNIQUE_STR)));
+	cl_assert_equal_oid(&expected_id, &id);
+
+	set_time_wayback(&before, UNIQUE_BLOB_FN);
+	cl_assert((before.st_mode & S_IWUSR) == 0);
+
+	cl_git_pass(git_blob_create_frombuffer(&id, repo, UNIQUE_STR, CONST_STRLEN(UNIQUE_STR)));
+	cl_assert_equal_oid(&expected_id, &id);
+	cl_must_pass(p_lstat("testrepo.git/objects/" UNIQUE_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..2e24d67 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_FSYNC_GITDIR, 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_FSYNC_GITDIR, 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 aa4c24d..6524fcd 100644
--- a/tests/online/badssl.c
+++ b/tests/online/badssl.c
@@ -4,7 +4,7 @@
 
 static git_repository *g_repo;
 
-#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
+#ifdef GIT_HTTPS
 static bool g_has_ssl = true;
 #else
 static bool g_has_ssl = false;
diff --git a/tests/pack/packbuilder.c b/tests/pack/packbuilder.c
index 9ad52f3..13ee073 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_FSYNC_GITDIR, 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_FSYNC_GITDIR, 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/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/create.c b/tests/refs/create.c
index 6d5a5f1..469cddd 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_FSYNC_GITDIR, 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_FSYNC_GITDIR, 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 9e46c8a..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"
@@ -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/refs/revparse.c b/tests/refs/revparse.c
index c22c304..459188c 100644
--- a/tests/refs/revparse.c
+++ b/tests/refs/revparse.c
@@ -122,6 +122,14 @@
 	test_id_inrepo(spec, expected_left, expected_right, expected_flags, g_repo);
 }
 
+static void test_invalid_revspec(const char* invalid_spec)
+{
+	git_revspec revspec;
+
+	cl_assert_equal_i(
+		GIT_EINVALIDSPEC, git_revparse(&revspec, g_repo, invalid_spec));
+}
+
 void test_refs_revparse__initialize(void)
 {
 	cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
@@ -749,6 +757,33 @@
 		"4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
 		GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+
+	test_id("HEAD~3..",
+		"4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+		GIT_REVPARSE_RANGE);
+
+	test_id("HEAD~3...",
+		"4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+		GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+
+	test_id("..HEAD~3",
+		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+		"4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+		GIT_REVPARSE_RANGE);
+
+	test_id("...HEAD~3",
+		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+		"4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+		GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+
+	test_id("...",
+		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+		GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+
+	test_invalid_revspec("..");
 }
 
 void test_refs_revparse__ext_retrieves_both_the_reference_and_its_target(void)
diff --git a/tests/repo/discover.c b/tests/repo/discover.c
index 48aa275..eadd055 100644
--- a/tests/repo/discover.c
+++ b/tests/repo/discover.c
@@ -33,7 +33,7 @@
 	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));
+	cl_git_pass(git_repository_discover(&found_path, start_path, 1, ceiling_dirs));
 
 	cl_assert_equal_s(found_path.ptr, resolved.ptr);
 
@@ -199,3 +199,12 @@
 
 	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/repo/head.c b/tests/repo/head.c
index 31c2287..d021160 100644
--- a/tests/repo/head.c
+++ b/tests/repo/head.c
@@ -261,15 +261,19 @@
 	cl_git_pass(git_revparse_single(&tag, repo, "tags/test"));
 	cl_git_pass(git_repository_set_head_detached(repo, git_object_id(tag)));
 	cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
+	cl_git_pass(git_repository_set_head(repo, "refs/tags/test"));
+	cl_git_pass(git_repository_set_head(repo, "refs/remotes/test/master"));
 
-	test_reflog(repo, 2, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from master to haacked");
-	test_reflog(repo, 1, NULL, "tags/test^{commit}", "foo@example.com", "checkout: moving from unborn to e90810b8df3e80c413d903f631643c716887138d");
-	test_reflog(repo, 0, "tags/test^{commit}", "refs/heads/haacked", "foo@example.com", "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked");
+	test_reflog(repo, 4, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from master to haacked");
+	test_reflog(repo, 3, NULL, "tags/test^{commit}", "foo@example.com", "checkout: moving from unborn to e90810b8df3e80c413d903f631643c716887138d");
+	test_reflog(repo, 2, "tags/test^{commit}", "refs/heads/haacked", "foo@example.com", "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked");
+	test_reflog(repo, 1, "refs/heads/haacked", "tags/test^{commit}", "foo@example.com", "checkout: moving from haacked to test");
+	test_reflog(repo, 0, "tags/test^{commit}", "refs/remotes/test/master", "foo@example.com", "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to test/master");
 
 	cl_git_pass(git_annotated_commit_from_revspec(&annotated, repo, "haacked~0"));
 	cl_git_pass(git_repository_set_head_detached_from_annotated(repo, annotated));
 
-	test_reflog(repo, 0, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from haacked to haacked~0");
+	test_reflog(repo, 0, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from be3563ae3f795b2b4353bcce3a527ad0a4f7f644 to haacked~0");
 
 	git_annotated_commit_free(annotated);
 	git_object_free(tag);
diff --git a/tests/repo/open.c b/tests/repo/open.c
index 6114ad2..3239b6f 100644
--- a/tests/repo/open.c
+++ b/tests/repo/open.c
@@ -398,7 +398,8 @@
 	cl_git_fail(git_repository_open_bare(&barerepo, "alternate/subdir/sub2"));
 
 	cl_git_pass(git_repository_open_ext(
-		&barerepo, "alternate/subdir/sub2", GIT_REPOSITORY_OPEN_BARE, NULL));
+		&barerepo, "alternate/subdir/sub2",
+		GIT_REPOSITORY_OPEN_BARE|GIT_REPOSITORY_OPEN_CROSS_FS, NULL));
 	cl_assert(git_repository_is_bare(barerepo));
 	git_repository_free(barerepo);
 }
diff --git a/tests/resources/indexv4/.gitted/HEAD b/tests/resources/indexv4/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/indexv4/.gitted/config b/tests/resources/indexv4/.gitted/config
new file mode 100644
index 0000000..515f483
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/config
@@ -0,0 +1,5 @@
+[core]
+	repositoryformatversion = 0
+	filemode = true
+	bare = false
+	logallrefupdates = true
diff --git a/tests/resources/indexv4/.gitted/index b/tests/resources/indexv4/.gitted/index
new file mode 100644
index 0000000..e8fc617
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/index
Binary files differ
diff --git a/tests/resources/indexv4/.gitted/objects/4c/9109b3e671d851eec87e0e72f6305b582e7e99 b/tests/resources/indexv4/.gitted/objects/4c/9109b3e671d851eec87e0e72f6305b582e7e99
new file mode 100644
index 0000000..cedd594
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/objects/4c/9109b3e671d851eec87e0e72f6305b582e7e99
Binary files differ
diff --git a/tests/resources/indexv4/.gitted/objects/b0/952dbb50bed5f01e03e31b296184cb183e54a7 b/tests/resources/indexv4/.gitted/objects/b0/952dbb50bed5f01e03e31b296184cb183e54a7
new file mode 100644
index 0000000..0ddc1d1
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/objects/b0/952dbb50bed5f01e03e31b296184cb183e54a7
Binary files differ
diff --git a/tests/resources/indexv4/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/indexv4/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/indexv4/.gitted/refs/heads/master b/tests/resources/indexv4/.gitted/refs/heads/master
new file mode 100644
index 0000000..f3e960e
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/refs/heads/master
@@ -0,0 +1 @@
+b0952dbb50bed5f01e03e31b296184cb183e54a7
diff --git a/tests/resources/indexv4/file.tx b/tests/resources/indexv4/file.tx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/indexv4/file.tx
diff --git a/tests/resources/indexv4/file.txt b/tests/resources/indexv4/file.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/indexv4/file.txt
diff --git a/tests/resources/indexv4/file.txz b/tests/resources/indexv4/file.txz
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/indexv4/file.txz
diff --git a/tests/resources/indexv4/foo b/tests/resources/indexv4/foo
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/indexv4/foo
diff --git a/tests/resources/indexv4/zzz b/tests/resources/indexv4/zzz
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/indexv4/zzz
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..23384fb 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);
@@ -1077,3 +1077,81 @@
     cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "code/projects/foo/bar/packages/repositories.config"));
     cl_assert_equal_i(0, ignored);
 }
+
+void test_status_ignore__ignore_all_toplevel_dirs_include_files(void)
+{
+	static const char *test_files[] = {
+		"empty_standard_repo/README.md",
+		"empty_standard_repo/src/main.c",
+		"empty_standard_repo/src/foo/foo.c",
+		"empty_standard_repo/dist/foo.o",
+		"empty_standard_repo/dist/main.o",
+		NULL
+	};
+
+	make_test_data("empty_standard_repo", test_files);
+	cl_git_mkfile(
+		"empty_standard_repo/.gitignore",
+		"/*/\n"
+		"!/src\n");
+
+	assert_is_ignored("dist/foo.o");
+	assert_is_ignored("dist/main.o");
+
+	refute_is_ignored("README.md");
+	refute_is_ignored("src/foo.c");
+	refute_is_ignored("src/foo/foo.c");
+}
+
+void test_status_ignore__subdir_ignore_all_toplevel_dirs_include_files(void)
+{
+	static const char *test_files[] = {
+		"empty_standard_repo/project/README.md",
+		"empty_standard_repo/project/src/main.c",
+		"empty_standard_repo/project/src/foo/foo.c",
+		"empty_standard_repo/project/dist/foo.o",
+		"empty_standard_repo/project/dist/main.o",
+		NULL
+	};
+
+	make_test_data("empty_standard_repo", test_files);
+	cl_git_mkfile(
+		"empty_standard_repo/project/.gitignore",
+		"/*/\n"
+		"!/src\n");
+
+	assert_is_ignored("project/dist/foo.o");
+	assert_is_ignored("project/dist/main.o");
+
+	refute_is_ignored("project/src/foo.c");
+	refute_is_ignored("project/src/foo/foo.c");
+	refute_is_ignored("project/README.md");
+}
+
+void test_status_ignore__subdir_ignore_everything_except_certain_files(void)
+{
+	static const char *test_files[] = {
+		"empty_standard_repo/project/README.md",
+		"empty_standard_repo/project/some_file",
+		"empty_standard_repo/project/src/main.c",
+		"empty_standard_repo/project/src/foo/foo.c",
+		"empty_standard_repo/project/dist/foo.o",
+		"empty_standard_repo/project/dist/main.o",
+		NULL
+	};
+
+	make_test_data("empty_standard_repo", test_files);
+	cl_git_mkfile(
+		"empty_standard_repo/project/.gitignore",
+		"/*\n"
+		"!/src\n"
+		"!README.md\n");
+
+	assert_is_ignored("project/some_file");
+	assert_is_ignored("project/dist/foo.o");
+	assert_is_ignored("project/dist/main.o");
+
+	refute_is_ignored("project/README.md");
+	refute_is_ignored("project/src/foo.c");
+	refute_is_ignored("project/src/foo/foo.c");
+}
diff --git a/tests/submodule/open.c b/tests/submodule/open.c
new file mode 100644
index 0000000..0ef01ec
--- /dev/null
+++ b/tests/submodule/open.c
@@ -0,0 +1,90 @@
+#include "clar_libgit2.h"
+#include "submodule_helpers.h"
+#include "path.h"
+
+static git_repository *g_parent;
+static git_repository *g_child;
+static git_submodule *g_module;
+
+void test_submodule_open__initialize(void)
+{
+	g_parent = setup_fixture_submod2();
+}
+
+void test_submodule_open__cleanup(void)
+{
+	git_submodule_free(g_module);
+	git_repository_free(g_child);
+	cl_git_sandbox_cleanup();
+	g_parent = NULL;
+	g_child = NULL;
+	g_module = NULL;
+}
+
+static void assert_sm_valid(git_repository *parent, git_repository *child, const char *sm_name)
+{
+	git_buf expected = GIT_BUF_INIT, actual = GIT_BUF_INIT;
+
+	/* assert working directory */
+	cl_git_pass(git_buf_joinpath(&expected, git_repository_workdir(parent), sm_name));
+	cl_git_pass(git_path_prettify_dir(&expected, expected.ptr, NULL));
+	cl_git_pass(git_buf_sets(&actual, git_repository_workdir(child)));
+	cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL));
+	cl_assert_equal_s(expected.ptr, actual.ptr);
+
+	git_buf_clear(&expected);
+	git_buf_clear(&actual);
+
+	/* assert common directory */
+	cl_git_pass(git_buf_joinpath(&expected, git_repository_commondir(parent), "modules"));
+	cl_git_pass(git_buf_joinpath(&expected, expected.ptr, sm_name));
+	cl_git_pass(git_path_prettify_dir(&expected, expected.ptr, NULL));
+	cl_git_pass(git_buf_sets(&actual, git_repository_commondir(child)));
+	cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL));
+	cl_assert_equal_s(expected.ptr, actual.ptr);
+
+	/* assert git directory */
+	cl_git_pass(git_buf_sets(&actual, git_repository_path(child)));
+	cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL));
+	cl_assert_equal_s(expected.ptr, actual.ptr);
+
+	git_buf_free(&expected);
+	git_buf_free(&actual);
+}
+
+void test_submodule_open__opening_via_lookup_succeeds(void)
+{
+	cl_git_pass(git_submodule_lookup(&g_module, g_parent, "sm_unchanged"));
+	cl_git_pass(git_submodule_open(&g_child, g_module));
+	assert_sm_valid(g_parent, g_child, "sm_unchanged");
+}
+
+void test_submodule_open__direct_open_succeeds(void)
+{
+	git_buf path = GIT_BUF_INIT;
+
+	cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_parent), "sm_unchanged"));
+	cl_git_pass(git_repository_open(&g_child, path.ptr));
+	assert_sm_valid(g_parent, g_child, "sm_unchanged");
+
+	git_buf_free(&path);
+}
+
+void test_submodule_open__direct_open_succeeds_for_broken_sm_with_gitdir(void)
+{
+	git_buf path = GIT_BUF_INIT;
+
+	/*
+	 * This is actually not a valid submodule, but we
+	 * encountered at least one occasion where the gitdir
+	 * file existed inside of a submodule's gitdir. As we are
+	 * now able to open these submodules correctly, we still
+	 * add a test for this.
+	 */
+	cl_git_mkfile("submod2/.git/modules/sm_unchanged/gitdir", ".git");
+	cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_parent), "sm_unchanged"));
+	cl_git_pass(git_repository_open(&g_child, path.ptr));
+	assert_sm_valid(g_parent, g_child, "sm_unchanged");
+
+	git_buf_free(&path);
+}
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 a9310bb..af60490 100644
--- a/tests/threads/basic.c
+++ b/tests/threads/basic.c
@@ -54,12 +54,6 @@
 {
 	return param;
 }
-
-static void *exit_abruptly(void *param)
-{
-	git_thread_exit(param);
-	return NULL;
-}
 #endif
 
 void test_threads_basic__exit(void)
diff --git a/tests/threads/diff.c b/tests/threads/diff.c
index c328114..2560402 100644
--- a/tests/threads/diff.c
+++ b/tests/threads/diff.c
@@ -19,12 +19,27 @@
 static git_tree *_a, *_b;
 static git_atomic _counts[4];
 static int _check_counts;
+#ifdef GIT_WIN32
+static int _retries;
+#endif
 
 #define THREADS 20
 
+void test_threads_diff__initialize(void)
+{
+#ifdef GIT_WIN32
+	_retries = git_win32__retries;
+	git_win32__retries = 1;
+#endif
+}
+
 void test_threads_diff__cleanup(void)
 {
 	cl_git_sandbox_cleanup();
+
+#ifdef GIT_WIN32
+	git_win32__retries = _retries;
+#endif
 }
 
 static void setup_trees(void)
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..a10f50a
--- /dev/null
+++ b/tests/worktree/refs.c
@@ -0,0 +1,173 @@
+#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_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+	git_reference *branch;
+	git_worktree *worktree;
+
+	opts.flags = GIT_WORKTREE_PRUNE_VALID;
+
+	cl_git_pass(git_worktree_lookup(&worktree, fixture.repo, fixture.worktreename));
+	cl_git_pass(git_worktree_prune(worktree, &opts));
+	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__renaming_reference_updates_worktree_heads(void)
+{
+	git_reference *head, *branch, *renamed;
+
+	cl_git_pass(git_branch_lookup(&branch, fixture.repo,
+		    "testrepo-worktree", GIT_BRANCH_LOCAL));
+	cl_git_pass(git_reference_rename(&renamed, branch, "refs/heads/renamed", 0, NULL));
+	cl_git_pass(git_repository_head(&head, fixture.worktree));
+
+	git_reference_free(head);
+	git_reference_free(branch);
+	git_reference_free(renamed);
+}
+
+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..2943852
--- /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, NULL));
+	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..4ac3b8b
--- /dev/null
+++ b/tests/worktree/worktree.c
@@ -0,0 +1,583 @@
+#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, NULL));
+
+	/* 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__add_locked(void)
+{
+	git_worktree *wt;
+	git_repository *repo;
+	git_reference *branch;
+	git_buf path = GIT_BUF_INIT;
+	git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
+
+	opts.lock = 1;
+
+	cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-locked"));
+	cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-locked", path.ptr, &opts));
+
+	/* Open and verify created repo */
+	cl_assert(git_worktree_is_locked(NULL, wt));
+	cl_git_pass(git_repository_open(&repo, path.ptr));
+	cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-locked/") == 0);
+	cl_git_pass(git_branch_lookup(&branch, repo, "worktree-locked", 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, NULL));
+
+	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, NULL));
+
+	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, NULL));
+
+	/* 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, NULL));
+	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_without_opts_fails(void)
+{
+	git_worktree *wt;
+	git_repository *repo;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_fail(git_worktree_prune(wt, NULL));
+
+	/* 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_valid(void)
+{
+	git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+	git_worktree *wt;
+	git_repository *repo;
+
+	opts.flags = GIT_WORKTREE_PRUNE_VALID;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_pass(git_worktree_prune(wt, &opts));
+
+	/* 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_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+	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));
+
+	opts.flags = GIT_WORKTREE_PRUNE_VALID;
+	cl_git_fail(git_worktree_prune(wt, &opts));
+	/* Assert the repository is still valid */
+	cl_git_pass(git_repository_open_from_worktree(&repo, wt));
+
+	opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_LOCKED;
+	cl_git_pass(git_worktree_prune(wt, &opts));
+
+	git_worktree_free(wt);
+	git_repository_free(repo);
+}
+
+void test_worktree_worktree__prune_gitdir_only(void)
+{
+	git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+	git_worktree *wt;
+
+	opts.flags = GIT_WORKTREE_PRUNE_VALID;
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_pass(git_worktree_prune(wt, &opts));
+
+	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_worktree(void)
+{
+	git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+	git_worktree *wt;
+
+	opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_WORKING_TREE;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_pass(git_worktree_prune(wt, &opts));
+
+	cl_assert(!git_path_exists(wt->gitdir_path));
+	cl_assert(!git_path_exists(wt->gitlink_path));
+
+	git_worktree_free(wt);
+}
+
+static int read_head_ref(git_repository *repo, const char *path, void *payload)
+{
+	git_vector *refs = (git_vector *) payload;
+	git_reference *head;
+
+	GIT_UNUSED(repo);
+
+	cl_git_pass(git_reference__read_head(&head, repo, path));
+
+	git_vector_insert(refs, head);
+
+	return 0;
+}
+
+void test_worktree_worktree__foreach_head_gives_same_results_in_wt_and_repo(void)
+{
+	git_vector repo_refs = GIT_VECTOR_INIT, worktree_refs = GIT_VECTOR_INIT;
+	git_reference *heads[2];
+	size_t i;
+
+	cl_git_pass(git_reference_lookup(&heads[0], fixture.repo, GIT_HEAD_FILE));
+	cl_git_pass(git_reference_lookup(&heads[1], fixture.worktree, GIT_HEAD_FILE));
+
+	cl_git_pass(git_repository_foreach_head(fixture.repo, read_head_ref, &repo_refs));
+	cl_git_pass(git_repository_foreach_head(fixture.worktree, read_head_ref, &worktree_refs));
+
+	cl_assert_equal_i(repo_refs.length, ARRAY_SIZE(heads));
+	cl_assert_equal_i(worktree_refs.length, ARRAY_SIZE(heads));
+
+	for (i = 0; i < ARRAY_SIZE(heads); i++) {
+		cl_assert_equal_s(heads[i]->name, ((git_reference *) repo_refs.contents[i])->name);
+		cl_assert_equal_s(heads[i]->name, ((git_reference *) repo_refs.contents[i])->name);
+		cl_assert_equal_s(heads[i]->name, ((git_reference *) worktree_refs.contents[i])->name);
+
+		git_reference_free(heads[i]);
+		git_reference_free(repo_refs.contents[i]);
+		git_reference_free(worktree_refs.contents[i]);
+	}
+
+	git_vector_free(&repo_refs);
+	git_vector_free(&worktree_refs);
+}
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);